Compare commits

..

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
7e6003ef24 Refactor generate_app_passwd to properly respect all password policy requirements
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 11:49:10 +00:00
copilot-swe-agent[bot]
2b38f348a5 Address code review feedback: improve comments and handle edge cases
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 11:46:47 +00:00
copilot-swe-agent[bot]
c5906dfb2f Add generate_app_passwd function and update mobileconfig.php to use it
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 11:42:54 +00:00
copilot-swe-agent[bot]
0221c4ccf2 Initial plan 2025-12-12 11:39:01 +00:00
15 changed files with 93 additions and 199 deletions

View File

@@ -246,25 +246,6 @@ while true; do
done
VALIDATED_CONFIG_DOMAINS+=("${VALIDATED_CONFIG_DOMAINS_SUBDOMAINS[*]}")
done
# Fetch alias domains where target domain has MTA-STS enabled
if [[ ${AUTODISCOVER_SAN} == "y" ]]; then
SQL_ALIAS_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT ad.alias_domain FROM alias_domain ad INNER JOIN mta_sts m ON ad.target_domain = m.domain WHERE ad.active = 1 AND m.active = 1" -Bs)
if [[ $? -eq 0 ]]; then
while read alias_domain; do
if [[ -z "${alias_domain}" ]]; then
# ignore empty lines
continue
fi
# Only add mta-sts subdomain for alias domains
if [[ "mta-sts.${alias_domain}" != "${MAILCOW_HOSTNAME}" ]]; then
if check_domain "mta-sts.${alias_domain}"; then
VALIDATED_CONFIG_DOMAINS+=("mta-sts.${alias_domain}")
fi
fi
done <<< "${SQL_ALIAS_DOMAINS}"
fi
fi
fi
if check_domain ${MAILCOW_HOSTNAME}; then

View File

@@ -2,7 +2,7 @@ FROM debian:trixie-slim
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG RSPAMD_VER=rspamd_3.14.2-82~90302bc
ARG RSPAMD_VER=rspamd_3.14.1-1~46a758617
ARG CODENAME=trixie
ENV LC_ALL=C

View File

