From 4e164c9ef9a414b5b5a642f617dd9bf1988f6aa9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:31:25 +0000 Subject: [PATCH 1/6] Initial plan From 42a9e65f2811db173f074b932edcacb5be300f7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:39:49 +0000 Subject: [PATCH 2/6] Add MTA-STS support to domain templates Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com> --- data/web/inc/functions.mailbox.inc.php | 22 ++++++++++ data/web/lang/lang.en-gb.json | 2 + data/web/templates/edit/domain-templates.twig | 42 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index d8e4e178a..df0e894fe 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -664,6 +664,18 @@ 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") { @@ -1648,6 +1660,11 @@ 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`) @@ -2999,6 +3016,11 @@ 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'] : ''; // update template $stmt = $pdo->prepare("UPDATE `templates` diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index 1e8525957..ae3011e6e 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -723,6 +723,8 @@ "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", diff --git a/data/web/templates/edit/domain-templates.twig b/data/web/templates/edit/domain-templates.twig index d4612a198..690613380 100644 --- a/data/web/templates/edit/domain-templates.twig +++ b/data/web/templates/edit/domain-templates.twig @@ -124,6 +124,48 @@
+
+ +
+
+ + +

{{ lang.edit.mta_sts_template_info|raw }}

+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + {{ lang.edit.mta_sts_mx_notice|raw }} +
+
+
+
+
From f4afd19e99abe3f39472babaada2d9f9cae87c89 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:40:49 +0000 Subject: [PATCH 3/6] Add MTA-STS fields to add domain template modal Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com> --- data/web/templates/modals/mailbox.twig | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/data/web/templates/modals/mailbox.twig b/data/web/templates/modals/mailbox.twig index 1e8ee53fa..e94f36516 100644 --- a/data/web/templates/modals/mailbox.twig +++ b/data/web/templates/modals/mailbox.twig @@ -688,6 +688,48 @@

+
+ +
+
+ + +

{{ lang.edit.mta_sts_template_info|raw }}

+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + {{ lang.edit.mta_sts_mx_notice|raw }} +
+
+
+
+
From 96ca1ed6930eb535adafbf371315637be5903fca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:42:43 +0000 Subject: [PATCH 4/6] Fix duplicate CSS classes in MTA-STS template fields Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com> --- data/web/templates/modals/mailbox.twig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/web/templates/modals/mailbox.twig b/data/web/templates/modals/mailbox.twig index e94f36516..b766b40db 100644 --- a/data/web/templates/modals/mailbox.twig +++ b/data/web/templates/modals/mailbox.twig @@ -689,7 +689,7 @@

- +
@@ -697,7 +697,7 @@

{{ lang.edit.mta_sts_template_info|raw }}

- +
@@ -715,13 +715,13 @@
- +
- +
{{ lang.edit.mta_sts_mx_notice|raw }} From ea8a383c2cd5b78a6d0cfa7bba0a92c08cfd35c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:51:44 +0000 Subject: [PATCH 5/6] Fix domain template edit to preserve existing MTA-STS values The edit function now properly preserves existing attribute values from the database when updating a domain template, instead of resetting to hardcoded defaults. This applies to all template attributes including the newly added MTA-STS fields. Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com> --- data/web/inc/functions.mailbox.inc.php | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index df0e894fe..202cb4249 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -3001,26 +3001,26 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } // check attributes $attr = array(); - $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; - $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'] : ''; + $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'] : ''); // update template $stmt = $pdo->prepare("UPDATE `templates` From ad260aa81c1d2976d93b79ed3676df2f28e3d806 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 16:05:08 +0000 Subject: [PATCH 6/6] Move mta_sts hidden input to top of form Moved the mta_sts hidden input to the top of the form alongside other checkbox hidden inputs (active, backupmx, gal, etc.) to ensure consistent form serialization behavior. This matches the pattern used for all other checkbox fields in the template edit form. Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com> --- data/web/templates/edit/domain-templates.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/templates/edit/domain-templates.twig b/data/web/templates/edit/domain-templates.twig index 690613380..bb151adbe 100644 --- a/data/web/templates/edit/domain-templates.twig +++ b/data/web/templates/edit/domain-templates.twig @@ -10,6 +10,7 @@ + {% if mailcow_cc_role == 'admin' %}
@@ -128,7 +129,6 @@
-

{{ lang.edit.mta_sts_template_info|raw }}