feat: Postfix permit DSN (Delivery Status Notification) only on authenticated ports (465 + 587) (#3572)

* add POSTFIX_DSN

* add tests for POSTFIX_DSN

* Revert "add POSTFIX_DSN"

This reverts commit d5bd0e9117.

* discard DSN requests on unauthenticated ports

* make tests work with overrides instead of ENV

* Apply suggestions from code review

* fix test inconsistencies

---------

Co-authored-by: allddd <allddd@proton.me>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
This commit is contained in:
allddd 2023-10-22 15:16:41 +02:00 committed by GitHub
parent 811a769845
commit eacc379cf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 138 additions and 0 deletions

View file

@ -9,6 +9,17 @@ All notable changes to this project will be documented in this file. The format
### Breaking
- The environment variable `ENABLE_LDAP=1` has been changed to `ACCOUNT_PROVISIONER=LDAP`.
- Postfix now defaults to supporting DSNs (_[Delivery Status Notifications](https://github.com/docker-mailserver/docker-mailserver/pull/3572#issuecomment-1751880574)_) only for authenticated users. This is a security measure to reduce spammer abuse of your DMS instance as a backscatter source.
- If you need to modify this change, please let us know by opening an issue / discussion.
- You can [opt-out (_enable DSNs_) via the `postfix-main.cf` override support](https://docker-mailserver.github.io/docker-mailserver/v12.1/config/advanced/override-defaults/postfix/) using the contents: `smtpd_discard_ehlo_keywords =`.
- Likewise for authenticated users, the submission(s) ports (465 + 587) are configured internally via `master.cf` to keep DSNs enabled (_since authentication protects from abuse_).
If necessary, DSNs for authenticated users can be disabled via the `postfix-master.cf` override with the following contents:
```
submission/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
submissions/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
```
### Added

View file

@ -54,6 +54,7 @@ smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_una
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain
smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining
smtpd_sender_restrictions = $dms_smtpd_sender_restrictions
smtpd_discard_ehlo_keywords = silent-discard, dsn
disable_vrfy_command = yes
# Custom defined parameters for DMS:

View file

@ -24,6 +24,7 @@ submission inet n - n - - smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup
@ -37,6 +38,7 @@ submissions inet n - n - - smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup

View file

@ -0,0 +1 @@
smtpd_discard_ehlo_keywords =

View file

@ -0,0 +1,2 @@
submission/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
submissions/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn

View file

@ -0,0 +1,14 @@
EHLO mail
AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu
bXlwYXNzd29yZA==
MAIL FROM: user1@localhost.localdomain
RCPT TO: user1@localhost.localdomain NOTIFY=success,failure
DATA
From: Existing Local User <user1@localhost.localdomain>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message
This is a test mail.
.
QUIT

View file

@ -0,0 +1,12 @@
HELO mail.external.tld
MAIL FROM: user@external.tld
RCPT TO: user1@localhost.localdomain NOTIFY=success,failure
DATA
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message
This is a test mail.
.
QUIT

View file

@ -0,0 +1,95 @@
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/common"
BATS_TEST_NAME_PREFIX='[DSN] '
CONTAINER1_NAME='dms-test_dsn_send_always'
CONTAINER2_NAME='dms-test_dsn_send_auth'
CONTAINER3_NAME='dms-test_dsn_send_none'
# A similar line is added to the log when a DSN (Delivery Status Notification) is sent:
#
# postfix/bounce[1023]: C943BA6B46: sender delivery status notification: DBF86A6B4CO
#
LOG_DSN='delivery status notification'
function setup_file() {
local CUSTOM_SETUP_ARGUMENTS=(
# Required only for delivery via nc (_send_email)
--env PERMIT_DOCKER=container
)
export CONTAINER_NAME=${CONTAINER1_NAME}
_init_with_defaults
# Unset `smtpd_discard_ehlo_keywords` to allow DSNs by default on any `smtpd` service:
cp "${TEST_TMP_CONFIG}/dsn/postfix-main.cf" "${TEST_TMP_CONFIG}/postfix-main.cf"
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service postfix
_wait_for_smtp_port_in_container
export CONTAINER_NAME=${CONTAINER2_NAME}
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service postfix
_wait_for_smtp_port_in_container
export CONTAINER_NAME=${CONTAINER3_NAME}
_init_with_defaults
# Mirror default main.cf (disable DSN on ports 465 + 587 too):
cp "${TEST_TMP_CONFIG}/dsn/postfix-master.cf" "${TEST_TMP_CONFIG}/postfix-master.cf"
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service postfix
_wait_for_smtp_port_in_container
}
function teardown_file() {
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}" "${CONTAINER3_NAME}"
}
@test "should always send a DSN when requested" {
export CONTAINER_NAME=${CONTAINER1_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
_should_output_number_of_lines 3
}
# Defaults test case
@test "should only send a DSN when requested from ports 465/587" {
export CONTAINER_NAME=${CONTAINER2_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_wait_for_empty_mail_queue_in_container
# DSN requests can now only be made on ports 465 and 587,
# so grep should not find anything.
#
# Although external requests are discarded, anyone who has requested a DSN
# will still receive it, but it will come from the sending mail server, not this one.
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
assert_failure
# These ports are excluded via master.cf.
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
_should_output_number_of_lines 2
}
@test "should never send a DSN" {
export CONTAINER_NAME=${CONTAINER3_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
# DSN requests are rejected regardless of origin.
# This is usually a bad idea, as you won't get them either.
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
assert_failure
}