@@ -129,16 +129,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
);
}
// Check if domain is an alias domain and get target domain's MTA-STS
$alias_domain_details = mailbox('get', 'alias_domain_details', $domain);
$mta_sts_domain = $domain;
if ($alias_domain_details !== false && !empty($alias_domain_details['target_domain'])) {
// This is an alias domain, check target domain for MTA-STS
$mta_sts_domain = $alias_domain_details['target_domain'];
}
$mta_sts = mailbox('get', 'mta_sts', $mta_sts_domain);
$mta_sts = mailbox('get', 'mta_sts', $domain);
if (count($mta_sts) > 0 && $mta_sts['active'] == 1) {
if (!in_array($domain, $alias_domains)) {
$records[] = array(

View File

@@ -251,6 +251,73 @@ function password_check($password1, $password2) {
return true;
}
function generate_app_passwd($length = 32) {
// Get password complexity requirements
$password_complexity = password_complexity('get');
// Determine the actual length to use
$required_length = max($length, intval($password_complexity['length']));
// Define character sets
$lowercase = 'abcdefghijklmnopqrstuvwxyz';
$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$digits = '0123456789';
$special = '!@#$%^&*()-_=+[]{}|;:,.<>?';
// Build the character pool and required chars based on policy
$pool = '';
$required_chars = '';
// Add digits to pool and ensure at least one if required
if ($password_complexity['numbers'] == 1) {
$pool .= $digits;
$required_chars .= $digits[random_int(0, strlen($digits) - 1)];
}
// Add alphabetic characters if required
if ($password_complexity['chars'] == 1) {
$pool .= $lowercase;
// Only add required char if not already added by lowerupper requirement
if ($password_complexity['lowerupper'] != 1) {
$required_chars .= $lowercase[random_int(0, strlen($lowercase) - 1)];
}
}
// Add both uppercase and lowercase letters if lowerupper required
if ($password_complexity['lowerupper'] == 1) {
$pool .= $lowercase . $uppercase;
$required_chars .= $uppercase[random_int(0, strlen($uppercase) - 1)];
$required_chars .= $lowercase[random_int(0, strlen($lowercase) - 1)];
}
// Add special characters if required
if ($password_complexity['special_chars'] == 1) {
$pool .= $special;
$required_chars .= $special[random_int(0, strlen($special) - 1)];
}
// If no requirements specified, use alphanumeric as default
if (empty($pool)) {
$pool = $lowercase . $uppercase . $digits;
$required_chars .= $digits[random_int(0, strlen($digits) - 1)];
}
// Ensure the password is at least as long as the required characters
$final_length = max($required_length, strlen($required_chars));
// Generate remaining characters from the pool
$remaining_length = $final_length - strlen($required_chars);
$password = $required_chars;
for ($i = 0; $i < $remaining_length; $i++) {
$password .= $pool[random_int(0, strlen($pool) - 1)];
}
// Shuffle the password to mix required chars with random ones
$password = str_shuffle($password);
return $password;
}
function last_login($action, $username, $sasl_limit_days = 7, $ui_offset = 1) {
global $pdo;
global $redis;

View File

@@ -664,18 +664,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
dkim('add', array('key_size' => $_data['key_size'], 'dkim_selector' => $_data['dkim_selector'], 'domains' => $domain));
}
}
// Create MTA-STS settings from template if enabled
if (!empty($DOMAIN_DEFAULT_ATTRIBUTES['mta_sts']) && $DOMAIN_DEFAULT_ATTRIBUTES['mta_sts'] == 1) {
$mta_sts_data = array(
'domain' => $domain,
'version' => $DOMAIN_DEFAULT_ATTRIBUTES['mta_sts_version'],
'mode' => $DOMAIN_DEFAULT_ATTRIBUTES['mta_sts_mode'],
'max_age' => $DOMAIN_DEFAULT_ATTRIBUTES['mta_sts_max_age'],
'mx' => $DOMAIN_DEFAULT_ATTRIBUTES['mta_sts_mx'],
'active' => 1
);
mailbox('add', 'mta_sts', $mta_sts_data);
}
if (!empty($restart_sogo)) {
$restart_response = json_decode(docker('post', 'sogo-mailcow', 'restart'), true);
if ($restart_response['type'] == "success") {
@@ -1660,11 +1648,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['relay_unknown_only'] = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : 0;
$attr['dkim_selector'] = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : "dkim";
$attr['key_size'] = isset($_data['key_size']) ? intval($_data['key_size']) : 2048;
$attr['mta_sts'] = (isset($_data['mta_sts'])) ? intval($_data['mta_sts']) : 0;
$attr['mta_sts_version'] = (isset($_data['mta_sts_version'])) ? $_data['mta_sts_version'] : 'stsv1';
$attr['mta_sts_mode'] = (isset($_data['mta_sts_mode'])) ? $_data['mta_sts_mode'] : 'enforce';
$attr['mta_sts_max_age'] = (isset($_data['mta_sts_max_age'])) ? intval($_data['mta_sts_max_age']) : 604800;
$attr['mta_sts_mx'] = (isset($_data['mta_sts_mx'])) ? $_data['mta_sts_mx'] : '';
// save template
$stmt = $pdo->prepare("INSERT INTO `templates` (`type`, `template`, `attributes`)
@@ -3001,26 +2984,21 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
// check attributes
$attr = array();
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : (isset($is_now['attributes']['tags']) ? $is_now['attributes']['tags'] : array());
$attr['max_num_aliases_for_domain'] = (isset($_data['max_num_aliases_for_domain'])) ? intval($_data['max_num_aliases_for_domain']) : (isset($is_now['attributes']['max_num_aliases_for_domain']) ? $is_now['attributes']['max_num_aliases_for_domain'] : 0);
$attr['max_num_mboxes_for_domain'] = (isset($_data['max_num_mboxes_for_domain'])) ? intval($_data['max_num_mboxes_for_domain']) : (isset($is_now['attributes']['max_num_mboxes_for_domain']) ? $is_now['attributes']['max_num_mboxes_for_domain'] : 0);
$attr['def_quota_for_mbox'] = (isset($_data['def_quota_for_mbox'])) ? intval($_data['def_quota_for_mbox']) * 1048576 : (isset($is_now['attributes']['def_quota_for_mbox']) ? $is_now['attributes']['def_quota_for_mbox'] : 0);
$attr['max_quota_for_mbox'] = (isset($_data['max_quota_for_mbox'])) ? intval($_data['max_quota_for_mbox']) * 1048576 : (isset($is_now['attributes']['max_quota_for_mbox']) ? $is_now['attributes']['max_quota_for_mbox'] : 0);
$attr['max_quota_for_domain'] = (isset($_data['max_quota_for_domain'])) ? intval($_data['max_quota_for_domain']) * 1048576 : (isset($is_now['attributes']['max_quota_for_domain']) ? $is_now['attributes']['max_quota_for_domain'] : 0);
$attr['rl_frame'] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : (isset($is_now['attributes']['rl_frame']) ? $is_now['attributes']['rl_frame'] : "s");
$attr['rl_value'] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : (isset($is_now['attributes']['rl_value']) ? $is_now['attributes']['rl_value'] : "");
$attr['active'] = isset($_data['active']) ? intval($_data['active']) : (isset($is_now['attributes']['active']) ? $is_now['attributes']['active'] : 1);
$attr['gal'] = (isset($_data['gal'])) ? intval($_data['gal']) : (isset($is_now['attributes']['gal']) ? $is_now['attributes']['gal'] : 1);
$attr['backupmx'] = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : (isset($is_now['attributes']['backupmx']) ? $is_now['attributes']['backupmx'] : 0);
$attr['relay_all_recipients'] = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : (isset($is_now['attributes']['relay_all_recipients']) ? $is_now['attributes']['relay_all_recipients'] : 0);
$attr['relay_unknown_only'] = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : (isset($is_now['attributes']['relay_unknown_only']) ? $is_now['attributes']['relay_unknown_only'] : 0);
$attr['dkim_selector'] = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : (isset($is_now['attributes']['dkim_selector']) ? $is_now['attributes']['dkim_selector'] : "dkim");
$attr['key_size'] = isset($_data['key_size']) ? intval($_data['key_size']) : (isset($is_now['attributes']['key_size']) ? $is_now['attributes']['key_size'] : 2048);
$attr['mta_sts'] = (isset($_data['mta_sts'])) ? intval($_data['mta_sts']) : (isset($is_now['attributes']['mta_sts']) ? $is_now['attributes']['mta_sts'] : 0);
$attr['mta_sts_version'] = (isset($_data['mta_sts_version'])) ? $_data['mta_sts_version'] : (isset($is_now['attributes']['mta_sts_version']) ? $is_now['attributes']['mta_sts_version'] : 'stsv1');
$attr['mta_sts_mode'] = (isset($_data['mta_sts_mode'])) ? $_data['mta_sts_mode'] : (isset($is_now['attributes']['mta_sts_mode']) ? $is_now['attributes']['mta_sts_mode'] : 'enforce');
$attr['mta_sts_max_age'] = (isset($_data['mta_sts_max_age'])) ? intval($_data['mta_sts_max_age']) : (isset($is_now['attributes']['mta_sts_max_age']) ? $is_now['attributes']['mta_sts_max_age'] : 604800);
$attr['mta_sts_mx'] = (isset($_data['mta_sts_mx'])) ? $_data['mta_sts_mx'] : (isset($is_now['attributes']['mta_sts_mx']) ? $is_now['attributes']['mta_sts_mx'] : '');
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : array();
$attr['max_num_aliases_for_domain'] = (isset($_data['max_num_aliases_for_domain'])) ? intval($_data['max_num_aliases_for_domain']) : 0;
$attr['max_num_mboxes_for_domain'] = (isset($_data['max_num_mboxes_for_domain'])) ? intval($_data['max_num_mboxes_for_domain']) : 0;
$attr['def_quota_for_mbox'] = (isset($_data['def_quota_for_mbox'])) ? intval($_data['def_quota_for_mbox']) * 1048576 : 0;
$attr['max_quota_for_mbox'] = (isset($_data['max_quota_for_mbox'])) ? intval($_data['max_quota_for_mbox']) * 1048576 : 0;
$attr['max_quota_for_domain'] = (isset($_data['max_quota_for_domain'])) ? intval($_data['max_quota_for_domain']) * 1048576 : 0;
$attr['rl_frame'] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
$attr['rl_value'] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : "";
$attr['active'] = isset($_data['active']) ? intval($_data['active']) : 1;
$attr['gal'] = (isset($_data['gal'])) ? intval($_data['gal']) : 1;
$attr['backupmx'] = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : 0;
$attr['relay_all_recipients'] = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : 0;
$attr['relay_unknown_only'] = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : 0;
$attr['dkim_selector'] = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : "dkim";
$attr['key_size'] = isset($_data['key_size']) ? intval($_data['key_size']) : 2048;
// update template
$stmt = $pdo->prepare("UPDATE `templates`

View File

@@ -54,16 +54,7 @@ jQuery(function($){
$.get("/inc/ajax/show_rspamd_global_filters.php");
$("#confirm_show_rspamd_global_filters").hide();
$("#rspamd_global_filters").removeClass("d-none");
localStorage.setItem('rspamd_global_filters_confirmed', 'true');
});
$(document).ready(function() {
if (localStorage.getItem('rspamd_global_filters_confirmed') === 'true') {
$("#confirm_show_rspamd_global_filters").hide();
$("#rspamd_global_filters").removeClass("d-none");
}
});
$("#super_delete").click(function() { return confirm(lang.queue_ays); });
$(".refresh_table").on('click', function(e) {

View File

@@ -723,8 +723,6 @@
"mta_sts_mx": "MX server",
"mta_sts_mx_info": "Allows sending only to explicitly listed mail server hostnames; the sending MTA checks if the DNS MX hostname matches the policy list, and only allows delivery with a valid TLS certificate (guards against MITM).",
"mta_sts_mx_notice": "Multiple MX servers can be specified (separated by commas).",
"mta_sts_enable": "Enable MTA-STS",
"mta_sts_template_info": "When enabled, MTA-STS will be automatically configured for all domains created with this template.",
"multiple_bookings": "Multiple bookings",
"none_inherit": "None / Inherit",
"nexthop": "Next hop",

View File

@@ -1266,7 +1266,7 @@
"no_last_login": "Aucune dernière information de connexion à l'interface",
"no_record": "Pas d'enregistrement",
"password": "Mot de passe",
"password_now": "Mot de passe actuel (confirmer les changements)",
"password_now": "Mot de passe courant (confirmer les changements)",
"password_repeat": "Mot de passe (répéter)",
"pushover_evaluate_x_prio": "Acheminement du courrier hautement prioritaire [<code>X-Priority: 1</code>]",
"pushover_info": "Les paramètres de notification push sappliqueront à tout le courrier propre (non spam) livré à <b>%s</b> y compris les alias (partagés, non partagés, étiquetés).",

View File

@@ -240,7 +240,7 @@
"generate": "Generuj",
"guid": "GUID - unikalny identyfikator instancji",
"guid_and_license": "GUID & licencja",
"hash_remove_info": "Usunięcie hasha z limitem współczynnika (jeśli nadal istnieje) spowoduje całkowite zresetowanie jego licznika.<br> Każdy hash jest oznaczony indywidualnym kolorem.",
"hash_remove_info": "Usunięcie hasha z limitem współczynnika (jeśli nadal istnieje) spowoduje całkowite zresetowanie jego licznika.<br>\n\n\n\n Każdy hash jest oznaczony indywidualnym kolorem.",
"help_text": "Zastąp tekst pomocy poniżej maski logowania (dozwolone HTML)",
"html": "HTML",
"iam": "Dostawca tożsamości",
@@ -683,11 +683,7 @@
"mailbox_rename_agree": "Stworzyłem kopię zapasową.",
"mailbox_rename_warning": "WAŻNE! Utwórz kopię zapasową przed zmianą nazwy skrzynki pocztowej.",
"mailbox_rename_alias": "Tworzenie aliasów automatycznie",
"mailbox_rename_title": "Nowa nazwa lokalnej skrzynki pocztowej",
"mbox_rl_info": "Ten limit szybkości dotyczy nazwy logowania SASL i odpowiada dowolnemu adresowi „from” używanemu przez zalogowanego użytkownika. Limit szybkości dla skrzynki pocztowej nadpisuje limit szybkości dla całej domeny.",
"nexthop": "Następny hop",
"private_comment": "Prywatny komentarz",
"public_comment": "Komentarz publiczny"
"mailbox_rename_title": "Nowa nazwa lokalnej skrzynki pocztowej"
},
"footer": {
"cancel": "Anuluj",
@@ -1079,7 +1075,7 @@
"spamfilter_table_remove": "Usuń",
"spamfilter_table_rule": "Zasada",
"spamfilter_wl": "Biała lista",
"spamfilter_wl_desc": "Adresy e-mail znajdujące się na liście dozwolonych (allowlist) są zaprogramowane tak, aby <b> nigdy nie </b> były klasyfikowane jako spam. Można używać symboli wieloznacznych (wildcardów).Filtr jest stosowany wyłącznie do bezpośrednich aliasów (aliasów wskazujących na jedną skrzynkę pocztową), z wyłączeniem aliasów typu „catch-all” oraz samej skrzynki pocztowej",
"spamfilter_wl_desc": "Adresy e-mail znajdujące się na liście dozwolonych (allowlist) są zaprogramowane tak, aby <b> nigdy nie </b> były klasyfikowane jako spam.\nMożna używać symboli wieloznacznych (wildcardów).\nFiltr jest stosowany wyłącznie do bezpośrednich aliasów (aliasów wskazujących na jedną skrzynkę pocztową), z wyłączeniem aliasów typu „catch-all” oraz samej skrzynki pocztowej",
"spamfilter_yellow": "Żółty: ta wiadomość może być spamem, zostanie oznaczona jako spam i przeniesiona do folderu spam",
"sync_jobs": "Zadania synchronizacji",
"tag_handling": "Ustaw obsługę znaczników pocztowych",

View File

@@ -340,8 +340,7 @@
"tls_policy": "Política de TLS",
"quarantine_attachments": "Anexos de quarentena",
"filters": "Filtros",
"smtp_ip_access": "Mudar anfitriões permitidos para SMTP",
"app_passwds": "Gerenciar senhas de aplicativos"
"smtp_ip_access": "Mudar anfitriões permitidos para SMTP"
},
"warning": {
"no_active_admin": "Não é possível desactivar o último administrador activo"

View File

@@ -52,7 +52,7 @@ if (isset($_GET['app_password'])) {
else
$platform = $_SERVER['HTTP_USER_AGENT'];
$password = bin2hex(openssl_random_pseudo_bytes(16));
$password = generate_app_passwd();
$attr = array(
'app_name' => $platform,
'app_passwd' => $password,

View File

@@ -7,30 +7,7 @@ if (!isset($_SERVER['HTTP_HOST']) || strpos($_SERVER['HTTP_HOST'], 'mta-sts.') !
}
$host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']);
$domain = idn_to_ascii(strtolower(str_replace('mta-sts.', '', $host)), 0, INTL_IDNA_VARIANT_UTS46);
// Validate domain or return 404 on error
if ($domain === false || empty($domain)) {
http_response_code(404);
exit;
}
// Check if domain is an alias domain and resolve to target domain
try {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain");
$stmt->execute(array(':domain' => $domain));
$alias_row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($alias_row !== false && !empty($alias_row['target_domain'])) {
// This is an alias domain, use the target domain for MTA-STS lookup
$domain = $alias_row['target_domain'];
}
} catch (PDOException $e) {
// On database error, return 404
http_response_code(404);
exit;
}
$domain = str_replace('mta-sts.', '', $host);
$mta_sts = mailbox('get', 'mta_sts', $domain);
if (count($mta_sts) == 0 ||

View File

@@ -10,7 +10,6 @@
<input type="hidden" value="0" name="gal">
<input type="hidden" value="0" name="relay_all_recipients">
<input type="hidden" value="0" name="relay_unknown_only">
<input type="hidden" value="0" name="mta_sts">
{% if mailcow_cc_role == 'admin' %}
<div class="row mb-4">
@@ -125,47 +124,6 @@
</div>
</div>
<hr>
<div class="row mb-4">
<label class="control-label col-sm-2">{{ lang.edit.mta_sts }}</label>
<div class="col-sm-10">
<div class="form-check mb-3">
<label><input type="checkbox" class="form-check-input" value="1" name="mta_sts"{% if template.attributes.mta_sts == '1' %} checked{% endif %}> {{ lang.edit.mta_sts_enable }}</label>
<p><small class="text-muted">{{ lang.edit.mta_sts_template_info|raw }}</small></p>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3" for="mta_sts_version">{{ lang.edit.mta_sts_version }}</label>
<div class="col-sm-9">
<select data-style="btn btn-light" class="form-control" name="mta_sts_version">
<option value="stsv1"{% if template.attributes.mta_sts_version == 'stsv1' %} selected{% endif %}>STSv1</option>
</select>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3" for="mta_sts_mode">{{ lang.edit.mta_sts_mode }}</label>
<div class="col-sm-9">
<select data-style="btn btn-light" class="form-control" name="mta_sts_mode">
<option value="enforce"{% if template.attributes.mta_sts_mode == 'enforce' %} selected{% endif %}>enforce</option>
<option value="testing"{% if template.attributes.mta_sts_mode == 'testing' %} selected{% endif %}>testing</option>
<option value="none"{% if template.attributes.mta_sts_mode == 'none' %} selected{% endif %}>none</option>
</select>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3" for="mta_sts_max_age">{{ lang.edit.mta_sts_max_age }}</label>
<div class="col-sm-9">
<input type="number" class="form-control" name="mta_sts_max_age" value="{{ template.attributes.mta_sts_max_age|default('604800') }}">
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3" for="mta_sts_mx">{{ lang.edit.mta_sts_mx }}</label>
<div class="col-sm-9">
<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="3" name="mta_sts_mx">{{ template.attributes.mta_sts_mx }}</textarea>
<small class="text-muted">{{ lang.edit.mta_sts_mx_notice|raw }}</small>
</div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="offset-sm-2 col-sm-10">
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="edit_selected" data-id="editdomain_template" data-item="{{ template.id }}" data-api-url='edit/domain/template' data-api-attr='{}' href="#">{{ lang.admin.save }}</button>

View File

@@ -688,48 +688,6 @@
</div>
</div>
<hr>
<div class="row mb-4">
<label class="control-label col-sm-2 text-sm-end">{{ lang.edit.mta_sts }}</label>
<div class="col-sm-10">
<div class="form-check mb-3">
<input type="hidden" value="0" name="mta_sts">
<label><input type="checkbox" class="form-check-input" value="1" name="mta_sts"> {{ lang.edit.mta_sts_enable }}</label>
<p><small class="text-muted">{{ lang.edit.mta_sts_template_info|raw }}</small></p>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="mta_sts_version">{{ lang.edit.mta_sts_version }}</label>
<div class="col-sm-9">
<select data-style="btn btn-light" class="form-control" name="mta_sts_version">
<option value="stsv1" selected>STSv1</option>
</select>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="mta_sts_mode">{{ lang.edit.mta_sts_mode }}</label>
<div class="col-sm-9">
<select data-style="btn btn-light" class="form-control" name="mta_sts_mode">
<option value="enforce" selected>enforce</option>
<option value="testing">testing</option>
<option value="none">none</option>
</select>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="mta_sts_max_age">{{ lang.edit.mta_sts_max_age }}</label>
<div class="col-sm-9">
<input type="number" class="form-control" name="mta_sts_max_age" value="604800">
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="mta_sts_mx">{{ lang.edit.mta_sts_mx }}</label>
<div class="col-sm-9">
<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="3" name="mta_sts_mx"></textarea>
<small class="text-muted">{{ lang.edit.mta_sts_mx_notice|raw }}</small>
</div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="offset-sm-2 col-sm-10">
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="add_item" data-id="adddomain_template" data-item="{{ domain }}" data-api-url='add/domain/template' data-api-attr='{}' href="#">{{ lang.admin.add }}</button>

View File

@@ -84,7 +84,7 @@ services:
- clamd
rspamd-mailcow:
image: ghcr.io/mailcow/rspamd:3.14.2
image: ghcr.io/mailcow/rspamd:3.14.1
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@@ -465,7 +465,7 @@ services:
condition: service_started
unbound-mailcow:
condition: service_healthy
image: ghcr.io/mailcow/acme:1.95
image: ghcr.io/mailcow/acme:1.94
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment: