diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f2fd3f..de0832d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,10 +184,12 @@ The most noteworthy change of this release is the update of the container's base - **Documentation:** - A guide for configuring a public server to relay inbound and outbound mail from DMS on a private server ([#3973](https://github.com/docker-mailserver/docker-mailserver/pull/3973)) + - Added information on how to configure send-only aliases ([#4044](https://github.com/docker-mailserver/docker-mailserver/pull/4044)) - **Environment Variables:** - `LOGROTATE_COUNT` defines the number of files kept by logrotate ([#3907](https://github.com/docker-mailserver/docker-mailserver/pull/3907)) - The fail2ban log file is now also taken into account by `LOGROTATE_COUNT` and `LOGROTATE_INTERVAL` ([#3915](https://github.com/docker-mailserver/docker-mailserver/pull/3915), [#3919](https://github.com/docker-mailserver/docker-mailserver/pull/3919)) - +- **Postfix:** + - `smtpd_sender_login_maps` allows configuration with sender-only aliases out of the box using `postfix-regexp-send-only.cf` ([#4044](https://github.com/docker-mailserver/docker-mailserver/pull/4044)) - **Internal:** - Regular container restarts are now better supported. Setup scripts that ran previously will now be skipped ([#3929](https://github.com/docker-mailserver/docker-mailserver/pull/3929)) diff --git a/docs/content/config/advanced/optional-config.md b/docs/content/config/advanced/optional-config.md index 57fa68de..9644458e 100644 --- a/docs/content/config/advanced/optional-config.md +++ b/docs/content/config/advanced/optional-config.md @@ -80,6 +80,7 @@ This is a list of all configuration files and directories which are optional, au - **postfix-sasl-password.cf:** listing of relayed domains with their respective `:`. Modify via `setup.sh relay add-auth []`. (Docs: [Relay-Hosts Auth][docs::relay-hosts::advanced]) - **postfix-relaymap.cf:** domain-specific relays and exclusions. Modify via `setup.sh relay add-domain` and `setup.sh relay exclude-domain`. (Docs: [Relay-Hosts Senders][docs::relay-hosts::advanced]) - **postfix-regexp.cf:** Regular expression alias file. (Docs: [Aliases][docs-aliases-regex]) +- **postfix-regexp-send-only.cf:** Regular expression alias file for sending only. (Docs: [Send-Only Aliases][docs-aliases-send-only]) - **ldap-users.cf:** Configuration for the virtual user mapping `virtual_mailbox_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script. - **ldap-groups.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script. - **ldap-aliases.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script. @@ -97,8 +98,9 @@ This is a list of all configuration files and directories which are optional, au [docker-docs::volumes]: https://docs.docker.com/storage/volumes/ [docker-docs::volumes::bind-mount]: https://docs.docker.com/storage/bind-mounts/ -[docs-accounts-quota]: ../../config/account-management/provisioner/file.md#quotas -[docs-aliases-regex]: ../../config/account-management/provisioner/file.md#configuring-regex-aliases +[docs-accounts-quota]: ../../config/user-management.md#quotas +[docs-aliases-regex]: ../../config/user-management.md#configuring-regexp-aliases +[docs-aliases-send-only]: ../../config/user-management.md#send-only-aliases [docs-dkim]: ../../config/best-practices/dkim_dmarc_spf.md#dkim [docs-fail2ban]: ../../config/security/fail2ban.md [docs-faq-spamrules]: ../../faq.md#how-can-i-manage-my-custom-spamassassin-rules diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index e209a0cf..0066a7ba 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -204,6 +204,8 @@ Configures the handling of creating mails with forged sender addresses. - **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a [forged sender address](https://en.wikipedia.org/wiki/Email_spoofing). - 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. +To allow certain accounts to send as other addresses, set the `SPOOF_PROTECTION` to `1` and see [the Aliases page in the documentation][docs-aliases]. + ##### ENABLE_SRS Enables the Sender Rewriting Scheme. SRS is needed if DMS acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation. diff --git a/target/scripts/check-for-changes.sh b/target/scripts/check-for-changes.sh index bf5cd90b..f256ac10 100755 --- a/target/scripts/check-for-changes.sh +++ b/target/scripts/check-for-changes.sh @@ -136,9 +136,10 @@ function _postfix_dovecot_changes() { fi # Regenerate system + virtual account aliases via `helpers/aliases.sh`: - [[ ${CHANGED} =~ ${DMS_DIR}/postfix-virtual.cf ]] && _handle_postfix_virtual_config - [[ ${CHANGED} =~ ${DMS_DIR}/postfix-regexp.cf ]] && _handle_postfix_regexp_config - [[ ${CHANGED} =~ ${DMS_DIR}/postfix-aliases.cf ]] && _handle_postfix_aliases_config + [[ ${CHANGED} =~ ${DMS_DIR}/postfix-virtual.cf ]] && _handle_postfix_virtual_config + [[ ${CHANGED} =~ ${DMS_DIR}/postfix-regexp.cf ]] && _handle_postfix_regexp_config + [[ ${CHANGED} =~ ${DMS_DIR}/postfix-regexp-send-only.cf ]] && _handle_postfix_regexp_send_only_config + [[ ${CHANGED} =~ ${DMS_DIR}/postfix-aliases.cf ]] && _handle_postfix_aliases_config # Legacy workaround handled here, only seems necessary for _create_accounts: # - `helpers/accounts.sh` logic creates folders/files with wrong ownership. diff --git a/target/scripts/helpers/aliases.sh b/target/scripts/helpers/aliases.sh index b0f2fa1a..7604fd44 100644 --- a/target/scripts/helpers/aliases.sh +++ b/target/scripts/helpers/aliases.sh @@ -30,6 +30,17 @@ function _handle_postfix_regexp_config() { fi } +function _handle_postfix_regexp_send_only_config() { + : >/etc/postfix/regexp-send-only + + if [[ -f /tmp/docker-mailserver/postfix-regexp-send-only.cf ]]; then + _log 'trace' "Adding regexp-send-only alias file postfix-regexp-send-only.cf" + + cp -f /tmp/docker-mailserver/postfix-regexp-send-only.cf /etc/postfix/regexp-send-only + # we specifically do NOT append this to virtual_alias_maps + fi +} + function _handle_postfix_aliases_config() { _log 'trace' 'Configuring root alias' @@ -46,5 +57,6 @@ function _handle_postfix_aliases_config() { function _create_aliases() { _handle_postfix_virtual_config _handle_postfix_regexp_config + _handle_postfix_regexp_send_only_config _handle_postfix_aliases_config } diff --git a/target/scripts/helpers/change-detection.sh b/target/scripts/helpers/change-detection.sh index a37df9ea..65c1aebd 100644 --- a/target/scripts/helpers/change-detection.sh +++ b/target/scripts/helpers/change-detection.sh @@ -34,6 +34,7 @@ function _monitored_files_checksums() { "${DMS_DIR}/postfix-accounts.cf" "${DMS_DIR}/postfix-virtual.cf" "${DMS_DIR}/postfix-regexp.cf" + "${DMS_DIR}/postfix-regexp-send-only.cf" "${DMS_DIR}/postfix-aliases.cf" "${DMS_DIR}/postfix-relaymap.cf" "${DMS_DIR}/postfix-sasl-password.cf" diff --git a/target/scripts/startup/setup.d/security/spoofing.sh b/target/scripts/startup/setup.d/security/spoofing.sh index ffefb279..91ba14a3 100644 --- a/target/scripts/startup/setup.d/security/spoofing.sh +++ b/target/scripts/startup/setup.d/security/spoofing.sh @@ -14,7 +14,11 @@ function _setup_spoof_protection() { # NOTE: This file is always created at startup, it potentially has content added. # TODO: From section: "SPOOF_PROTECTION=1 handling for smtpd_sender_login_maps" # https://github.com/docker-mailserver/docker-mailserver/issues/2819#issue-1402114383 - if [[ -f /etc/postfix/regexp ]]; then + if [[ -f /etc/postfix/regexp && -f /etc/postfix/regexp-send-only ]]; then + postconf 'smtpd_sender_login_maps = unionmap:{ texthash:/etc/postfix/virtual, hash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre, pcre:/etc/postfix/regexp, pcre:/etc/postfix/regexp-send-only }' + elif [[ -f /etc/postfix/regexp-send-only ]]; then + postconf 'smtpd_sender_login_maps = unionmap:{ texthash:/etc/postfix/virtual, hash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre, pcre:/etc/postfix/regexp-send-only }' + elif [[ -f /etc/postfix/regexp ]]; then postconf 'smtpd_sender_login_maps = unionmap:{ texthash:/etc/postfix/virtual, hash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre, pcre:/etc/postfix/regexp }' else postconf 'smtpd_sender_login_maps = texthash:/etc/postfix/virtual, hash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre' diff --git a/test/config/postfix-regexp-send-only.cf b/test/config/postfix-regexp-send-only.cf new file mode 100644 index 00000000..d81f927c --- /dev/null +++ b/test/config/postfix-regexp-send-only.cf @@ -0,0 +1 @@ +/^user3@localhost.localdomain/ user1@localhost.localdomain diff --git a/test/files/emails/auth/added-smtp-auth-spoofed-alias.txt b/test/files/emails/auth/added-smtp-auth-spoofed-from-alias1.txt similarity index 100% rename from test/files/emails/auth/added-smtp-auth-spoofed-alias.txt rename to test/files/emails/auth/added-smtp-auth-spoofed-from-alias1.txt diff --git a/test/files/emails/auth/added-smtp-auth-spoofed.txt b/test/files/emails/auth/added-smtp-auth-spoofed-from-test123.txt similarity index 72% rename from test/files/emails/auth/added-smtp-auth-spoofed.txt rename to test/files/emails/auth/added-smtp-auth-spoofed-from-test123.txt index fd96d401..e5584366 100644 --- a/test/files/emails/auth/added-smtp-auth-spoofed.txt +++ b/test/files/emails/auth/added-smtp-auth-spoofed-from-test123.txt @@ -1,4 +1,4 @@ -From: Not_My_Business +From: test123_alias To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message diff --git a/test/files/emails/auth/added-smtp-auth-spoofed-from-user1.txt b/test/files/emails/auth/added-smtp-auth-spoofed-from-user1.txt new file mode 100644 index 00000000..cd3960be --- /dev/null +++ b/test/files/emails/auth/added-smtp-auth-spoofed-from-user1.txt @@ -0,0 +1,5 @@ +From: User 1 +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message +This is a test mail. diff --git a/test/files/emails/auth/added-smtp-auth-spoofed-from-user3.txt b/test/files/emails/auth/added-smtp-auth-spoofed-from-user3.txt new file mode 100644 index 00000000..0a2ccdb6 --- /dev/null +++ b/test/files/emails/auth/added-smtp-auth-spoofed-from-user3.txt @@ -0,0 +1,5 @@ +From: User 3 +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message +This is a test mail. diff --git a/test/tests/parallel/set2/auth/spoofing.bats b/test/tests/parallel/set2/auth/spoofing.bats new file mode 100644 index 00000000..954d5aaa --- /dev/null +++ b/test/tests/parallel/set2/auth/spoofing.bats @@ -0,0 +1,105 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +BATS_TEST_NAME_PREFIX='[Postfix] (sender spoofing) ' +CONTAINER_NAME='dms-test_postfix-spoofing' + +function setup_file() { + _init_with_defaults + + local CUSTOM_SETUP_ARGUMENTS=( + --env SPOOF_PROTECTION=1 + --env LOG_LEVEL=trace + --env SSL_TYPE='snakeoil' + ) + + _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' + + _wait_for_service postfix + _wait_for_smtp_port_in_container_to_respond +} + +function teardown_file() { _default_teardown ; } + +# These tests ensure spoofing protection works, and that exceptions are available for aliases. +# user1 has aliases configured for the following accounts: +# - test\d* via /etc/postfix/regexp +# - alias1@localhost via /etc/postfix/virtual +# - user3@localhost via /etc/postfix/regexp-send-only + +@test "allows forging as send-only alias" { + # An authenticated account should be able to send mail from a send-only alias, + # Verifies `main.cf:smtpd_sender_login_maps` includes /etc/postfix/regexp-send-only + _send_email \ + --port 587 -tls --auth PLAIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from user3@localhost.localdomain \ + --data 'auth/added-smtp-auth-spoofed-from-user3.txt' + assert_success + assert_output --partial 'End data with' +} + +@test "allows forging as regular alias" { + # An authenticated account should be able to send mail from an alias, + # Verifies `main.cf:smtpd_sender_login_maps` includes /etc/postfix/virtual + _send_email \ + --port 587 -tls --auth PLAIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from alias1@localhost.localdomain \ + --data 'auth/added-smtp-auth-spoofed-from-alias1.txt' + assert_success + assert_output --partial 'End data with' +} + +@test "allows forging as regular (regex) alias" { + # An authenticated account should be able to send mail from an alias, + # Verifies `main.cf:smtpd_sender_login_maps` includes /etc/postfix/regexp + _send_email \ + --port 587 -tls --auth PLAIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from test123@localhost.localdomain \ + --data 'auth/added-smtp-auth-spoofed-from-test123.txt' + assert_success + assert_output --partial 'End data with' +} + +@test "rejects sender forging" { + # An authenticated user cannot use an envelope sender (MAIL FROM) + # address they do not own according to `main.cf:smtpd_sender_login_maps` lookup + _send_email --expect-rejection \ + --port 587 -tls --auth PLAIN \ + --auth-user user3@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from user1@localhost.localdomain \ + --data 'auth/added-smtp-auth-spoofed-from-user1.txt' + assert_output --partial 'Sender address rejected: not owned by user' +} + +@test "send-only alias does not affect incoming mail" { + # user1 is allowed to send as user3, however, mail to user3 should still be delivered to user3. + # Verifies that /etc/postfix/regexp-send-only does not affect incoming mail. + _send_email \ + --port 587 -tls --auth PLAIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from user1@localhost.localdomain \ + --to user3@localhost.localdomain \ + --data 'test-email.txt' + assert_success + assert_output --partial 'End data with' + + _wait_for_empty_mail_queue_in_container + + # would have an orig_to if it got forwarded + _service_log_should_contain_string 'mail' ': to=' + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index eff151df..15e6a6b0 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -281,39 +281,6 @@ EOF assert_success } -@test "spoofing: rejects sender forging" { - # rejection of spoofed sender - _wait_for_smtp_port_in_container_to_respond - - # An authenticated user cannot use an envelope sender (MAIL FROM) - # address they do not own according to `main.cf:smtpd_sender_login_maps` lookup - _send_email --expect-rejection \ - --port 465 -tlsc --auth PLAIN \ - --auth-user added@localhost.localdomain \ - --auth-password mypassword \ - --ehlo mail \ - --from user2@localhost.localdomain \ - --data 'auth/added-smtp-auth-spoofed.txt' - assert_output --partial 'Sender address rejected: not owned by user' -} - -@test "spoofing: accepts sending as alias" { - # An authenticated account should be able to send mail from an alias, - # Verifies `main.cf:smtpd_sender_login_maps` includes /etc/postfix/virtual - # The envelope sender address (MAIL FROM) is the lookup key - # to each table. Address is authorized when a result that maps to - # the DMS account is returned. - _send_email \ - --port 465 -tlsc --auth PLAIN \ - --auth-user user1@localhost.localdomain \ - --auth-password mypassword \ - --ehlo mail \ - --from alias1@localhost.localdomain \ - --data 'auth/added-smtp-auth-spoofed-alias.txt' - assert_success - assert_output --partial 'End data with' -} - # # Pflogsumm delivery check #