From 98a4c13ca9c8340c38a2d9ae6aef5607d695a9d5 Mon Sep 17 00:00:00 2001 From: Casper Date: Mon, 18 Dec 2023 12:26:28 +0100 Subject: [PATCH 01/34] Add ENV `ENABLE_IMAP` (#3703) --- CHANGELOG.md | 5 ++- docs/content/config/environment.md | 7 ++++- mailserver.env | 10 ++++-- target/scripts/startup/setup.d/dovecot.sh | 38 ++++++++++++++--------- target/scripts/startup/variables-stack.sh | 1 + 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3553804f..339c92e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,12 @@ All notable changes to this project will be documented in this file. The format ### Added +- **Dovecot:** + - ENV `ENABLE_IMAP` ([#3703](https://github.com/docker-mailserver/docker-mailserver/pull/3703)) - **Tests:** - You can now use `make run-local-instance` to run a DMS image that was built locally to test changes ([#3663](https://github.com/docker-mailserver/docker-mailserver/pull/3663)) -- Log a warning when update-check is enabled, but no stable release image is used ([#3684](https://github.com/docker-mailserver/docker-mailserver/pull/3684)) +- **Internal**: + - Log a warning when update-check is enabled, but no stable release image is used ([#3684](https://github.com/docker-mailserver/docker-mailserver/pull/3684)) ### Updates diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index b8e257cc..5ef3b968 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -131,9 +131,14 @@ Enabled `policyd-spf` in Postfix's configuration. You will likely want to set th ##### ENABLE_POP3 -- **empty** => POP3 service disabled +- **0** => POP3 service disabled - 1 => Enables POP3 service +##### ENABLE_IMAP + +- 0 => Disabled +- **1** => Enabled + ##### ENABLE_CLAMAV - **0** => ClamAV is disabled diff --git a/mailserver.env b/mailserver.env index 68786224..48b537bc 100644 --- a/mailserver.env +++ b/mailserver.env @@ -119,10 +119,16 @@ ENABLE_OPENDMARC=1 # - **1** => Enabled ENABLE_POLICYD_SPF=1 -# 1 => Enables POP3 service -# empty => disables POP3 +# Enables POP3 service +# - **0** => Disabled +# - 1 => Enabled ENABLE_POP3= +# Enables IMAP service +# - 0 => Disabled +# - **1** => Enabled +ENABLE_IMAP=1 + # Enables ClamAV, and anti-virus scanner. # 1 => Enabled # **0** => Disabled diff --git a/target/scripts/startup/setup.d/dovecot.sh b/target/scripts/startup/setup.d/dovecot.sh index 3eeda286..e46aca21 100644 --- a/target/scripts/startup/setup.d/dovecot.sh +++ b/target/scripts/startup/setup.d/dovecot.sh @@ -6,12 +6,10 @@ function _setup_dovecot() { cp -a /usr/share/dovecot/protocols.d /etc/dovecot/ # disable pop3 (it will be eventually enabled later in the script, if requested) mv /etc/dovecot/protocols.d/pop3d.protocol /etc/dovecot/protocols.d/pop3d.protocol.disab + # disable imap (it will be eventually enabled later in the script, if requested) + mv /etc/dovecot/protocols.d/imapd.protocol /etc/dovecot/protocols.d/imapd.protocol.disab mv /etc/dovecot/protocols.d/managesieved.protocol /etc/dovecot/protocols.d/managesieved.protocol.disab - sed -i -e 's|#ssl = yes|ssl = yes|g' /etc/dovecot/conf.d/10-master.conf - sed -i -e 's|#port = 993|port = 993|g' /etc/dovecot/conf.d/10-master.conf - sed -i -e 's|#port = 995|port = 995|g' /etc/dovecot/conf.d/10-master.conf - sed -i -e 's|#ssl = yes|ssl = required|g' /etc/dovecot/conf.d/10-ssl.conf - sed -i 's|^postmaster_address = .*$|postmaster_address = '"${POSTMASTER_ADDRESS}"'|g' /etc/dovecot/conf.d/15-lda.conf + sedfile -i 's|^postmaster_address = .*$|postmaster_address = '"${POSTMASTER_ADDRESS}"'|g' /etc/dovecot/conf.d/15-lda.conf if ! grep -q -E '^stats_writer_socket_path=' /etc/dovecot/dovecot.conf; then printf '\n%s\n' 'stats_writer_socket_path=' >>/etc/dovecot/dovecot.conf @@ -37,9 +35,21 @@ function _setup_dovecot() { esac + if [[ ${ENABLE_POP3} -eq 1 || ${ENABLE_IMAP} -eq 1 ]]; then + sedfile -i -e 's|#ssl = yes|ssl = yes|g' /etc/dovecot/conf.d/10-master.conf + sedfile -i -e 's|#ssl = yes|ssl = required|g' /etc/dovecot/conf.d/10-ssl.conf + fi + if [[ ${ENABLE_POP3} -eq 1 ]]; then _log 'debug' 'Enabling POP3 services' mv /etc/dovecot/protocols.d/pop3d.protocol.disab /etc/dovecot/protocols.d/pop3d.protocol + sedfile -i -e 's|#port = 995|port = 995|g' /etc/dovecot/conf.d/10-master.conf + fi + + if [[ ${ENABLE_IMAP} -eq 1 ]]; then + _log 'debug' 'Enabling IMAP services' + mv /etc/dovecot/protocols.d/imapd.protocol.disab /etc/dovecot/protocols.d/imapd.protocol + sedfile -i -e 's|#port = 993|port = 993|g' /etc/dovecot/conf.d/10-master.conf fi [[ -f /tmp/docker-mailserver/dovecot.cf ]] && cp /tmp/docker-mailserver/dovecot.cf /etc/dovecot/local.conf @@ -89,23 +99,23 @@ function _setup_dovecot_quota() { # disable dovecot quota in docevot confs if [[ -f /etc/dovecot/conf.d/90-quota.conf ]]; then mv /etc/dovecot/conf.d/90-quota.conf /etc/dovecot/conf.d/90-quota.conf.disab - sed -i \ + sedfile -i \ "s|mail_plugins = \$mail_plugins quota|mail_plugins = \$mail_plugins|g" \ /etc/dovecot/conf.d/10-mail.conf - sed -i \ + sedfile -i \ "s|mail_plugins = \$mail_plugins imap_quota|mail_plugins = \$mail_plugins|g" \ /etc/dovecot/conf.d/20-imap.conf fi # disable quota policy check in postfix - sed -i "s|check_policy_service inet:localhost:65265||g" /etc/postfix/main.cf + sedfile -i "s|check_policy_service inet:localhost:65265||g" /etc/postfix/main.cf else if [[ -f /etc/dovecot/conf.d/90-quota.conf.disab ]]; then mv /etc/dovecot/conf.d/90-quota.conf.disab /etc/dovecot/conf.d/90-quota.conf - sed -i \ + sedfile -i \ "s|mail_plugins = \$mail_plugins|mail_plugins = \$mail_plugins quota|g" \ /etc/dovecot/conf.d/10-mail.conf - sed -i \ + sedfile -i \ "s|mail_plugins = \$mail_plugins|mail_plugins = \$mail_plugins imap_quota|g" \ /etc/dovecot/conf.d/20-imap.conf fi @@ -113,11 +123,11 @@ function _setup_dovecot_quota() { local MESSAGE_SIZE_LIMIT_MB=$((POSTFIX_MESSAGE_SIZE_LIMIT / 1000000)) local MAILBOX_LIMIT_MB=$((POSTFIX_MAILBOX_SIZE_LIMIT / 1000000)) - sed -i \ + sedfile -i \ "s|quota_max_mail_size =.*|quota_max_mail_size = ${MESSAGE_SIZE_LIMIT_MB}$([[ ${MESSAGE_SIZE_LIMIT_MB} -eq 0 ]] && echo "" || echo "M")|g" \ /etc/dovecot/conf.d/90-quota.conf - sed -i \ + sedfile -i \ "s|quota_rule = \*:storage=.*|quota_rule = *:storage=${MAILBOX_LIMIT_MB}$([[ ${MAILBOX_LIMIT_MB} -eq 0 ]] && echo "" || echo "M")|g" \ /etc/dovecot/conf.d/90-quota.conf @@ -127,7 +137,7 @@ function _setup_dovecot_quota() { fi # enable quota policy check in postfix - sed -i -E \ + sedfile -i -E \ "s|(reject_unknown_recipient_domain)|\1, check_policy_service inet:localhost:65265|g" \ /etc/postfix/main.cf fi @@ -188,5 +198,5 @@ function _setup_dovecot_dhparam() { function _setup_dovecot_hostname() { _log 'debug' 'Applying hostname to Dovecot' - sed -i "s|^#hostname =.*$|hostname = '${HOSTNAME}'|g" /etc/dovecot/conf.d/15-lda.conf + sedfile -i "s|^#hostname =.*$|hostname = '${HOSTNAME}'|g" /etc/dovecot/conf.d/15-lda.conf } diff --git a/target/scripts/startup/variables-stack.sh b/target/scripts/startup/variables-stack.sh index 3b575f50..2660ce89 100644 --- a/target/scripts/startup/variables-stack.sh +++ b/target/scripts/startup/variables-stack.sh @@ -87,6 +87,7 @@ function __environment_variables_general_setup() { VARS[ENABLE_OPENDMARC]="${ENABLE_OPENDMARC:=1}" VARS[ENABLE_POLICYD_SPF]="${ENABLE_POLICYD_SPF:=1}" VARS[ENABLE_POP3]="${ENABLE_POP3:=0}" + VARS[ENABLE_IMAP]="${ENABLE_IMAP:=1}" VARS[ENABLE_POSTGREY]="${ENABLE_POSTGREY:=0}" VARS[ENABLE_QUOTAS]="${ENABLE_QUOTAS:=1}" VARS[ENABLE_RSPAMD]="${ENABLE_RSPAMD:=0}" From 083e46408413c11e71f7e9ae299ae89874d8c35c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:05:37 +0100 Subject: [PATCH 02/34] chore(deps): Bump github/codeql-action from 2 to 3 (#3709) --- .github/workflows/generic_vulnerability-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generic_vulnerability-scan.yml b/.github/workflows/generic_vulnerability-scan.yml index 95245557..08b4fd29 100644 --- a/.github/workflows/generic_vulnerability-scan.yml +++ b/.github/workflows/generic_vulnerability-scan.yml @@ -62,6 +62,6 @@ jobs: fail-build: false - name: 'Upload vulnerability report' - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.scan.outputs.sarif }} From b9f7ff86dea6709f785be0bf900c2845f9e321ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:50:56 +0100 Subject: [PATCH 03/34] chore(deps): Bump docker/metadata-action from 5.3.0 to 5.4.0 (#3710) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/v5.3.0...v5.4.0) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/generic_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generic_publish.yml b/.github/workflows/generic_publish.yml index 36b44770..b0a32b93 100644 --- a/.github/workflows/generic_publish.yml +++ b/.github/workflows/generic_publish.yml @@ -23,7 +23,7 @@ jobs: - name: 'Prepare tags' id: prep - uses: docker/metadata-action@v5.3.0 + uses: docker/metadata-action@v5.4.0 with: images: | ${{ secrets.DOCKER_REPOSITORY }} From 5015dc80b16f61e29cb9b496ceb6c6210de635a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 00:16:38 +0100 Subject: [PATCH 04/34] chore(deps): Bump actions/upload-artifact from 3 to 4 (#3708) --- .github/workflows/docs-preview-prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs-preview-prepare.yml b/.github/workflows/docs-preview-prepare.yml index befd4008..cee5562a 100644 --- a/.github/workflows/docs-preview-prepare.yml +++ b/.github/workflows/docs-preview-prepare.yml @@ -73,7 +73,7 @@ jobs: tar --zstd -cf artifact.tar.zst pr.env ${{ env.BUILD_DIR }} - name: 'Upload artifact for workflow transfer' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: preview-build path: artifact.tar.zst From ee87291225f55feb4a4c96b2383c55827db54736 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 01:29:15 +0100 Subject: [PATCH 05/34] chore(deps): Bump dawidd6/action-download-artifact from 2 to 3 (#3707) --- .github/workflows/docs-preview-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs-preview-deploy.yml b/.github/workflows/docs-preview-deploy.yml index cac2ac64..93819efa 100644 --- a/.github/workflows/docs-preview-deploy.yml +++ b/.github/workflows/docs-preview-deploy.yml @@ -25,7 +25,7 @@ jobs: # The official Github Action for downloading artifacts does not support multi-workflow - name: 'Download build artifact' - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} run_id: ${{ github.event.workflow_run.id }} From 5908d9f060b1c74d7f168a578d11869ff15f7ad4 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:33:38 +1300 Subject: [PATCH 06/34] tests(refactor): Dovecot quotas (#3068) * chore: Extract out Dovecot Quota test cases into new test file Test cases are just cut + paste, no logic changed there yet. * chore: Rename test case descriptions * chore: Use `setup ...` methods instead of direct calls * chore: Adjust `_run_in_container_bash` to `_run_in_container` Plus some additional bug fixes in the disabled test case * tests(refactor): Revise ENV test cases for max mailbox and message sizes * tests(refactor): Revise ENV test cases for mailbox and message limits v2 Removes the extra variables and filtering in favour of explicit values instead of matching for comparison. - Easier at a glance to know what is actually expected. - Additionally reworks the quota limit checks in other test cases. Using a different formatter for `doveadm` is easier to match the desired value (`Limit`). * chore: Sync improvement from `tests.bats` master --- NOTE: This PR has been merged to avoid additional maintenance burden without losing the improvements. It was not considered complete, but remaining tasks were not documented in the PR. --- docs/content/config/environment.md | 8 +- mailserver.env | 4 +- target/bin/setquota | 6 +- .../parallel/set1/dovecot/dovecot_quotas.bats | 246 ++++++++++++++++++ test/tests/serial/tests.bats | 193 -------------- 5 files changed, 257 insertions(+), 200 deletions(-) create mode 100644 test/tests/parallel/set1/dovecot/dovecot_quotas.bats diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index 5ef3b968..71807c87 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -228,9 +228,9 @@ Provide any valid URI. Examples: - `lmtps:inet::` (secure lmtp with starttls) - `lmtp::2003` (use kopano as mailstore) -##### POSTFIX\_MAILBOX\_SIZE\_LIMIT +##### POSTFIX_MAILBOX_SIZE_LIMIT -Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). +Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). Size is in bytes. - **empty** => 0 (no limit) @@ -241,9 +241,9 @@ Set the mailbox size limit for all users. If set to zero, the size will be unlim See [mailbox quota][docs-accounts-quota]. -##### POSTFIX\_MESSAGE\_SIZE\_LIMIT +##### POSTFIX_MESSAGE_SIZE_LIMIT -Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) +Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes. - **empty** => 10240000 (~10 MB) diff --git a/mailserver.env b/mailserver.env index 48b537bc..e84157a2 100644 --- a/mailserver.env +++ b/mailserver.env @@ -254,7 +254,7 @@ VIRUSMAILS_DELETE_DELAY= # `lmtp::2003` (use kopano as mailstore) POSTFIX_DAGENT= -# Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). +# Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). Size is in bytes. # # empty => 0 POSTFIX_MAILBOX_SIZE_LIMIT= @@ -264,7 +264,7 @@ POSTFIX_MAILBOX_SIZE_LIMIT= # 1 => Dovecot quota is enabled ENABLE_QUOTAS=1 -# Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) +# Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes. # # empty => 10240000 (~10 MB) POSTFIX_MESSAGE_SIZE_LIMIT= diff --git a/target/bin/setquota b/target/bin/setquota index 5b2bba41..039421b2 100755 --- a/target/bin/setquota +++ b/target/bin/setquota @@ -59,10 +59,14 @@ function _quota_request_if_missing() { fi } + +# Dovecot docs incorrectly refer to these units with names for SI types (base 10), +# But then mentions they're actually treated as IEC type (base 2): +# https://doc.dovecot.org/settings/types/#size function _quota_unit_is_valid() { if ! grep -qE "^([0-9]+(B|k|M|G|T)|0)\$" <<< "${QUOTA}"; then __usage - _exit_with_error 'Invalid quota format. e.g. 302M (B (byte), k (kilobyte), M (megabyte), G (gigabyte) or T (terabyte))' + _exit_with_error 'Invalid quota format. e.g. 302M (B (byte), k (kibibyte), M (mebibyte), G (gibibyte) or T (tebibyte))' fi } diff --git a/test/tests/parallel/set1/dovecot/dovecot_quotas.bats b/test/tests/parallel/set1/dovecot/dovecot_quotas.bats new file mode 100644 index 00000000..2c176235 --- /dev/null +++ b/test/tests/parallel/set1/dovecot/dovecot_quotas.bats @@ -0,0 +1,246 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +# upstream default: 10 240 000 +# https://www.postfix.org/postconf.5.html#message_size_limit +# > The maximal size in bytes of a message, including envelope information. +# > The value cannot exceed LONG_MAX (typically, a 32-bit or 64-bit signed integer). +# > Note: Be careful when making changes. Excessively small values will result in the loss of non-delivery notifications, when a bounce message size exceeds the local or remote MTA's message size limit. + +# upstream default: 51 200 000 +# https://www.postfix.org/postconf.5.html#mailbox_size_limit +# > The maximal size of any local(8) individual mailbox or maildir file, or zero (no limit). +# > In fact, this limits the size of any file that is written to upon local delivery, including files written by external commands that are executed by the local(8) delivery agent. +# > The value cannot exceed LONG_MAX (typically, a 32-bit or 64-bit signed integer). +# > This limit must not be smaller than the message size limit. + +# upstream default: 51 200 000 +# https://www.postfix.org/postconf.5.html#virtual_mailbox_limit +# > The maximal size in bytes of an individual virtual(8) mailbox or maildir file, or zero (no limit). +# > This parameter is specific to the virtual(8) delivery agent. +# > It does not apply when mail is delivered with a different mail delivery program. + +BATS_TEST_NAME_PREFIX='[Dovecot Quotas] ' +CONTAINER_NAME='dms-test_dovecot-quotas' + +function setup_file() { + _init_with_defaults + + local CONTAINER_ARGS_ENV_CUSTOM=( + --env ENABLE_QUOTAS=1 + --env POSTFIX_MAILBOX_SIZE_LIMIT=4096000 + --env POSTFIX_MESSAGE_SIZE_LIMIT=2048000 + --env PERMIT_DOCKER=container + ) + _common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' +} + +function teardown_file() { _default_teardown ; } + +@test 'should only support setting quota for a valid account' { + # Prepare + _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' + + # Actual tests + _run_in_container setup quota set quota_user 50M + assert_failure + + _run_in_container setup quota set username@fulldomain 50M + assert_failure + + _run_in_container setup quota set quota_user@domain.tld 50M + assert_success + + # Cleanup + _run_in_container setup email del -y quota_user@domain.tld + assert_success +} + +@test 'should only allow valid units as quota size' { + # Prepare + _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' + + # Actual tests + _run_in_container setup quota set quota_user@domain.tld 26GIGOTS + assert_failure + _run_in_container setup quota set quota_user@domain.tld 123 + assert_failure + _run_in_container setup quota set quota_user@domain.tld M + assert_failure + _run_in_container setup quota set quota_user@domain.tld -60M + assert_failure + + + _run_in_container setup quota set quota_user@domain.tld 10B + assert_success + _run_in_container setup quota set quota_user@domain.tld 10k + assert_success + _run_in_container setup quota set quota_user@domain.tld 10M + assert_success + _run_in_container setup quota set quota_user@domain.tld 10G + assert_success + _run_in_container setup quota set quota_user@domain.tld 10T + assert_success + + # Cleanup + _run_in_container setup email del -y quota_user@domain.tld + assert_success +} + +@test 'should only support removing quota from a valid account' { + # Prepare + _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' + + # Actual tests + _run_in_container setup quota del uota_user@domain.tld + assert_failure + _run_in_container setup quota del quota_user + assert_failure + _run_in_container setup quota del dontknowyou@domain.tld + assert_failure + + _run_in_container setup quota set quota_user@domain.tld 10T + assert_success + _run_in_container setup quota del quota_user@domain.tld + assert_success + _run_in_container grep -i 'quota_user@domain.tld' /tmp/docker-mailserver/dovecot-quotas.cf + assert_failure + + # Cleanup + _run_in_container setup email del -y quota_user@domain.tld + assert_success +} + +@test 'should not error when there is no quota to remove for an account' { + # Prepare + _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' + + # Actual tests + _run_in_container grep -i 'quota_user@domain.tld' /tmp/docker-mailserver/dovecot-quotas.cf + assert_failure + + _run_in_container setup quota del quota_user@domain.tld + assert_success + _run_in_container setup quota del quota_user@domain.tld + assert_success + + # Cleanup + _run_in_container setup email del -y quota_user@domain.tld + assert_success +} + +@test 'should have configured Postfix to use the Dovecot quota-status service' { + _run_in_container postconf + assert_success + assert_output --partial 'check_policy_service inet:localhost:65265' +} + +@test '(ENV POSTFIX_MAILBOX_SIZE_LIMIT) should be configured for both Postfix and Dovecot' { + _run_in_container postconf -h mailbox_size_limit + assert_output 4096000 + + # Dovecot mailbox is sized by `virtual_mailbox_size` from Postfix: + _run_in_container postconf -h virtual_mailbox_limit + assert_output 4096000 + + # Quota support: + _run_in_container doveconf -h plugin/quota_rule + # Global default storage limit quota for each mailbox 4 MiB: + assert_output '*:storage=4M' + + # Sizes are equivalent - Bytes to MiB (rounded): + run numfmt --to=iec --format '%.0f' 4096000 + assert_output '4M' +} + +@test '(ENV POSTFIX_MESSAGE_SIZE_LIMIT) should be configured for both Postfix and Dovecot' { + _run_in_container postconf -h message_size_limit + assert_output 2048000 + + _run_in_container doveconf -h plugin/quota_max_mail_size + assert_output '2M' + + # Sizes are equivalent - Bytes to MiB (rounded): + run numfmt --to=iec --format '%.0f' 2048000 + assert_output '2M' +} + +@test 'Deleting an mailbox account should also remove that account from dovecot-quotas.cf' { + _add_mail_account_then_wait_until_ready 'quserremoved@domain.tld' + + _run_in_container setup quota set quserremoved@domain.tld 12M + assert_success + + _run_in_container cat '/tmp/docker-mailserver/dovecot-quotas.cf' + assert_success + assert_output 'quserremoved@domain.tld:12M' + + _run_in_container setup email del -y quserremoved@domain.tld + assert_success + + _run_in_container cat /tmp/docker-mailserver/dovecot-quotas.cf + assert_success + refute_output --partial 'quserremoved@domain.tld:12M' +} + +@test 'Dovecot should acknowledge quota configured for accounts' { + # sed -nE 's/.*STORAGE.*Limit=([0-9]+).*/\1/p' | numfmt --from-unit=1024 --to=iec --format '%.0f' + local CMD_GET_QUOTA="doveadm -f flow quota get -u 'user1@localhost.localdomain'" + + # 4M == 4096 kiB (numfmt --to-unit=1024 --from=iec 4M) + _run_in_container_bash "${CMD_GET_QUOTA}" + assert_line --partial 'Type=STORAGE Value=0 Limit=4096' + + # Setting a new limit for the user: + _run_in_container setup quota set 'user1@localhost.localdomain' 50M + assert_success + # 50M (50 * 1024^2) == 51200 kiB (numfmt --to-unit=1024 --from=iec 52428800) + run _repeat_until_success_or_timeout 20 _exec_in_container_bash "${CMD_GET_QUOTA} | grep -o 'Type=STORAGE Value=0 Limit=51200'" + assert_success + + # Deleting quota resets it to default global quota limit (`plugin/quota_rule`): + _run_in_container setup quota del 'user1@localhost.localdomain' + assert_success + run _repeat_until_success_or_timeout 20 _exec_in_container_bash "${CMD_GET_QUOTA} | grep -o 'Type=STORAGE Value=0 Limit=4096'" + assert_success +} + +@test 'should receive a warning mail from Dovecot when quota is exceeded' { + # skip 'disabled as it fails randomly: https://github.com/docker-mailserver/docker-mailserver/pull/2511' + + # Prepare + _add_mail_account_then_wait_until_ready 'quotauser@otherdomain.tld' + + # Actual tests + _run_in_container setup quota set quotauser@otherdomain.tld 10k + assert_success + + # wait until quota has been updated + run _repeat_until_success_or_timeout 20 _exec_in_container_bash "doveadm -f flow quota get -u 'quotauser@otherdomain.tld' | grep -o 'Type=STORAGE Value=0 Limit=10'" + assert_success + + # dovecot and postfix has been restarted + _wait_for_service postfix + _wait_for_service dovecot + sleep 10 + + # send some big emails + _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' + _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' + _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' + # check for quota warn message existence + run _repeat_until_success_or_timeout 20 _exec_in_container grep -R 'Subject: quota warning' /var/mail/otherdomain.tld/quotauser/new/ + assert_success + + run _repeat_until_success_or_timeout 20 sh -c "docker logs ${CONTAINER_NAME} | grep 'Quota exceeded (mailbox for user is full)'" + assert_success + + # ensure only the first big message and the warn message are present (other messages are rejected: mailbox is full) + _run_in_container sh -c 'ls /var/mail/otherdomain.tld/quotauser/new/ | wc -l' + assert_success + assert_output "2" + + # Cleanup + _run_in_container setup email del -y quotauser@otherdomain.tld + assert_success +} diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index 20ee0dd1..094454f0 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -17,7 +17,6 @@ function setup_file() { local CONTAINER_ARGS_ENV_CUSTOM=( --env ENABLE_AMAVIS=1 --env AMAVIS_LOGLEVEL=2 - --env ENABLE_QUOTAS=1 --env ENABLE_SRS=1 --env PERMIT_DOCKER=host --env PFLOGSUMM_TRIGGER=logrotate @@ -244,198 +243,6 @@ zip EOF } -@test "quota: setquota user must be existing" { - _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' - - _run_in_container_bash "setquota quota_user 50M" - assert_failure - _run_in_container_bash "setquota quota_user@domain.tld 50M" - assert_success - - _run_in_container_bash "setquota username@fulldomain 50M" - assert_failure - - _run_in_container_bash "delmailuser -y quota_user@domain.tld" - assert_success -} - -@test "quota: setquota must be well formatted" { - _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' - - _run_in_container_bash "setquota quota_user@domain.tld 26GIGOTS" - assert_failure - _run_in_container_bash "setquota quota_user@domain.tld 123" - assert_failure - _run_in_container_bash "setquota quota_user@domain.tld M" - assert_failure - _run_in_container_bash "setquota quota_user@domain.tld -60M" - assert_failure - - - _run_in_container_bash "setquota quota_user@domain.tld 10B" - assert_success - _run_in_container_bash "setquota quota_user@domain.tld 10k" - assert_success - _run_in_container_bash "setquota quota_user@domain.tld 10M" - assert_success - _run_in_container_bash "setquota quota_user@domain.tld 10G" - assert_success - _run_in_container_bash "setquota quota_user@domain.tld 10T" - assert_success - - - _run_in_container_bash "delmailuser -y quota_user@domain.tld" - assert_success -} - -@test "quota: delquota user must be existing" { - _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' - - _run_in_container_bash "delquota uota_user@domain.tld" - assert_failure - _run_in_container_bash "delquota quota_user" - assert_failure - _run_in_container_bash "delquota dontknowyou@domain.tld" - assert_failure - - _run_in_container_bash "setquota quota_user@domain.tld 10T" - assert_success - _run_in_container_bash "delquota quota_user@domain.tld" - assert_success - _run_in_container_bash "grep -i 'quota_user@domain.tld' /tmp/docker-mailserver/dovecot-quotas.cf" - assert_failure - - _run_in_container_bash "delmailuser -y quota_user@domain.tld" - assert_success -} - -@test "quota: delquota allow when no quota for existing user" { - _add_mail_account_then_wait_until_ready 'quota_user@domain.tld' - - _run_in_container_bash "grep -i 'quota_user@domain.tld' /tmp/docker-mailserver/dovecot-quotas.cf" - assert_failure - - _run_in_container_bash "delquota quota_user@domain.tld" - assert_success - _run_in_container_bash "delquota quota_user@domain.tld" - assert_success - - _run_in_container_bash "delmailuser -y quota_user@domain.tld" - assert_success -} - -@test "quota: dovecot quota present in postconf" { - _run_in_container_bash "postconf | grep 'check_policy_service inet:localhost:65265'" - assert_success -} - - -@test "quota: dovecot mailbox max size must be equal to postfix mailbox max size" { - postfix_mailbox_size=$(_exec_in_container_bash "postconf | grep -Po '(?<=mailbox_size_limit = )[0-9]+'") - run echo "${postfix_mailbox_size}" - refute_output "" - - # dovecot relies on virtual_mailbox_size by default - postfix_virtual_mailbox_size=$(_exec_in_container_bash "postconf | grep -Po '(?<=virtual_mailbox_limit = )[0-9]+'") - assert_equal "${postfix_virtual_mailbox_size}" "${postfix_mailbox_size}" - - postfix_mailbox_size_mb=$(( postfix_mailbox_size / 1000000)) - - dovecot_mailbox_size_mb=$(_exec_in_container_bash "doveconf | grep -oP '(?<=quota_rule \= \*\:storage=)[0-9]+'") - run echo "${dovecot_mailbox_size_mb}" - refute_output "" - - assert_equal "${postfix_mailbox_size_mb}" "${dovecot_mailbox_size_mb}" -} - - -@test "quota: dovecot message max size must be equal to postfix messsage max size" { - postfix_message_size=$(_exec_in_container_bash "postconf | grep -Po '(?<=message_size_limit = )[0-9]+'") - run echo "${postfix_message_size}" - refute_output "" - - postfix_message_size_mb=$(( postfix_message_size / 1000000)) - - dovecot_message_size_mb=$(_exec_in_container_bash "doveconf | grep -oP '(?<=quota_max_mail_size = )[0-9]+'") - run echo "${dovecot_message_size_mb}" - refute_output "" - - assert_equal "${postfix_message_size_mb}" "${dovecot_message_size_mb}" -} - -@test "quota: quota directive is removed when mailbox is removed" { - _add_mail_account_then_wait_until_ready 'quserremoved@domain.tld' - - _run_in_container_bash "setquota quserremoved@domain.tld 12M" - assert_success - - _run_in_container_bash 'cat /tmp/docker-mailserver/dovecot-quotas.cf | grep -E "^quserremoved@domain.tld\:12M\$" | wc -l | grep 1' - assert_success - - _run_in_container_bash "delmailuser -y quserremoved@domain.tld" - assert_success - - _run_in_container_bash 'cat /tmp/docker-mailserver/dovecot-quotas.cf | grep -E "^quserremoved@domain.tld\:12M\$"' - assert_failure -} - -@test "quota: dovecot applies user quota" { - _run_in_container_bash "doveadm quota get -u 'user1@localhost.localdomain' | grep 'User quota STORAGE'" - assert_output --partial "- 0" - - _run_in_container_bash "setquota user1@localhost.localdomain 50M" - assert_success - - # wait until quota has been updated - run _repeat_until_success_or_timeout 20 _exec_in_container_bash 'doveadm quota get -u user1@localhost.localdomain | grep -oP "(User quota STORAGE\s+[0-9]+\s+)51200(.*)"' - assert_success - - _run_in_container_bash "delquota user1@localhost.localdomain" - assert_success - - # wait until quota has been updated - run _repeat_until_success_or_timeout 20 _exec_in_container_bash 'doveadm quota get -u user1@localhost.localdomain | grep -oP "(User quota STORAGE\s+[0-9]+\s+)-(.*)"' - assert_success -} - -@test "quota: warn message received when quota exceeded" { - skip 'disabled as it fails randomly: https://github.com/docker-mailserver/docker-mailserver/pull/2511' - - # create user - _add_mail_account_then_wait_until_ready 'quotauser@otherdomain.tld' - _run_in_container_bash 'setquota quotauser@otherdomain.tld 10k' - assert_success - - # wait until quota has been updated - run _repeat_until_success_or_timeout 20 _exec_in_container_bash 'doveadm quota get -u quotauser@otherdomain.tld | grep -oP \"(User quota STORAGE\s+[0-9]+\s+)10(.*)\"' - assert_success - - # dovecot and postfix has been restarted - _wait_for_service postfix - _wait_for_service dovecot - sleep 10 - - # send some big emails - _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' - _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' - _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' - - # check for quota warn message existence - run _repeat_until_success_or_timeout 20 _exec_in_container_bash 'grep \"Subject: quota warning\" /var/mail/otherdomain.tld/quotauser/new/ -R' - assert_success - - run _repeat_until_success_or_timeout 20 sh -c "docker logs mail | grep 'Quota exceeded (mailbox for user is full)'" - assert_success - - # ensure only the first big message and the warn message are present (other messages are rejected: mailbox is full) - _run_in_container sh -c 'ls /var/mail/otherdomain.tld/quotauser/new/ | wc -l' - assert_success - assert_output "2" - - _run_in_container_bash "delmailuser -y quotauser@otherdomain.tld" - assert_success -} - # # PERMIT_DOCKER mynetworks # From ca2c53dde714a5c4d64aebb6724cfb6d87352a54 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:41:07 +1300 Subject: [PATCH 07/34] ci: Avoiding linting `CONTRIBUTORS.yml` (#3705) The file is managed by the `contributors.yml` workflow, no need for linting to be triggered on PRs for that change. This should ideally skip the required check status for the lint workflow which cannot trigger implicitly for automated PRs. If this doesn't work the change should be reverted. --- .github/workflows/linting.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index bdde2d0f..1e93377f 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -2,6 +2,9 @@ name: Lint on: pull_request: + paths-ignore: + # Managed by workflow: contributors.yml + - CONTRIBUTORS.md push: branches: [ master ] From 03052a65b87542ce714d1e58dca579907b534d39 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:59:59 +1300 Subject: [PATCH 08/34] ci: Allow lint workflow to be manually triggered (#3714) * ci: Allow lint workflow to be manually triggered Without this a different event must occur to trigger the workflow, which is inconvenient for automated PRs. --- .github/workflows/linting.yml | 6 +++--- CHANGELOG.md | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 1e93377f..d419e5ec 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -1,10 +1,10 @@ name: Lint on: + # A workflow that creates a PR will not trigger this workflow, + # Providing a manual trigger as a workaround + workflow_dispatch: pull_request: - paths-ignore: - # Managed by workflow: contributors.yml - - CONTRIBUTORS.md push: branches: [ master ] diff --git a/CHANGELOG.md b/CHANGELOG.md index 339c92e0..8b1331f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ All notable changes to this project will be documented in this file. The format - Update-check: fix 'read' exit status ([#3688](https://github.com/docker-mailserver/docker-mailserver/pull/3688)) - **Rspamd:** - Switch to official arm64 packages to avoid segfaults ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686)) +- **CI / Automation:** + - The lint workflow can now be manually triggered by maintainers ([#3714]https://github.com/docker-mailserver/docker-mailserver/pull/3714) ## [v13.0.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.0.1) From 6a56c7e74936488626db973f1c586d888894fc82 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 06:01:25 +0000 Subject: [PATCH 09/34] docs: update `CONTRIBUTORS.md` (#3704) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CONTRIBUTORS.md | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 860f5df1..4103ad5f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -442,6 +442,13 @@ Thanks goes to these wonderful people ✨ pbek + + + reneploetz +
+ reneploetz +
+ Rubytastic2 @@ -469,15 +476,15 @@ Thanks goes to these wonderful people ✨
Zehir
- + + guardiande
guardiande
- - + kamuri @@ -512,15 +519,15 @@ Thanks goes to these wonderful people ✨
m-schmoock
- + + VanVan
VanVan
- - + elbracht @@ -555,15 +562,15 @@ Thanks goes to these wonderful people ✨
ubenmackin
- + + craue
craue
- - + danielpanteleit @@ -598,15 +605,15 @@ Thanks goes to these wonderful people ✨
emazzotta
- + + fl42
fl42
- - + ipernet @@ -641,15 +648,15 @@ Thanks goes to these wonderful people ✨
millaguie
- + + jedateach
jedateach
- - + spacecowboy @@ -684,15 +691,15 @@ Thanks goes to these wonderful people ✨
keslerm
- + + castorinop
castorinop
- - + p-fruck @@ -707,13 +714,6 @@ Thanks goes to these wonderful people ✨ Rillke - - - reneploetz -
- reneploetz -
- bobbravo2 From 226ec847a4875e80c4ceff4c486f71a88176b897 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:35:16 +1300 Subject: [PATCH 10/34] ci: Remove `VERSION` from `Dockerfile` (#3711) * ci: Remove `VERSION` from `Dockerfile` This line was meant to be dropped with the switch to using the `DMS_RELEASE` ENV. --- CHANGELOG.md | 1 + Dockerfile | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1331f1..37a7a123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ All notable changes to this project will be documented in this file. The format - **Internal**: - The container startup welcome log message now references `DMS_RELEASE` ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676)) - `VERSION` was incremented for prior releases to be notified of the v13.0.1 patch release ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676)) + - `VERSION` is no longer included in the image ([#3711](https://github.com/docker-mailserver/docker-mailserver/pull/3711)) - Update-check: fix 'read' exit status ([#3688](https://github.com/docker-mailserver/docker-mailserver/pull/3688)) - **Rspamd:** - Switch to official arm64 packages to avoid segfaults ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686)) diff --git a/Dockerfile b/Dockerfile index 0f19521a..4d0e3568 100644 --- a/Dockerfile +++ b/Dockerfile @@ -277,8 +277,6 @@ RUN < Date: Wed, 20 Dec 2023 01:43:32 +0100 Subject: [PATCH 11/34] Remove sed statement (#3715) --- CHANGELOG.md | 1 + target/scripts/startup/setup.d/dovecot.sh | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37a7a123..3fc60c05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to this project will be documented in this file. The format - `VERSION` was incremented for prior releases to be notified of the v13.0.1 patch release ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676)) - `VERSION` is no longer included in the image ([#3711](https://github.com/docker-mailserver/docker-mailserver/pull/3711)) - Update-check: fix 'read' exit status ([#3688](https://github.com/docker-mailserver/docker-mailserver/pull/3688)) + - `ENABLE_QUOTAS=0` no longer tries to remove non-existent config ([#3715](https://github.com/docker-mailserver/docker-mailserver/pull/3715)) - **Rspamd:** - Switch to official arm64 packages to avoid segfaults ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686)) - **CI / Automation:** diff --git a/target/scripts/startup/setup.d/dovecot.sh b/target/scripts/startup/setup.d/dovecot.sh index e46aca21..8e7dcfe7 100644 --- a/target/scripts/startup/setup.d/dovecot.sh +++ b/target/scripts/startup/setup.d/dovecot.sh @@ -106,9 +106,6 @@ function _setup_dovecot_quota() { "s|mail_plugins = \$mail_plugins imap_quota|mail_plugins = \$mail_plugins|g" \ /etc/dovecot/conf.d/20-imap.conf fi - - # disable quota policy check in postfix - sedfile -i "s|check_policy_service inet:localhost:65265||g" /etc/postfix/main.cf else if [[ -f /etc/dovecot/conf.d/90-quota.conf.disab ]]; then mv /etc/dovecot/conf.d/90-quota.conf.disab /etc/dovecot/conf.d/90-quota.conf From 72517d3f824859cb15a3ccc653ad8cc4bb1a4c32 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Tue, 26 Dec 2023 09:53:32 +1300 Subject: [PATCH 12/34] docs: Debugging - Delivery failure from service downtime (#3718) * docs: Debugging - Delivery failure from service downtime Services may be temporarily down, such as when restarted when certificates are updated due to the `check-for-changes.sh` service. This is another known source of intermittent delivery failures. --- CHANGELOG.md | 3 ++- docs/content/config/debugging.md | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc60c05..7a260508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ All notable changes to this project will be documented in this file. The format ### Updates - **Documentation:** - - Raise awareness in the troubleshooting page for a common misconfiguration when deviating from our advice by using a bare domain ([#3680](https://github.com/docker-mailserver/docker-mailserver/pull/3680)) + - Debugging - Raise awareness in the troubleshooting page for a common misconfiguration when deviating from our advice by using a bare domain ([#3680](https://github.com/docker-mailserver/docker-mailserver/pull/3680)) + - Debugging - Raise awareness of temporary downtime during certificate renewal that can cause a failure to deliver local mail ([#3718](https://github.com/docker-mailserver/docker-mailserver/pull/3718)) - **Internal:** - Postfix configures `virtual_mailbox_maps` and `virtual_transport` during startup instead of using defaults (configured for Dovecot) via our `main.cf` ([#3681](https://github.com/docker-mailserver/docker-mailserver/pull/3681)) diff --git a/docs/content/config/debugging.md b/docs/content/config/debugging.md index 9c3bebb5..d58430e1 100644 --- a/docs/content/config/debugging.md +++ b/docs/content/config/debugging.md @@ -55,6 +55,8 @@ Common logs related to this are: If your logs look like this, you likely have [assigned the same FQDN to the DMS `hostname` and your mail accounts][gh-issues::dms-fqdn-misconfigured] which is not supported by default. You can either adjust your DMS `hostname` or follow [this FAQ advice][docs::faq-bare-domain] +It is also possible that [DMS services are temporarily unavailable][gh-issues::dms-services-unavailable] when configuration changes are detected, producing the 2nd error. Certificate updates may be a less obvious trigger. + ## Steps for Debugging DMS 1. **Increase log verbosity**: Very helpful for troubleshooting problems during container startup. Set the environment variable [`LOG_LEVEL`][docs-environment-log-level] to `debug` or `trace`. @@ -126,6 +128,7 @@ This could be from outdated software, or running a system that isn't able to pro [gh-issues]: https://github.com/docker-mailserver/docker-mailserver/issues [gh-issues::dms-fqdn-misconfigured]: https://github.com/docker-mailserver/docker-mailserver/issues/3679#issuecomment-1837609043 +[gh-issues::dms-services-unavailable]: https://github.com/docker-mailserver/docker-mailserver/issues/3679#issuecomment-1848083358 [gh-macos-support]: https://github.com/docker-mailserver/docker-mailserver/issues/3648#issuecomment-1822774080 [gh-discuss-roundcube-fail2ban]: https://github.com/orgs/docker-mailserver/discussions/3273#discussioncomment-5654603 From 8392e3c1a8db660e6fd458b1bf426ab4736104c4 Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:58:54 +0100 Subject: [PATCH 13/34] release: v13.1.0 (#3720) Co-authored-by: Casper Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ VERSION | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a260508..32aaf78d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. The format > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +## [v13.1.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.1.0) + ### Added - **Dovecot:** @@ -22,6 +24,8 @@ All notable changes to this project will be documented in this file. The format - Debugging - Raise awareness of temporary downtime during certificate renewal that can cause a failure to deliver local mail ([#3718](https://github.com/docker-mailserver/docker-mailserver/pull/3718)) - **Internal:** - Postfix configures `virtual_mailbox_maps` and `virtual_transport` during startup instead of using defaults (configured for Dovecot) via our `main.cf` ([#3681](https://github.com/docker-mailserver/docker-mailserver/pull/3681)) +- **Rspamd:** + - Upgraded to version `3.7.5`. This was previously inconsistent between our AMD64 (`3.5`) and ARM64 (`3.4`) images ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686)) ### Fixed diff --git a/VERSION b/VERSION index 5cb7d856..e6ba3513 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -13.0.1 +13.1.0 From 0889b0ff063a37b482113a684d934e4bd728a33c Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Sat, 30 Dec 2023 09:59:09 +1300 Subject: [PATCH 14/34] fix: `supervisor-app.conf` - Correct the log location for `postgrey` (#3724) * fix: `supervisor-app.conf` - Correct `postgrey` log location Looks like this should have been like every other service and reference a log file(s) based on program name in the supervisor log directory. * tests: Adjust log location for `postgrey_enabled.bats` --- CHANGELOG.md | 1 + target/supervisor/conf.d/supervisor-app.conf | 4 ++-- test/tests/parallel/set1/spam_virus/postgrey_enabled.bats | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32aaf78d..f37fded4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ All notable changes to this project will be documented in this file. The format - `VERSION` is no longer included in the image ([#3711](https://github.com/docker-mailserver/docker-mailserver/pull/3711)) - Update-check: fix 'read' exit status ([#3688](https://github.com/docker-mailserver/docker-mailserver/pull/3688)) - `ENABLE_QUOTAS=0` no longer tries to remove non-existent config ([#3715](https://github.com/docker-mailserver/docker-mailserver/pull/3715)) + - The `postgrey` service now writes logs to the supervisor directory like all other services. Previously this was `/var/log/mail/mail.log` ([#3724](https://github.com/docker-mailserver/docker-mailserver/pull/3724)) - **Rspamd:** - Switch to official arm64 packages to avoid segfaults ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686)) - **CI / Automation:** diff --git a/target/supervisor/conf.d/supervisor-app.conf b/target/supervisor/conf.d/supervisor-app.conf index 2dd8b917..431357d8 100644 --- a/target/supervisor/conf.d/supervisor-app.conf +++ b/target/supervisor/conf.d/supervisor-app.conf @@ -83,8 +83,8 @@ startsecs=0 stopwaitsecs=55 autostart=false autorestart=true -stdout_logfile=/var/log/mail/mail.log -stderr_logfile=/var/log/mail/mail.log +stdout_logfile=/var/log/supervisor/%(program_name)s.log +stderr_logfile=/var/log/supervisor/%(program_name)s.log command=/usr/sbin/postgrey --inet=127.0.0.1:10023 --syslog-facility=mail --delay="%(ENV_POSTGREY_DELAY)s" --max-age="%(ENV_POSTGREY_MAX_AGE)s" --auto-whitelist-clients="%(ENV_POSTGREY_AUTO_WHITELIST_CLIENTS)s" --greylist-text="%(ENV_POSTGREY_TEXT)s" [program:amavis] diff --git a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats index d877a1ce..e32210ca 100644 --- a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats +++ b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats @@ -123,7 +123,7 @@ function _should_have_log_entry() { # Allow some extra time for logs to update to avoids a false-positive failure: _run_until_success_or_timeout 10 _exec_in_container grep \ "${ACTION}, ${REASON}," \ - /var/log/mail/mail.log + /var/log/supervisor/postgrey.log # Log entry matched should be for the expected triplet: assert_output --partial "${TRIPLET}" From 9e81517fe36d95597d3c8890f998bd7f9ea29aa7 Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Wed, 3 Jan 2024 01:17:54 +0100 Subject: [PATCH 15/34] tests: Use `swaks` instead of `nc` for sending mail (#3732) See associated `CHANGELOG.md` entry for details. --------- Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- .gitattributes | 6 +- CHANGELOG.md | 7 ++ target/scripts/build/packages.sh | 2 +- .../auth/added-imap-auth.txt | 0 .../auth/added-pop3-auth.txt | 0 test/{test-files => files}/auth/imap-auth.txt | 0 .../auth/imap-ldap-auth.txt | 0 test/{test-files => files}/auth/pop3-auth.txt | 0 .../emails/amavis/spam.txt} | 9 +- .../emails/amavis/virus.txt} | 9 +- .../auth/added-smtp-auth-spoofed-alias.txt | 9 -- .../emails}/auth/added-smtp-auth-spoofed.txt | 9 -- .../auth/ldap-smtp-auth-spoofed-alias.txt | 10 --- ...h-spoofed-sender-with-filter-exception.txt | 10 --- .../emails}/auth/ldap-smtp-auth-spoofed.txt | 10 --- .../emails/existing/added.txt} | 7 -- .../emails/existing/alias-external.txt} | 7 -- .../emails/existing/alias-local.txt} | 7 -- .../existing/alias-recipient-delimiter.txt} | 7 -- .../emails/existing/catchall-local.txt} | 7 -- .../existing/regexp-alias-external.txt} | 7 -- .../emails/existing/regexp-alias-local.txt} | 7 -- .../existing/user-and-cc-local-alias.txt} | 7 -- .../emails/existing/user1.txt} | 7 -- .../emails/nc_raw/dsn/authenticated.txt} | 0 .../emails/nc_raw/dsn/unauthenticated.txt} | 0 .../emails/nc_raw}/postscreen.txt | 0 .../emails/nc_raw}/smtp-only.txt | 0 .../emails}/non-existing-user.txt | 7 -- .../emails}/postgrey.txt | 7 -- test/files/emails/postscreen.txt | 5 ++ .../emails/privacy.txt} | 9 -- .../emails}/quota-exceeded.txt | 7 -- .../emails/rspamd/pass.txt} | 9 +- .../emails/rspamd/spam-header.txt} | 7 -- .../emails/rspamd/spam.txt} | 7 -- .../emails/rspamd/virus.txt} | 7 -- .../emails/sendmail}/root-email.txt | 0 .../emails/sieve/pipe.txt} | 7 -- .../emails/sieve/spam-folder.txt} | 7 -- .../emails}/test-email.txt | 0 .../nc}/imap_special_use_folders.txt | 0 .../nc}/postgrey_whitelist.txt | 0 .../nc}/postgrey_whitelist_recipients.txt | 0 .../nc}/rspamd_imap_move_to_inbox.txt | 0 .../nc}/rspamd_imap_move_to_junk.txt | 0 .../ssl/custom-dhe-params.pem | 0 .../ssl/example.test/README.md | 0 .../ssl/example.test/cert.ecdsa.pem | 0 .../ssl/example.test/cert.rsa.pem | 0 .../ssl/example.test/key.ecdsa.pem | 0 .../ssl/example.test/key.rsa.pem | 0 .../ssl/example.test/testssl.txt | 0 .../ssl/example.test/traefik.md | 0 .../with_ca/ecdsa/ca-cert.ecdsa.pem | 0 .../with_ca/ecdsa/ca-key.ecdsa.pem | 0 .../example.test/with_ca/ecdsa/cert.ecdsa.pem | 0 .../example.test/with_ca/ecdsa/cert.rsa.pem | 0 .../with_ca/ecdsa/ecdsa.acme.json | 0 .../example.test/with_ca/ecdsa/key.ecdsa.pem | 0 .../example.test/with_ca/ecdsa/key.rsa.pem | 0 .../example.test/with_ca/ecdsa/rsa.acme.json | 0 .../with_ca/ecdsa/wildcard/cert.ecdsa.pem | 0 .../with_ca/ecdsa/wildcard/ecdsa.acme.json | 0 .../with_ca/ecdsa/wildcard/key.ecdsa.pem | 0 .../example.test/with_ca/rsa/ca-cert.rsa.pem | 0 .../example.test/with_ca/rsa/ca-key.rsa.pem | 0 .../example.test/with_ca/rsa/cert.ecdsa.pem | 0 .../ssl/example.test/with_ca/rsa/cert.rsa.pem | 0 .../example.test/with_ca/rsa/ecdsa.acme.json | 0 .../example.test/with_ca/rsa/key.ecdsa.pem | 0 .../ssl/example.test/with_ca/rsa/key.rsa.pem | 0 .../example.test/with_ca/rsa/rsa.acme.json | 0 .../with_ca/rsa/wildcard/cert.rsa.pem | 0 .../with_ca/rsa/wildcard/key.rsa.pem | 0 .../with_ca/rsa/wildcard/rsa.acme.json | 0 test/helper/common.bash | 14 +++ test/helper/sending.bash | 65 +++++++++----- test/helper/setup.bash | 2 +- .../auth/added-smtp-auth-login-wrong.txt | 4 - .../test-files/auth/added-smtp-auth-login.txt | 4 - .../auth/added-smtp-auth-plain-wrong.txt | 3 - .../test-files/auth/added-smtp-auth-plain.txt | 3 - test/test-files/auth/sasl-ldap-smtp-auth.txt | 5 -- .../test-files/auth/smtp-auth-login-wrong.txt | 4 - test/test-files/auth/smtp-auth-login.txt | 4 - .../test-files/auth/smtp-auth-plain-wrong.txt | 3 - test/test-files/auth/smtp-auth-plain.txt | 3 - .../email-templates/existing-user2.txt | 12 --- .../email-templates/existing-user3.txt | 12 --- test/test-files/email-templates/smtp-ehlo.txt | 2 - .../parallel/set1/dovecot/dovecot_quotas.bats | 9 +- .../parallel/set1/dovecot/dovecot_sieve.bats | 6 +- .../set1/dovecot/mailbox_format_dbox.bats | 6 +- .../set1/dovecot/special_use_folders.bats | 5 +- .../parallel/set1/spam_virus/clamav.bats | 13 +-- .../disabled_clamav_spamassassin.bats | 4 +- .../parallel/set1/spam_virus/fail2ban.bats | 13 ++- .../set1/spam_virus/postgrey_enabled.bats | 37 +++----- .../parallel/set1/spam_virus/postscreen.bats | 55 +++++------- .../parallel/set1/spam_virus/rspamd_full.bats | 12 +-- .../set1/spam_virus/spam_junk_folder.bats | 2 +- test/tests/parallel/set1/tls/dhparams.bats | 2 +- test/tests/parallel/set1/tls/letsencrypt.bats | 4 +- test/tests/parallel/set1/tls/manual.bats | 6 +- test/tests/parallel/set2/tls_cipherlists.bats | 2 +- .../container_configuration/hostname.bats | 2 +- test/tests/parallel/set3/mta/dsn.bats | 20 +++-- test/tests/parallel/set3/mta/lmtp_ip.bats | 2 +- test/tests/parallel/set3/mta/privacy.bats | 6 +- .../parallel/set3/mta/smtp_delivery.bats | 88 +++++++++++-------- test/tests/parallel/set3/mta/smtponly.bats | 11 ++- test/tests/serial/mail_pop3.bats | 6 +- test/tests/serial/mail_with_imap.bats | 28 ++++-- test/tests/serial/mail_with_ldap.bats | 57 +++++++++--- test/tests/serial/permit_docker.bats | 10 +-- test/tests/serial/test_helper.bats | 8 +- test/tests/serial/tests.bats | 31 ++++++- test/tests/serial/vmail-id.bats | 2 +- 119 files changed, 355 insertions(+), 455 deletions(-) rename test/{test-files => files}/auth/added-imap-auth.txt (100%) rename test/{test-files => files}/auth/added-pop3-auth.txt (100%) rename test/{test-files => files}/auth/imap-auth.txt (100%) rename test/{test-files => files}/auth/imap-ldap-auth.txt (100%) rename test/{test-files => files}/auth/pop3-auth.txt (100%) rename test/{test-files/email-templates/amavis-spam.txt => files/emails/amavis/spam.txt} (63%) rename test/{test-files/email-templates/amavis-virus.txt => files/emails/amavis/virus.txt} (83%) rename test/{test-files => files/emails}/auth/added-smtp-auth-spoofed-alias.txt (52%) rename test/{test-files => files/emails}/auth/added-smtp-auth-spoofed.txt (53%) rename test/{test-files => files/emails}/auth/ldap-smtp-auth-spoofed-alias.txt (57%) rename test/{test-files => files/emails}/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt (58%) rename test/{test-files => files/emails}/auth/ldap-smtp-auth-spoofed.txt (53%) rename test/{test-files/email-templates/existing-added.txt => files/emails/existing/added.txt} (67%) rename test/{test-files/email-templates/existing-alias-external.txt => files/emails/existing/alias-external.txt} (68%) rename test/{test-files/email-templates/existing-alias-local.txt => files/emails/existing/alias-local.txt} (68%) rename test/{test-files/email-templates/existing-alias-recipient-delimiter.txt => files/emails/existing/alias-recipient-delimiter.txt} (70%) rename test/{test-files/email-templates/existing-catchall-local.txt => files/emails/existing/catchall-local.txt} (68%) rename test/{test-files/email-templates/existing-regexp-alias-external.txt => files/emails/existing/regexp-alias-external.txt} (68%) rename test/{test-files/email-templates/existing-regexp-alias-local.txt => files/emails/existing/regexp-alias-local.txt} (68%) rename test/{test-files/email-templates/existing-user-and-cc-local-alias.txt => files/emails/existing/user-and-cc-local-alias.txt} (73%) rename test/{test-files/email-templates/existing-user1.txt => files/emails/existing/user1.txt} (67%) rename test/{test-files/email-templates/dsn-authenticated.txt => files/emails/nc_raw/dsn/authenticated.txt} (100%) rename test/{test-files/email-templates/dsn-unauthenticated.txt => files/emails/nc_raw/dsn/unauthenticated.txt} (100%) rename test/{test-files/email-templates => files/emails/nc_raw}/postscreen.txt (100%) rename test/{test-files/email-templates => files/emails/nc_raw}/smtp-only.txt (100%) rename test/{test-files/email-templates => files/emails}/non-existing-user.txt (67%) rename test/{test-files/email-templates => files/emails}/postgrey.txt (66%) create mode 100644 test/files/emails/postscreen.txt rename test/{test-files/email-templates/send-privacy-email.txt => files/emails/privacy.txt} (61%) rename test/{test-files/email-templates => files/emails}/quota-exceeded.txt (98%) rename test/{test-files/email-templates/rspamd-pass.txt => files/emails/rspamd/pass.txt} (57%) rename test/{test-files/email-templates/rspamd-spam-header.txt => files/emails/rspamd/spam-header.txt} (70%) rename test/{test-files/email-templates/rspamd-spam.txt => files/emails/rspamd/spam.txt} (70%) rename test/{test-files/email-templates/rspamd-virus.txt => files/emails/rspamd/virus.txt} (70%) rename test/{test-files/email-templates => files/emails/sendmail}/root-email.txt (100%) rename test/{test-files/email-templates/sieve-pipe.txt => files/emails/sieve/pipe.txt} (67%) rename test/{test-files/email-templates/sieve-spam-folder.txt => files/emails/sieve/spam-folder.txt} (64%) rename test/{test-files/email-templates => files/emails}/test-email.txt (100%) rename test/{test-files/nc_templates => files/nc}/imap_special_use_folders.txt (100%) rename test/{test-files/nc_templates => files/nc}/postgrey_whitelist.txt (100%) rename test/{test-files/nc_templates => files/nc}/postgrey_whitelist_recipients.txt (100%) rename test/{test-files/nc_templates => files/nc}/rspamd_imap_move_to_inbox.txt (100%) rename test/{test-files/nc_templates => files/nc}/rspamd_imap_move_to_junk.txt (100%) rename test/{test-files => files}/ssl/custom-dhe-params.pem (100%) rename test/{test-files => files}/ssl/example.test/README.md (100%) rename test/{test-files => files}/ssl/example.test/cert.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/cert.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/key.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/key.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/testssl.txt (100%) rename test/{test-files => files}/ssl/example.test/traefik.md (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/ca-key.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/cert.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/cert.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/ecdsa.acme.json (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/key.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/key.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/rsa.acme.json (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/wildcard/cert.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/wildcard/ecdsa.acme.json (100%) rename test/{test-files => files}/ssl/example.test/with_ca/ecdsa/wildcard/key.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/ca-cert.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/ca-key.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/cert.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/cert.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/ecdsa.acme.json (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/key.ecdsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/key.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/rsa.acme.json (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/wildcard/cert.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/wildcard/key.rsa.pem (100%) rename test/{test-files => files}/ssl/example.test/with_ca/rsa/wildcard/rsa.acme.json (100%) delete mode 100644 test/test-files/auth/added-smtp-auth-login-wrong.txt delete mode 100644 test/test-files/auth/added-smtp-auth-login.txt delete mode 100644 test/test-files/auth/added-smtp-auth-plain-wrong.txt delete mode 100644 test/test-files/auth/added-smtp-auth-plain.txt delete mode 100644 test/test-files/auth/sasl-ldap-smtp-auth.txt delete mode 100644 test/test-files/auth/smtp-auth-login-wrong.txt delete mode 100644 test/test-files/auth/smtp-auth-login.txt delete mode 100644 test/test-files/auth/smtp-auth-plain-wrong.txt delete mode 100644 test/test-files/auth/smtp-auth-plain.txt delete mode 100644 test/test-files/email-templates/existing-user2.txt delete mode 100644 test/test-files/email-templates/existing-user3.txt delete mode 100644 test/test-files/email-templates/smtp-ehlo.txt diff --git a/.gitattributes b/.gitattributes index d3dba13d..869c153e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,7 +10,7 @@ *.yml text ### Documentation (Project, Tests, Docs site) *.md text -### TLS certs (test/test-files/) + DHE params (target/shared/) +### TLS certs (test/files/) + DHE params (target/shared/) *.pem text *.pem.sha512sum text @@ -90,9 +90,9 @@ TrustedHosts text whitelist_recipients text ## MISC -### test/config/ + test/test-files/ +### test/config/ + test/files/ *.txt text -### test/linting/ (.ecrc.json) + test/test-files/ (*.acme.json): +### test/linting/ (.ecrc.json) + test/files/ (*.acme.json): *.json text ################################################# diff --git a/CHANGELOG.md b/CHANGELOG.md index f37fded4..eeeb843d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file. The format > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +### Updates + +- The test suite now uses `swaks` instead of `nc`, which has multiple benefits ([#3732](https://github.com/docker-mailserver/docker-mailserver/pull/3732)): + - `swaks` handles pipelining correctly, hence we can now use `reject_unauth_pipelining` in Postfix's configuration. + - `swaks` provides better CLI options that make many files superflous. + - `swaks` can also replace `openssl s_client` and handles authentication on submission ports better. + ## [v13.1.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.1.0) ### Added diff --git a/target/scripts/build/packages.sh b/target/scripts/build/packages.sh index ec468d41..566d5441 100644 --- a/target/scripts/build/packages.sh +++ b/target/scripts/build/packages.sh @@ -80,7 +80,7 @@ function _install_packages() { # `bind9-dnsutils` provides the `dig` command # `iputils-ping` provides the `ping` command DEBUG_PACKAGES=( - bind9-dnsutils iputils-ping less nano + bind9-dnsutils iputils-ping less nano swaks ) apt-get "${QUIET}" --no-install-recommends install \ diff --git a/test/test-files/auth/added-imap-auth.txt b/test/files/auth/added-imap-auth.txt similarity index 100% rename from test/test-files/auth/added-imap-auth.txt rename to test/files/auth/added-imap-auth.txt diff --git a/test/test-files/auth/added-pop3-auth.txt b/test/files/auth/added-pop3-auth.txt similarity index 100% rename from test/test-files/auth/added-pop3-auth.txt rename to test/files/auth/added-pop3-auth.txt diff --git a/test/test-files/auth/imap-auth.txt b/test/files/auth/imap-auth.txt similarity index 100% rename from test/test-files/auth/imap-auth.txt rename to test/files/auth/imap-auth.txt diff --git a/test/test-files/auth/imap-ldap-auth.txt b/test/files/auth/imap-ldap-auth.txt similarity index 100% rename from test/test-files/auth/imap-ldap-auth.txt rename to test/files/auth/imap-ldap-auth.txt diff --git a/test/test-files/auth/pop3-auth.txt b/test/files/auth/pop3-auth.txt similarity index 100% rename from test/test-files/auth/pop3-auth.txt rename to test/files/auth/pop3-auth.txt diff --git a/test/test-files/email-templates/amavis-spam.txt b/test/files/emails/amavis/spam.txt similarity index 63% rename from test/test-files/email-templates/amavis-spam.txt rename to test/files/emails/amavis/spam.txt index 66be1df3..e8d26138 100644 --- a/test/test-files/email-templates/amavis-spam.txt +++ b/test/files/emails/amavis/spam.txt @@ -1,13 +1,6 @@ -HELO mail.external.tld -MAIL FROM: spam@external.tld -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message amavis-spam.txt +Subject: Test Message amavis/spam.txt This is a test mail. XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X - -. -QUIT diff --git a/test/test-files/email-templates/amavis-virus.txt b/test/files/emails/amavis/virus.txt similarity index 83% rename from test/test-files/email-templates/amavis-virus.txt rename to test/files/emails/amavis/virus.txt index 1343a07c..2c47dcad 100644 --- a/test/test-files/email-templates/amavis-virus.txt +++ b/test/files/emails/amavis/virus.txt @@ -1,11 +1,7 @@ -HELO mail.external.tld -MAIL FROM: virus@external.tld -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message amavis-virus.txt +Subject: Test Message amavis/virus.txt Content-type: multipart/mixed; boundary="emailboundary" MIME-version: 1.0 @@ -27,6 +23,3 @@ ACAA/4EAAAAAZWljYXIuY29tUEsFBgAAAAABAAEANwAAAGsAAAAAAA== --emailboundary-- - -. -QUIT diff --git a/test/test-files/auth/added-smtp-auth-spoofed-alias.txt b/test/files/emails/auth/added-smtp-auth-spoofed-alias.txt similarity index 52% rename from test/test-files/auth/added-smtp-auth-spoofed-alias.txt rename to test/files/emails/auth/added-smtp-auth-spoofed-alias.txt index 48145183..eeb68ac8 100644 --- a/test/test-files/auth/added-smtp-auth-spoofed-alias.txt +++ b/test/files/emails/auth/added-smtp-auth-spoofed-alias.txt @@ -1,14 +1,5 @@ -EHLO mail -AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu -bXlwYXNzd29yZA== -MAIL FROM: alias1@localhost.localdomain -RCPT TO: user1@localhost.localdomain -DATA From: user1_alias To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message This is a test mail. - -. -QUIT diff --git a/test/test-files/auth/added-smtp-auth-spoofed.txt b/test/files/emails/auth/added-smtp-auth-spoofed.txt similarity index 53% rename from test/test-files/auth/added-smtp-auth-spoofed.txt rename to test/files/emails/auth/added-smtp-auth-spoofed.txt index 279b6c0e..fd96d401 100644 --- a/test/test-files/auth/added-smtp-auth-spoofed.txt +++ b/test/files/emails/auth/added-smtp-auth-spoofed.txt @@ -1,14 +1,5 @@ -EHLO mail -AUTH LOGIN YWRkZWRAbG9jYWxob3N0LmxvY2FsZG9tYWlu -bXlwYXNzd29yZA== -MAIL FROM: user2@localhost.localdomain -RCPT TO: user1@localhost.localdomain -DATA From: Not_My_Business To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message This is a test mail. - -. -QUIT diff --git a/test/test-files/auth/ldap-smtp-auth-spoofed-alias.txt b/test/files/emails/auth/ldap-smtp-auth-spoofed-alias.txt similarity index 57% rename from test/test-files/auth/ldap-smtp-auth-spoofed-alias.txt rename to test/files/emails/auth/ldap-smtp-auth-spoofed-alias.txt index 007b0f99..7453675c 100644 --- a/test/test-files/auth/ldap-smtp-auth-spoofed-alias.txt +++ b/test/files/emails/auth/ldap-smtp-auth-spoofed-alias.txt @@ -1,15 +1,5 @@ -EHLO mail -AUTH LOGIN -c29tZS51c2VyQGxvY2FsaG9zdC5sb2NhbGRvbWFpbg== -c2VjcmV0 -MAIL FROM: postmaster@localhost.localdomain -RCPT TO: some.user@localhost.localdomain -DATA From: alias_address To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message This is a test mail from ldap-smtp-auth-spoofed-alias.txt - -. -QUIT diff --git a/test/test-files/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt b/test/files/emails/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt similarity index 58% rename from test/test-files/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt rename to test/files/emails/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt index bc0447af..3b500bf6 100644 --- a/test/test-files/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt +++ b/test/files/emails/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt @@ -1,15 +1,5 @@ -EHLO mail -AUTH LOGIN -c29tZS51c2VyLmVtYWlsQGxvY2FsaG9zdC5sb2NhbGRvbWFpbgo= -c2VjcmV0 -MAIL FROM: randomspoofedaddress@localhost.localdomain -RCPT TO: some.user@localhost.localdomain -DATA From: spoofed_address To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message This is a test mail from ldap-smtp-auth-spoofed-sender-with-filter-exception.txt - -. -QUIT diff --git a/test/test-files/auth/ldap-smtp-auth-spoofed.txt b/test/files/emails/auth/ldap-smtp-auth-spoofed.txt similarity index 53% rename from test/test-files/auth/ldap-smtp-auth-spoofed.txt rename to test/files/emails/auth/ldap-smtp-auth-spoofed.txt index cc0b164d..83193e17 100644 --- a/test/test-files/auth/ldap-smtp-auth-spoofed.txt +++ b/test/files/emails/auth/ldap-smtp-auth-spoofed.txt @@ -1,15 +1,5 @@ -EHLO mail -AUTH LOGIN -c29tZS51c2VyQGxvY2FsaG9zdC5sb2NhbGRvbWFpbg== -c2VjcmV0 -MAIL FROM: ldap@localhost.localdomain -RCPT TO: user1@localhost.localdomain -DATA From: forged_address To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-added.txt b/test/files/emails/existing/added.txt similarity index 67% rename from test/test-files/email-templates/existing-added.txt rename to test/files/emails/existing/added.txt index 320fa4d2..827b681f 100644 --- a/test/test-files/email-templates/existing-added.txt +++ b/test/files/emails/existing/added.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: added@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-added.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-alias-external.txt b/test/files/emails/existing/alias-external.txt similarity index 68% rename from test/test-files/email-templates/existing-alias-external.txt rename to test/files/emails/existing/alias-external.txt index 61b1df3c..03f1af6c 100644 --- a/test/test-files/email-templates/existing-alias-external.txt +++ b/test/files/emails/existing/alias-external.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: alias1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-alias-external.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-alias-local.txt b/test/files/emails/existing/alias-local.txt similarity index 68% rename from test/test-files/email-templates/existing-alias-local.txt rename to test/files/emails/existing/alias-local.txt index c1bbc890..9b481a98 100644 --- a/test/test-files/email-templates/existing-alias-local.txt +++ b/test/files/emails/existing/alias-local.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: alias2@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local Alias Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-alias-local.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-alias-recipient-delimiter.txt b/test/files/emails/existing/alias-recipient-delimiter.txt similarity index 70% rename from test/test-files/email-templates/existing-alias-recipient-delimiter.txt rename to test/files/emails/existing/alias-recipient-delimiter.txt index 47b01397..07cb8d40 100644 --- a/test/test-files/email-templates/existing-alias-recipient-delimiter.txt +++ b/test/files/emails/existing/alias-recipient-delimiter.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: alias1~test@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local Alias With Delimiter Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-alias-recipient-delimiter.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-catchall-local.txt b/test/files/emails/existing/catchall-local.txt similarity index 68% rename from test/test-files/email-templates/existing-catchall-local.txt rename to test/files/emails/existing/catchall-local.txt index c80db170..ab3e1988 100644 --- a/test/test-files/email-templates/existing-catchall-local.txt +++ b/test/files/emails/existing/catchall-local.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: wildcard@localdomain2.com -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-catchall-local.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-regexp-alias-external.txt b/test/files/emails/existing/regexp-alias-external.txt similarity index 68% rename from test/test-files/email-templates/existing-regexp-alias-external.txt rename to test/files/emails/existing/regexp-alias-external.txt index 0e214db4..b50ac90f 100644 --- a/test/test-files/email-templates/existing-regexp-alias-external.txt +++ b/test/files/emails/existing/regexp-alias-external.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: bounce-always@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-regexp-alias-external.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-regexp-alias-local.txt b/test/files/emails/existing/regexp-alias-local.txt similarity index 68% rename from test/test-files/email-templates/existing-regexp-alias-local.txt rename to test/files/emails/existing/regexp-alias-local.txt index 6af46e92..e45b7c6c 100644 --- a/test/test-files/email-templates/existing-regexp-alias-local.txt +++ b/test/files/emails/existing/regexp-alias-local.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: test123@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-regexp-alias-local.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-user-and-cc-local-alias.txt b/test/files/emails/existing/user-and-cc-local-alias.txt similarity index 73% rename from test/test-files/email-templates/existing-user-and-cc-local-alias.txt rename to test/files/emails/existing/user-and-cc-local-alias.txt index 5fcb333b..37814f91 100644 --- a/test/test-files/email-templates/existing-user-and-cc-local-alias.txt +++ b/test/files/emails/existing/user-and-cc-local-alias.txt @@ -1,13 +1,6 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Cc: Existing Local Alias Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-user-and-cc-local-alias.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-user1.txt b/test/files/emails/existing/user1.txt similarity index 67% rename from test/test-files/email-templates/existing-user1.txt rename to test/files/emails/existing/user1.txt index 5ab0333f..23d49dc9 100644 --- a/test/test-files/email-templates/existing-user1.txt +++ b/test/files/emails/existing/user1.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message existing-user1.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/dsn-authenticated.txt b/test/files/emails/nc_raw/dsn/authenticated.txt similarity index 100% rename from test/test-files/email-templates/dsn-authenticated.txt rename to test/files/emails/nc_raw/dsn/authenticated.txt diff --git a/test/test-files/email-templates/dsn-unauthenticated.txt b/test/files/emails/nc_raw/dsn/unauthenticated.txt similarity index 100% rename from test/test-files/email-templates/dsn-unauthenticated.txt rename to test/files/emails/nc_raw/dsn/unauthenticated.txt diff --git a/test/test-files/email-templates/postscreen.txt b/test/files/emails/nc_raw/postscreen.txt similarity index 100% rename from test/test-files/email-templates/postscreen.txt rename to test/files/emails/nc_raw/postscreen.txt diff --git a/test/test-files/email-templates/smtp-only.txt b/test/files/emails/nc_raw/smtp-only.txt similarity index 100% rename from test/test-files/email-templates/smtp-only.txt rename to test/files/emails/nc_raw/smtp-only.txt diff --git a/test/test-files/email-templates/non-existing-user.txt b/test/files/emails/non-existing-user.txt similarity index 67% rename from test/test-files/email-templates/non-existing-user.txt rename to test/files/emails/non-existing-user.txt index 406f6755..3d92470e 100644 --- a/test/test-files/email-templates/non-existing-user.txt +++ b/test/files/emails/non-existing-user.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: nouser@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message non-existing-user.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/postgrey.txt b/test/files/emails/postgrey.txt similarity index 66% rename from test/test-files/email-templates/postgrey.txt rename to test/files/emails/postgrey.txt index 33a3b153..cdfe8f93 100644 --- a/test/test-files/email-templates/postgrey.txt +++ b/test/files/emails/postgrey.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Postgrey Test Message This is a test mail. - -. -QUIT diff --git a/test/files/emails/postscreen.txt b/test/files/emails/postscreen.txt new file mode 100644 index 00000000..732ac897 --- /dev/null +++ b/test/files/emails/postscreen.txt @@ -0,0 +1,5 @@ +From: Docker Mail Server +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message postscreen.txt +This is a test mail for postscreen. diff --git a/test/test-files/email-templates/send-privacy-email.txt b/test/files/emails/privacy.txt similarity index 61% rename from test/test-files/email-templates/send-privacy-email.txt rename to test/files/emails/privacy.txt index 0c51ec5b..1d3a1b96 100644 --- a/test/test-files/email-templates/send-privacy-email.txt +++ b/test/files/emails/privacy.txt @@ -1,15 +1,6 @@ -EHLO mail -AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu -bXlwYXNzd29yZA== -mail from: -rcpt to: -data From: Some User To: Some User User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Thunderbird/52.2.1 Subject: Test ESMTP Auth LOGIN and remove privacy This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/quota-exceeded.txt b/test/files/emails/quota-exceeded.txt similarity index 98% rename from test/test-files/email-templates/quota-exceeded.txt rename to test/files/emails/quota-exceeded.txt index 71d221a1..c5281637 100644 --- a/test/test-files/email-templates/quota-exceeded.txt +++ b/test/files/emails/quota-exceeded.txt @@ -1,7 +1,3 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: quotauser@otherdomain.tld -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 @@ -20,6 +16,3 @@ Et voluptatum nobis ut odio voluptatem et quibusdam fugit ut libero sapiente vel Sit sint obcaecati et reiciendis tenetur aut dolorum culpa. Ab veritatis maxime qui necessitatibus facilis eum voluptate asperiores non totam omnis. Nam modi officia in reiciendis odit sit rerum laudantium est rerum voluptatem ut fugit cupiditate! Sit atque sint aut delectus omnis ut asperiores enim quo reprehenderit quae! In quasi nemo ut error totam ut quia harum ut commodi tenetur? Non quod dolorum eum explicabo labore vel asperiores quas est perferendis nulla eum nemo tenetur. Ut libero blanditiis ex voluptatibus repudiandae ab reiciendis nemo id debitis impedit hic quia incidunt sed quam excepturi ut magnam odit. Qui dolor deleniti aut sunt voluptas aut blanditiis distinctio nam omnis deleniti hic omnis rerum eum magni voluptatem. Nam labore facere eum molestiae dolorum ea consectetur praesentium ut cupiditate iste ad magnam aut neque maiores! Et excepturi ducimus ut nemo voluptas eum voluptas nihil hic perferendis quos vel quasi nesciunt est praesentium dolore hic quia quis. Et maxime ducimus ea cupiditate voluptatem ad quia dolores! Sed quos quaerat vel aperiam minus non sapiente quia ut ratione dolore eum officiis rerum. Non dolor vitae qui facilis dignissimos aut voluptate odit et ullam consequuntur. Et laudantium perspiciatis sit nisi temporibus a temporibus itaque ut iure dolor a voluptatum mollitia eos officia nobis et quibusdam voluptas. Amet eligendi eos nulla corporis et blanditiis nihil vel eveniet veritatis et sunt perferendis id molestiae eius! Quo harum quod aut nemo autem ut adipisci sint sed quia sunt. Aut voluptas error ut quae perferendis eos adipisci internos. Nam rerum fugiat aut minima nostrum quo repellendus quas exercitationem tenetur. Et molestiae architecto id quibusdam reprehenderit et magnam aliquam! Quo tempora veritatis At dolorem sint ex nulla blanditiis At voluptas laudantium est molestiae exercitationem et sequi voluptates aut ipsa atque. Et animi ipsum aut atque recusandae ea nemo ullam non quisquam quos sit libero sint vel libero delectus. Eos labore quidem a velit obcaecati nam explicabo consequatur eos maxime blanditiis? Et ipsam molestiae non quia explicabo ex galisum repudiandae et tempora veniam. Sed optio repellendus ut consequatur temporibus et harum quas hic ipsa officia? Aut dolores ipsum sit nulla dignissimos id quia perferendis aut dolores dolor et quibusdam porro aut Quis consequatur. - -. -QUIT diff --git a/test/test-files/email-templates/rspamd-pass.txt b/test/files/emails/rspamd/pass.txt similarity index 57% rename from test/test-files/email-templates/rspamd-pass.txt rename to test/files/emails/rspamd/pass.txt index 0f244740..ce9286b1 100644 --- a/test/test-files/email-templates/rspamd-pass.txt +++ b/test/files/emails/rspamd/pass.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: pass@example.test -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message rspamd-pass.txt +Subject: Test Message rspamd/pass.txt This mail should pass and Rspamd should not mark it. - -. -QUIT diff --git a/test/test-files/email-templates/rspamd-spam-header.txt b/test/files/emails/rspamd/spam-header.txt similarity index 70% rename from test/test-files/email-templates/rspamd-spam-header.txt rename to test/files/emails/rspamd/spam-header.txt index 7be1a56d..8722e42f 100644 --- a/test/test-files/email-templates/rspamd-spam-header.txt +++ b/test/files/emails/rspamd/spam-header.txt @@ -1,12 +1,5 @@ -HELO mail.example.test -MAIL FROM: spam-header@example.test -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 21 Jan 2023 11:11:11 +0000 Subject: Test Message rspamd-spam-header.txt YJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X - -. -QUIT diff --git a/test/test-files/email-templates/rspamd-spam.txt b/test/files/emails/rspamd/spam.txt similarity index 70% rename from test/test-files/email-templates/rspamd-spam.txt rename to test/files/emails/rspamd/spam.txt index 88bd719c..c561e779 100644 --- a/test/test-files/email-templates/rspamd-spam.txt +++ b/test/files/emails/rspamd/spam.txt @@ -1,12 +1,5 @@ -HELO mail.example.test -MAIL FROM: spam@example.test -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 21 Jan 2023 11:11:11 +0000 Subject: Test Message rspamd-spam.txt XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X - -. -QUIT diff --git a/test/test-files/email-templates/rspamd-virus.txt b/test/files/emails/rspamd/virus.txt similarity index 70% rename from test/test-files/email-templates/rspamd-virus.txt rename to test/files/emails/rspamd/virus.txt index c745f261..cb18927d 100644 --- a/test/test-files/email-templates/rspamd-virus.txt +++ b/test/files/emails/rspamd/virus.txt @@ -1,12 +1,5 @@ -HELO mail.example.test -MAIL FROM: virus@example.test -RCPT TO: user1@localhost.localdomain -DATA From: Docker Mail Server To: Existing Local User Date: Sat, 21 Jan 2023 11:11:11 +0000 Subject: Test Message rspamd-virus.txt X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* - -. -QUIT diff --git a/test/test-files/email-templates/root-email.txt b/test/files/emails/sendmail/root-email.txt similarity index 100% rename from test/test-files/email-templates/root-email.txt rename to test/files/emails/sendmail/root-email.txt diff --git a/test/test-files/email-templates/sieve-pipe.txt b/test/files/emails/sieve/pipe.txt similarity index 67% rename from test/test-files/email-templates/sieve-pipe.txt rename to test/files/emails/sieve/pipe.txt index f13dba87..4e8cfb39 100644 --- a/test/test-files/email-templates/sieve-pipe.txt +++ b/test/files/emails/sieve/pipe.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user2@otherdomain.tld -DATA From: Sieve-pipe-test To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Sieve pipe test message This is a test mail to sieve pipe. - -. -QUIT diff --git a/test/test-files/email-templates/sieve-spam-folder.txt b/test/files/emails/sieve/spam-folder.txt similarity index 64% rename from test/test-files/email-templates/sieve-spam-folder.txt rename to test/files/emails/sieve/spam-folder.txt index 8e802817..7ffd09a7 100644 --- a/test/test-files/email-templates/sieve-spam-folder.txt +++ b/test/files/emails/sieve/spam-folder.txt @@ -1,12 +1,5 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user1@localhost.localdomain -DATA From: Spambot To: Existing Local User Date: Sat, 22 May 2010 07:43:25 -0400 Subject: Test Message sieve-spam-folder.txt This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/test-email.txt b/test/files/emails/test-email.txt similarity index 100% rename from test/test-files/email-templates/test-email.txt rename to test/files/emails/test-email.txt diff --git a/test/test-files/nc_templates/imap_special_use_folders.txt b/test/files/nc/imap_special_use_folders.txt similarity index 100% rename from test/test-files/nc_templates/imap_special_use_folders.txt rename to test/files/nc/imap_special_use_folders.txt diff --git a/test/test-files/nc_templates/postgrey_whitelist.txt b/test/files/nc/postgrey_whitelist.txt similarity index 100% rename from test/test-files/nc_templates/postgrey_whitelist.txt rename to test/files/nc/postgrey_whitelist.txt diff --git a/test/test-files/nc_templates/postgrey_whitelist_recipients.txt b/test/files/nc/postgrey_whitelist_recipients.txt similarity index 100% rename from test/test-files/nc_templates/postgrey_whitelist_recipients.txt rename to test/files/nc/postgrey_whitelist_recipients.txt diff --git a/test/test-files/nc_templates/rspamd_imap_move_to_inbox.txt b/test/files/nc/rspamd_imap_move_to_inbox.txt similarity index 100% rename from test/test-files/nc_templates/rspamd_imap_move_to_inbox.txt rename to test/files/nc/rspamd_imap_move_to_inbox.txt diff --git a/test/test-files/nc_templates/rspamd_imap_move_to_junk.txt b/test/files/nc/rspamd_imap_move_to_junk.txt similarity index 100% rename from test/test-files/nc_templates/rspamd_imap_move_to_junk.txt rename to test/files/nc/rspamd_imap_move_to_junk.txt diff --git a/test/test-files/ssl/custom-dhe-params.pem b/test/files/ssl/custom-dhe-params.pem similarity index 100% rename from test/test-files/ssl/custom-dhe-params.pem rename to test/files/ssl/custom-dhe-params.pem diff --git a/test/test-files/ssl/example.test/README.md b/test/files/ssl/example.test/README.md similarity index 100% rename from test/test-files/ssl/example.test/README.md rename to test/files/ssl/example.test/README.md diff --git a/test/test-files/ssl/example.test/cert.ecdsa.pem b/test/files/ssl/example.test/cert.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/cert.ecdsa.pem rename to test/files/ssl/example.test/cert.ecdsa.pem diff --git a/test/test-files/ssl/example.test/cert.rsa.pem b/test/files/ssl/example.test/cert.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/cert.rsa.pem rename to test/files/ssl/example.test/cert.rsa.pem diff --git a/test/test-files/ssl/example.test/key.ecdsa.pem b/test/files/ssl/example.test/key.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/key.ecdsa.pem rename to test/files/ssl/example.test/key.ecdsa.pem diff --git a/test/test-files/ssl/example.test/key.rsa.pem b/test/files/ssl/example.test/key.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/key.rsa.pem rename to test/files/ssl/example.test/key.rsa.pem diff --git a/test/test-files/ssl/example.test/testssl.txt b/test/files/ssl/example.test/testssl.txt similarity index 100% rename from test/test-files/ssl/example.test/testssl.txt rename to test/files/ssl/example.test/testssl.txt diff --git a/test/test-files/ssl/example.test/traefik.md b/test/files/ssl/example.test/traefik.md similarity index 100% rename from test/test-files/ssl/example.test/traefik.md rename to test/files/ssl/example.test/traefik.md diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/ca-key.ecdsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/ca-key.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/ca-key.ecdsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/ca-key.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/cert.ecdsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/cert.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/cert.ecdsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/cert.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/cert.rsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/cert.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/cert.rsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/cert.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/ecdsa.acme.json b/test/files/ssl/example.test/with_ca/ecdsa/ecdsa.acme.json similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/ecdsa.acme.json rename to test/files/ssl/example.test/with_ca/ecdsa/ecdsa.acme.json diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/key.ecdsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/key.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/key.ecdsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/key.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/key.rsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/key.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/key.rsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/key.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/rsa.acme.json b/test/files/ssl/example.test/with_ca/ecdsa/rsa.acme.json similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/rsa.acme.json rename to test/files/ssl/example.test/with_ca/ecdsa/rsa.acme.json diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/wildcard/cert.ecdsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/wildcard/cert.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/wildcard/cert.ecdsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/wildcard/cert.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/wildcard/ecdsa.acme.json b/test/files/ssl/example.test/with_ca/ecdsa/wildcard/ecdsa.acme.json similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/wildcard/ecdsa.acme.json rename to test/files/ssl/example.test/with_ca/ecdsa/wildcard/ecdsa.acme.json diff --git a/test/test-files/ssl/example.test/with_ca/ecdsa/wildcard/key.ecdsa.pem b/test/files/ssl/example.test/with_ca/ecdsa/wildcard/key.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/ecdsa/wildcard/key.ecdsa.pem rename to test/files/ssl/example.test/with_ca/ecdsa/wildcard/key.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/ca-cert.rsa.pem b/test/files/ssl/example.test/with_ca/rsa/ca-cert.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/ca-cert.rsa.pem rename to test/files/ssl/example.test/with_ca/rsa/ca-cert.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/ca-key.rsa.pem b/test/files/ssl/example.test/with_ca/rsa/ca-key.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/ca-key.rsa.pem rename to test/files/ssl/example.test/with_ca/rsa/ca-key.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/cert.ecdsa.pem b/test/files/ssl/example.test/with_ca/rsa/cert.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/cert.ecdsa.pem rename to test/files/ssl/example.test/with_ca/rsa/cert.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/cert.rsa.pem b/test/files/ssl/example.test/with_ca/rsa/cert.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/cert.rsa.pem rename to test/files/ssl/example.test/with_ca/rsa/cert.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/ecdsa.acme.json b/test/files/ssl/example.test/with_ca/rsa/ecdsa.acme.json similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/ecdsa.acme.json rename to test/files/ssl/example.test/with_ca/rsa/ecdsa.acme.json diff --git a/test/test-files/ssl/example.test/with_ca/rsa/key.ecdsa.pem b/test/files/ssl/example.test/with_ca/rsa/key.ecdsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/key.ecdsa.pem rename to test/files/ssl/example.test/with_ca/rsa/key.ecdsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/key.rsa.pem b/test/files/ssl/example.test/with_ca/rsa/key.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/key.rsa.pem rename to test/files/ssl/example.test/with_ca/rsa/key.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/rsa.acme.json b/test/files/ssl/example.test/with_ca/rsa/rsa.acme.json similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/rsa.acme.json rename to test/files/ssl/example.test/with_ca/rsa/rsa.acme.json diff --git a/test/test-files/ssl/example.test/with_ca/rsa/wildcard/cert.rsa.pem b/test/files/ssl/example.test/with_ca/rsa/wildcard/cert.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/wildcard/cert.rsa.pem rename to test/files/ssl/example.test/with_ca/rsa/wildcard/cert.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/wildcard/key.rsa.pem b/test/files/ssl/example.test/with_ca/rsa/wildcard/key.rsa.pem similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/wildcard/key.rsa.pem rename to test/files/ssl/example.test/with_ca/rsa/wildcard/key.rsa.pem diff --git a/test/test-files/ssl/example.test/with_ca/rsa/wildcard/rsa.acme.json b/test/files/ssl/example.test/with_ca/rsa/wildcard/rsa.acme.json similarity index 100% rename from test/test-files/ssl/example.test/with_ca/rsa/wildcard/rsa.acme.json rename to test/files/ssl/example.test/with_ca/rsa/wildcard/rsa.acme.json diff --git a/test/helper/common.bash b/test/helper/common.bash index 8fb7854e..ab21ef60 100644 --- a/test/helper/common.bash +++ b/test/helper/common.bash @@ -469,5 +469,19 @@ function _print_mail_log_for_id() { _run_in_container grep -F "${MAIL_ID}" /var/log/mail.log } +# A simple wrapper for netcat (`nc`). This is useful when sending +# "raw" e-mails or doing IMAP-related work. +# +# @param ${1} = the file that is given to `nc` +# @param ${1} = custom parameters for `nc` [OPTIONAL] (default: 0.0.0.0 25) +function _nc_wrapper() { + local FILE=${1:?Must provide name of template file} + local NC_PARAMETERS=${2:-0.0.0.0 25} + + [[ -v CONTAINER_NAME ]] || return 1 + + _run_in_container_bash "nc ${NC_PARAMETERS} < /tmp/docker-mailserver-test/${FILE}.txt" +} + # ? << Miscellaneous helper functions # ! ------------------------------------------------------------------- diff --git a/test/helper/sending.bash b/test/helper/sending.bash index 631617a1..48012178 100644 --- a/test/helper/sending.bash +++ b/test/helper/sending.bash @@ -8,11 +8,12 @@ # ! ATTENTION: This file requires helper functions from `common.sh`! # Sends a mail from localhost (127.0.0.1) to a container. To send -# a custom email, create a file at `test/test-files/`, +# a custom email, create a file at `test/files/`, # and provide `` as an argument to this function. # -# @param ${1} = template file (path) name -# @param ${2} = parameters for `nc` [OPTIONAL] (default: `0.0.0.0 25`) +# Parameters include all options that one can supply to `swaks` +# itself. The `--data` parameter expects a relative path from `emails/` +# where the contents will be implicitly provided to `swaks` via STDIN. # # ## Attention # @@ -23,17 +24,42 @@ # send the email but it will not make sure the mail queue is empty after the mail # has been sent. function _send_email() { - local TEMPLATE_FILE=${1:?Must provide name of template file} - local NC_PARAMETERS=${2:-0.0.0.0 25} + [[ -v CONTAINER_NAME ]] || return 1 - assert_not_equal "${NC_PARAMETERS}" '' - assert_not_equal "${CONTAINER_NAME:-}" '' + # Parameter defaults common to our testing needs: + local EHLO='mail.external.tld' + local FROM='user@external.tld' + local TO='user1@localhost.localdomain' + local SERVER='0.0.0.0' + local PORT=25 + # Extra options for `swaks` that aren't covered by the default options above: + local ADDITIONAL_SWAKS_OPTIONS=() + # Specifically for handling `--data` option below: + local FINAL_SWAKS_OPTIONS=() - _run_in_container_bash "nc ${NC_PARAMETERS} < /tmp/docker-mailserver-test/${TEMPLATE_FILE}.txt" - assert_success + while [[ ${#} -gt 0 ]]; do + case "${1}" in + ( '--ehlo' ) EHLO=${2:?--ehlo given but no argument} ; shift 2 ;; + ( '--from' ) FROM=${2:?--from given but no argument} ; shift 2 ;; + ( '--to' ) TO=${2:?--to given but no argument} ; shift 2 ;; + ( '--server' ) SERVER=${2:?--server given but no argument} ; shift 2 ;; + ( '--port' ) PORT=${2:?--port given but no argument} ; shift 2 ;; + ( '--data' ) + local TEMPLATE_FILE="/tmp/docker-mailserver-test/emails/${2:?--data given but no argument provided}.txt" + FINAL_SWAKS_OPTIONS+=('--data') + FINAL_SWAKS_OPTIONS+=('-') + FINAL_SWAKS_OPTIONS+=('<') + FINAL_SWAKS_OPTIONS+=("${TEMPLATE_FILE}") + shift 2 + ;; + ( * ) ADDITIONAL_SWAKS_OPTIONS+=("${1}") ; shift 1 ;; + esac + done + + _run_in_container_bash "swaks --server ${SERVER} --port ${PORT} --ehlo ${EHLO} --from ${FROM} --to ${TO} ${ADDITIONAL_SWAKS_OPTIONS[*]} ${FINAL_SWAKS_OPTIONS[*]}" } -# Like `_send_mail` with two major differences: +# Like `_send_email` with two major differences: # # 1. this function waits for the mail to be processed; there is no asynchronicity # because filtering the logs in a synchronous way is easier and safer! @@ -42,8 +68,7 @@ function _send_email() { # No. 2 is especially useful in case you send more than one email in a single # test file and need to assert certain log entries for each mail individually. # -# @param ${1} = template file (path) name -# @param ${2} = parameters for `nc` [OPTIONAL] (default: `0.0.0.0 25`) +# This function takes the same arguments as `_send_mail`. # # ## Attention # @@ -57,17 +82,13 @@ function _send_email() { # chosen. Sending more than one mail at any given point in time with this function # is UNDEFINED BEHAVIOR! function _send_email_and_get_id() { - local TEMPLATE_FILE=${1:?Must provide name of template file} - local NC_PARAMETERS=${2:-0.0.0.0 25} + [[ -v CONTAINER_NAME ]] || return 1 + + _wait_for_empty_mail_queue_in_container + _send_email "${@}" + _wait_for_empty_mail_queue_in_container + local MAIL_ID - - assert_not_equal "${NC_PARAMETERS}" '' - assert_not_equal "${CONTAINER_NAME:-}" '' - - _wait_for_empty_mail_queue_in_container - _send_email "${TEMPLATE_FILE}" - _wait_for_empty_mail_queue_in_container - # The unique ID Postfix (and other services) use may be different in length # on different systems (e.g. amd64 (11) vs aarch64 (10)). Hence, we use a # range to safely capture it. diff --git a/test/helper/setup.bash b/test/helper/setup.bash index 65e2999f..0dd57bd6 100644 --- a/test/helper/setup.bash +++ b/test/helper/setup.bash @@ -98,7 +98,7 @@ function _init_with_defaults() { # Common complimentary test files, read-only safe to share across containers: export TEST_FILES_CONTAINER_PATH='/tmp/docker-mailserver-test' - export TEST_FILES_VOLUME="${REPOSITORY_ROOT}/test/test-files:${TEST_FILES_CONTAINER_PATH}:ro" + export TEST_FILES_VOLUME="${REPOSITORY_ROOT}/test/files:${TEST_FILES_CONTAINER_PATH}:ro" # The config volume cannot be read-only as some data needs to be written at container startup # diff --git a/test/test-files/auth/added-smtp-auth-login-wrong.txt b/test/test-files/auth/added-smtp-auth-login-wrong.txt deleted file mode 100644 index a75856f1..00000000 --- a/test/test-files/auth/added-smtp-auth-login-wrong.txt +++ /dev/null @@ -1,4 +0,0 @@ -EHLO mail -AUTH LOGIN YWRkZWRAbG9jYWxob3N0LmxvY2FsZG9tYWlu -Bn3JKisq4HQ2RO== -QUIT diff --git a/test/test-files/auth/added-smtp-auth-login.txt b/test/test-files/auth/added-smtp-auth-login.txt deleted file mode 100644 index 5276b7f4..00000000 --- a/test/test-files/auth/added-smtp-auth-login.txt +++ /dev/null @@ -1,4 +0,0 @@ -EHLO mail -AUTH LOGIN YWRkZWRAbG9jYWxob3N0LmxvY2FsZG9tYWlu -bXlwYXNzd29yZA== -QUIT diff --git a/test/test-files/auth/added-smtp-auth-plain-wrong.txt b/test/test-files/auth/added-smtp-auth-plain-wrong.txt deleted file mode 100644 index 6ce5a383..00000000 --- a/test/test-files/auth/added-smtp-auth-plain-wrong.txt +++ /dev/null @@ -1,3 +0,0 @@ -EHLO mail -AUTH PLAIN YWRkZWRAbG9jYWxob3N0LmxvY2FsZG9tYWluAGFkZGVkQGxvY2FsaG9zdC5sb2NhbGRvbWFpbgBCQURQQVNTV09SRA== -QUIT diff --git a/test/test-files/auth/added-smtp-auth-plain.txt b/test/test-files/auth/added-smtp-auth-plain.txt deleted file mode 100644 index ed48d77d..00000000 --- a/test/test-files/auth/added-smtp-auth-plain.txt +++ /dev/null @@ -1,3 +0,0 @@ -EHLO mail -AUTH PLAIN YWRkZWRAbG9jYWxob3N0LmxvY2FsZG9tYWluAGFkZGVkQGxvY2FsaG9zdC5sb2NhbGRvbWFpbgBteXBhc3N3b3Jk -QUIT diff --git a/test/test-files/auth/sasl-ldap-smtp-auth.txt b/test/test-files/auth/sasl-ldap-smtp-auth.txt deleted file mode 100644 index df4d7db4..00000000 --- a/test/test-files/auth/sasl-ldap-smtp-auth.txt +++ /dev/null @@ -1,5 +0,0 @@ -EHLO mail -AUTH LOGIN -c29tZS51c2VyQGxvY2FsaG9zdC5sb2NhbGRvbWFpbg== -c2VjcmV0 -QUIT diff --git a/test/test-files/auth/smtp-auth-login-wrong.txt b/test/test-files/auth/smtp-auth-login-wrong.txt deleted file mode 100644 index 39b4f01c..00000000 --- a/test/test-files/auth/smtp-auth-login-wrong.txt +++ /dev/null @@ -1,4 +0,0 @@ -EHLO mail -AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu -Bn3JKisq4HQ2RO== -QUIT diff --git a/test/test-files/auth/smtp-auth-login.txt b/test/test-files/auth/smtp-auth-login.txt deleted file mode 100644 index 50ff99f3..00000000 --- a/test/test-files/auth/smtp-auth-login.txt +++ /dev/null @@ -1,4 +0,0 @@ -EHLO mail -AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu -bXlwYXNzd29yZA== -QUIT diff --git a/test/test-files/auth/smtp-auth-plain-wrong.txt b/test/test-files/auth/smtp-auth-plain-wrong.txt deleted file mode 100644 index d8d8ad2a..00000000 --- a/test/test-files/auth/smtp-auth-plain-wrong.txt +++ /dev/null @@ -1,3 +0,0 @@ -EHLO mail -AUTH PLAIN WRONGPASSWORD -QUIT diff --git a/test/test-files/auth/smtp-auth-plain.txt b/test/test-files/auth/smtp-auth-plain.txt deleted file mode 100644 index 2e60fdc3..00000000 --- a/test/test-files/auth/smtp-auth-plain.txt +++ /dev/null @@ -1,3 +0,0 @@ -EHLO mail -AUTH PLAIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWluAHVzZXIxQGxvY2FsaG9zdC5sb2NhbGRvbWFpbgBteXBhc3N3b3Jk -QUIT diff --git a/test/test-files/email-templates/existing-user2.txt b/test/test-files/email-templates/existing-user2.txt deleted file mode 100644 index 63554f27..00000000 --- a/test/test-files/email-templates/existing-user2.txt +++ /dev/null @@ -1,12 +0,0 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user2@otherdomain.tld -DATA -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-user2.txt -This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/existing-user3.txt b/test/test-files/email-templates/existing-user3.txt deleted file mode 100644 index facd5328..00000000 --- a/test/test-files/email-templates/existing-user3.txt +++ /dev/null @@ -1,12 +0,0 @@ -HELO mail.external.tld -MAIL FROM: user@external.tld -RCPT TO: user3@localhost.localdomain -DATA -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:33 -0400 -Subject: Test Message existing-user1.txt -This is a test mail. - -. -QUIT diff --git a/test/test-files/email-templates/smtp-ehlo.txt b/test/test-files/email-templates/smtp-ehlo.txt deleted file mode 100644 index 05524efd..00000000 --- a/test/test-files/email-templates/smtp-ehlo.txt +++ /dev/null @@ -1,2 +0,0 @@ -EHLO mail.localhost -QUIT diff --git a/test/tests/parallel/set1/dovecot/dovecot_quotas.bats b/test/tests/parallel/set1/dovecot/dovecot_quotas.bats index 2c176235..81cf9bc1 100644 --- a/test/tests/parallel/set1/dovecot/dovecot_quotas.bats +++ b/test/tests/parallel/set1/dovecot/dovecot_quotas.bats @@ -225,9 +225,12 @@ function teardown_file() { _default_teardown ; } sleep 10 # send some big emails - _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' - _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' - _send_email 'email-templates/quota-exceeded' '0.0.0.0 25' + _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded' + assert_success + _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded' + assert_success + _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded' + assert_success # check for quota warn message existence run _repeat_until_success_or_timeout 20 _exec_in_container grep -R 'Subject: quota warning' /var/mail/otherdomain.tld/quotauser/new/ assert_success diff --git a/test/tests/parallel/set1/dovecot/dovecot_sieve.bats b/test/tests/parallel/set1/dovecot/dovecot_sieve.bats index c2e9e6c7..e3e076a5 100644 --- a/test/tests/parallel/set1/dovecot/dovecot_sieve.bats +++ b/test/tests/parallel/set1/dovecot/dovecot_sieve.bats @@ -26,9 +26,11 @@ function setup_file() { _wait_for_smtp_port_in_container # Single mail sent from 'spam@spam.com' that is handled by User (relocate) and Global (copy) sieves for user1: - _send_email 'email-templates/sieve-spam-folder' + _send_email --data 'sieve/spam-folder' + assert_success # Mail for user2 triggers the sieve-pipe: - _send_email 'email-templates/sieve-pipe' + _send_email --to 'user2@otherdomain.tld' --data 'sieve/pipe' + assert_success _wait_for_empty_mail_queue_in_container } diff --git a/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats b/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats index 8ce03d9a..033a5bde 100644 --- a/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats +++ b/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats @@ -26,7 +26,8 @@ function teardown() { _default_teardown ; } _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' _wait_for_smtp_port_in_container - _send_email 'email-templates/existing-user1' + _send_email --data 'existing/user1' + assert_success _wait_for_empty_mail_queue_in_container # Mail received should be stored as `u.1` (one file per message) @@ -47,7 +48,8 @@ function teardown() { _default_teardown ; } _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' _wait_for_smtp_port_in_container - _send_email 'email-templates/existing-user1' + _send_email --data 'existing/user1' + assert_success _wait_for_empty_mail_queue_in_container # Mail received should be stored in `m.1` (1 or more messages) diff --git a/test/tests/parallel/set1/dovecot/special_use_folders.bats b/test/tests/parallel/set1/dovecot/special_use_folders.bats index e70899a0..fe1f554e 100644 --- a/test/tests/parallel/set1/dovecot/special_use_folders.bats +++ b/test/tests/parallel/set1/dovecot/special_use_folders.bats @@ -14,7 +14,8 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test 'normal delivery works' { - _send_email 'email-templates/existing-user1' + _send_email --data 'existing/user1' + assert_success _count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new 1 } @@ -26,7 +27,7 @@ function teardown_file() { _default_teardown ; } } @test "(IMAP) special-use folders should be created when necessary" { - _send_email 'nc_templates/imap_special_use_folders' '-w 8 0.0.0.0 143' + _nc_wrapper 'nc/imap_special_use_folders' '-w 8 0.0.0.0 143' assert_output --partial 'Drafts' assert_output --partial 'Junk' assert_output --partial 'Trash' diff --git a/test/tests/parallel/set1/spam_virus/clamav.bats b/test/tests/parallel/set1/spam_virus/clamav.bats index 31608ef8..9232f90f 100644 --- a/test/tests/parallel/set1/spam_virus/clamav.bats +++ b/test/tests/parallel/set1/spam_virus/clamav.bats @@ -25,34 +25,35 @@ function setup_file() { _wait_for_service postfix _wait_for_smtp_port_in_container - _send_email 'email-templates/amavis-virus' + _send_email --from 'virus@external.tld' --data 'amavis/virus' + assert_success _wait_for_empty_mail_queue_in_container } function teardown_file() { _default_teardown ; } -@test "log files exist at /var/log/mail directory" { +@test 'log files exist at /var/log/mail directory' { _run_in_container_bash "ls -1 /var/log/mail/ | grep -E 'clamav|freshclam|mail.log' | wc -l" assert_success assert_output 3 } -@test "should be identified by Amavis" { +@test 'should be identified by Amavis' { _run_in_container grep -i 'Found secondary av scanner ClamAV-clamscan' /var/log/mail/mail.log assert_success } -@test "freshclam cron is enabled" { +@test 'freshclam cron is enabled' { _run_in_container_bash "grep '/usr/bin/freshclam' -r /etc/cron.d" assert_success } -@test "env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly" { +@test 'env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly' { _run_in_container grep -q '^MaxFileSize 30M$' /etc/clamav/clamd.conf assert_success } -@test "rejects virus" { +@test 'rejects virus' { _run_in_container_bash "grep 'Blocked INFECTED' /var/log/mail/mail.log | grep ' -> '" assert_success } diff --git a/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats b/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats index 8402422c..f2474cc0 100644 --- a/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats +++ b/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats @@ -12,12 +12,14 @@ function setup_file() { --env ENABLE_CLAMAV=0 --env ENABLE_SPAMASSASSIN=0 --env AMAVIS_LOGLEVEL=2 + --env PERMIT_DOCKER=container ) _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' _wait_for_smtp_port_in_container - _send_email 'email-templates/existing-user1' + _send_email --data 'existing/user1' + assert_success _wait_for_empty_mail_queue_in_container } diff --git a/test/tests/parallel/set1/spam_virus/fail2ban.bats b/test/tests/parallel/set1/spam_virus/fail2ban.bats index 9ae30758..8a03ba04 100644 --- a/test/tests/parallel/set1/spam_virus/fail2ban.bats +++ b/test/tests/parallel/set1/spam_virus/fail2ban.bats @@ -73,8 +73,17 @@ function teardown_file() { @test "ban ip on multiple failed login" { CONTAINER1_IP=$(_get_container_ip "${CONTAINER1_NAME}") # Trigger a ban by failing to login twice: - CONTAINER_NAME=${CONTAINER2_NAME} _send_email 'auth/smtp-auth-login-wrong' "${CONTAINER1_IP} 465" - CONTAINER_NAME=${CONTAINER2_NAME} _send_email 'auth/smtp-auth-login-wrong' "${CONTAINER1_IP} 465" + for _ in {1..2}; do + CONTAINER_NAME=${CONTAINER2_NAME} _send_email \ + --server "${CONTAINER1_IP}" \ + --port 465 \ + --auth PLAIN \ + --auth-user user1@localhost.localdomain \ + --auth-password wrongpassword + assert_failure + assert_output --partial 'authentication failed' + assert_output --partial 'No authentication type succeeded' + done # Checking that CONTAINER2_IP is banned in "${CONTAINER1_NAME}" CONTAINER2_IP=$(_get_container_ip "${CONTAINER2_NAME}") diff --git a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats index e32210ca..316e3350 100644 --- a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats +++ b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats @@ -51,17 +51,15 @@ function teardown_file() { _default_teardown ; } _reload_postfix # Send test mail (it should fail to deliver): - _send_test_mail '/tmp/docker-mailserver-test/email-templates/postgrey.txt' '25' + _send_email --from 'user@external.tld' --port 25 --data 'postgrey' + assert_failure + assert_output --partial 'Recipient address rejected: Delayed by Postgrey' # Confirm mail was greylisted: _should_have_log_entry \ 'action=greylist' \ 'reason=new' \ 'client_address=127.0.0.1/32, sender=user@external.tld, recipient=user1@localhost.localdomain' - - _repeat_until_success_or_timeout 10 _run_in_container grep \ - 'Recipient address rejected: Delayed by Postgrey' \ - /var/log/mail/mail.log } # NOTE: This test case depends on the previous one @@ -69,7 +67,8 @@ function teardown_file() { _default_teardown ; } # Wait until `$POSTGREY_DELAY` seconds pass before trying again: sleep 3 # Retry delivering test mail (it should be trusted this time): - _send_test_mail '/tmp/docker-mailserver-test/email-templates/postgrey.txt' '25' + _send_email --from 'user@external.tld' --port 25 --data 'postgrey' + assert_success # Confirm postgrey permitted delivery (triplet is now trusted): _should_have_log_entry \ @@ -78,8 +77,9 @@ function teardown_file() { _default_teardown ; } 'client_address=127.0.0.1/32, sender=user@external.tld, recipient=user1@localhost.localdomain' } - -# NOTE: These two whitelist tests use `test-files/nc_templates/` instead of `test-files/email-templates`. +# NOTE: These two whitelist tests use `files/nc/` instead of `files/emails`. +# `nc` option `-w 0` terminates the connection after sending the template, it does not wait for a response. +# This is required for port 10023, otherwise the connection never drops. # - This allows to bypass the SMTP protocol on port 25, and send data directly to Postgrey instead. # - Appears to be a workaround due to `client_name=localhost` when sent from Postfix. # - Could send over port 25 if whitelisting `localhost`, @@ -87,7 +87,7 @@ function teardown_file() { _default_teardown ; } # - It'd also cause the earlier greylist test to fail. # - TODO: Actually confirm whitelist feature works correctly as these test cases are using a workaround: @test "should whitelist sender 'user@whitelist.tld'" { - _send_test_mail '/tmp/docker-mailserver-test/nc_templates/postgrey_whitelist.txt' '10023' + _nc_wrapper 'nc/postgrey_whitelist' '-w 0 0.0.0.0 10023' _should_have_log_entry \ 'action=pass' \ @@ -96,7 +96,7 @@ function teardown_file() { _default_teardown ; } } @test "should whitelist recipient 'user2@otherdomain.tld'" { - _send_test_mail '/tmp/docker-mailserver-test/nc_templates/postgrey_whitelist_recipients.txt' '10023' + _nc_wrapper 'nc/postgrey_whitelist_recipients' '-w 0 0.0.0.0 10023' _should_have_log_entry \ 'action=pass' \ @@ -104,21 +104,10 @@ function teardown_file() { _default_teardown ; } 'client_address=127.0.0.1/32, sender=test@nonwhitelist.tld, recipient=user2@otherdomain.tld' } -function _send_test_mail() { - local MAIL_TEMPLATE=$1 - local PORT=${2:-25} - - # `-w 0` terminates the connection after sending the template, it does not wait for a response. - # This is required for port 10023, otherwise the connection never drops. - # It could increase the number of seconds to wait for port 25 to allow for asserting a response, - # but that would enforce the delay in tests for port 10023. - _run_in_container_bash "nc -w 0 0.0.0.0 ${PORT} < ${MAIL_TEMPLATE}" -} - function _should_have_log_entry() { - local ACTION=$1 - local REASON=$2 - local TRIPLET=$3 + local ACTION=${1} + local REASON=${2} + local TRIPLET=${3} # Allow some extra time for logs to update to avoids a false-positive failure: _run_until_success_or_timeout 10 _exec_in_container grep \ diff --git a/test/tests/parallel/set1/spam_virus/postscreen.bats b/test/tests/parallel/set1/spam_virus/postscreen.bats index a1ddeb29..377b2479 100644 --- a/test/tests/parallel/set1/spam_virus/postscreen.bats +++ b/test/tests/parallel/set1/spam_virus/postscreen.bats @@ -37,46 +37,35 @@ function teardown_file() { docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}" } +# `POSTSCREEN_ACTION=enforce` (DMS default) should reject delivery with a 550 SMTP reply +# A legitimate mail client should speak SMTP by waiting it's turn, which postscreen defaults enforce (only on port 25) +# https://www.postfix.org/postconf.5.html#postscreen_greet_wait +# +# Use `nc` to send all SMTP commands at once instead (emulate a misbehaving client that should be rejected) +# NOTE: Postscreen only runs on port 25, avoid implicit ports in test methods @test 'should fail send when talking out of turn' { - CONTAINER_NAME=${CONTAINER2_NAME} _send_email 'email-templates/postscreen' "${CONTAINER1_IP} 25" + CONTAINER_NAME=${CONTAINER2_NAME} _nc_wrapper 'emails/nc_raw/postscreen' "${CONTAINER1_IP} 25" + # Expected postscreen log entry: assert_output --partial 'Protocol error' - # Expected postscreen log entry: - _run_in_container cat /var/log/mail/mail.log + _run_in_container cat /var/log/mail.log assert_output --partial 'COMMAND PIPELINING' + assert_output --partial 'DATA without valid RCPT' } @test "should successfully pass postscreen and get postfix greeting message (respecting postscreen_greet_wait time)" { - # NOTE: Sometimes fails on first attempt (trying too soon?), - # Instead of a `run` + asserting partial, Using repeat + internal grep match: - _repeat_until_success_or_timeout 10 _should_wait_turn_speaking_smtp \ - "${CONTAINER2_NAME}" \ - "${CONTAINER1_IP}" \ - '/tmp/docker-mailserver-test/email-templates/postscreen.txt' \ - '220 mail.example.test ESMTP' + # Configure `send_email()` to send from the mail client container (CONTAINER2_NAME) via ENV override, + # mail is sent to the DMS server container (CONTAINER1_NAME) via `--server` parameter: + CONTAINER_NAME=${CONTAINER2_NAME} _send_email --server "${CONTAINER1_IP}" --port 25 --data 'postscreen' + # NOTE: Cannot assert_success due to sender address not being resolvable. + # TODO: Uncomment when proper resolution of domain names is possible: + # assert_success - # Expected postscreen log entry: - _run_in_container cat /var/log/mail/mail.log + # TODO: Prefer this approach when `_send_email_and_get_id()` can support separate client and server containers: + # local MAIL_ID=$(_send_email_and_get_id --port 25 --data 'postscreen') + # _print_mail_log_for_id "${MAIL_ID}" + # assert_output --partial "stored mail into mailbox 'INBOX'" + + _run_in_container cat /var/log/mail.log assert_output --partial 'PASS NEW' } - -# When postscreen is active, it prevents the usual method of piping a file through nc: -# (Won't work: CONTAINER_NAME=${CLIENT_CONTAINER_NAME} _send_email "${SMTP_TEMPLATE}" "${TARGET_CONTAINER_IP} 25") -# The below workaround respects `postscreen_greet_wait` time (default 6 sec), talking to the mail-server in turn: -# https://www.postfix.org/postconf.5.html#postscreen_greet_wait -function _should_wait_turn_speaking_smtp() { - local CLIENT_CONTAINER_NAME=$1 - local TARGET_CONTAINER_IP=$2 - local SMTP_TEMPLATE=$3 - local EXPECTED=$4 - - # shellcheck disable=SC2016 - local UGLY_WORKAROUND='exec 3<>/dev/tcp/'"${TARGET_CONTAINER_IP}"'/25 && \ - while IFS= read -r cmd; do \ - head -1 <&3; \ - [[ ${cmd} == "EHLO"* ]] && sleep 6; \ - echo ${cmd} >&3; \ - done < '"${SMTP_TEMPLATE}" - - docker exec "${CLIENT_CONTAINER_NAME}" bash -c "${UGLY_WORKAROUND}" | grep "${EXPECTED}" -} diff --git a/test/tests/parallel/set1/spam_virus/rspamd_full.bats b/test/tests/parallel/set1/spam_virus/rspamd_full.bats index ba8a23f5..2e610d72 100644 --- a/test/tests/parallel/set1/spam_virus/rspamd_full.bats +++ b/test/tests/parallel/set1/spam_virus/rspamd_full.bats @@ -45,10 +45,10 @@ function setup_file() { # We will send 3 emails: the first one should pass just fine; the second one should # be rejected due to spam; the third one should be rejected due to a virus. - export MAIL_ID1=$(_send_email_and_get_id 'email-templates/rspamd-pass') - export MAIL_ID2=$(_send_email_and_get_id 'email-templates/rspamd-spam') - export MAIL_ID3=$(_send_email_and_get_id 'email-templates/rspamd-virus') - export MAIL_ID4=$(_send_email_and_get_id 'email-templates/rspamd-spam-header') + export MAIL_ID1=$(_send_email_and_get_id --from 'rspamd-pass@example.test' --data 'rspamd/pass') + export MAIL_ID2=$(_send_email_and_get_id --from 'rspamd-spam@example.test' --data 'rspamd/spam') + export MAIL_ID3=$(_send_email_and_get_id --from 'rspamd-virus@example.test' --data 'rspamd/virus') + export MAIL_ID4=$(_send_email_and_get_id --from 'rspamd-spam-header@example.test' --data 'rspamd/spam-header') for ID in MAIL_ID{1,2,3,4}; do [[ -n ${!ID} ]] || { echo "${ID} is empty - aborting!" ; return 1 ; } @@ -256,7 +256,7 @@ function teardown_file() { _default_teardown ; } # Move an email to the "Junk" folder from "INBOX"; the first email we # sent should pass fine, hence we can now move it. - _send_email 'nc_templates/rspamd_imap_move_to_junk' '0.0.0.0 143' + _nc_wrapper 'nc/rspamd_imap_move_to_junk' '0.0.0.0 143' sleep 1 # wait for the transaction to finish _run_in_container cat /var/log/mail/mail.log @@ -270,7 +270,7 @@ function teardown_file() { _default_teardown ; } # Move an email to the "INBOX" folder from "Junk"; there should be two mails # in the "Junk" folder, since the second email we sent during setup should # have landed in the Junk folder already. - _send_email 'nc_templates/rspamd_imap_move_to_inbox' '0.0.0.0 143' + _nc_wrapper 'nc/rspamd_imap_move_to_inbox' '0.0.0.0 143' sleep 1 # wait for the transaction to finish _run_in_container cat /var/log/mail/mail.log diff --git a/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats b/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats index 94a9b9c4..fea23b0b 100644 --- a/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats +++ b/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats @@ -95,7 +95,7 @@ function teardown() { _default_teardown ; } function _should_send_spam_message() { _wait_for_smtp_port_in_container _wait_for_tcp_port_in_container 10024 # port 10024 is for Amavis - _send_email 'email-templates/amavis-spam' + _send_email --from 'spam@external.tld' --data 'amavis/spam' } function _should_be_received_by_amavis() { diff --git a/test/tests/parallel/set1/tls/dhparams.bats b/test/tests/parallel/set1/tls/dhparams.bats index 3157034c..8b3047d1 100644 --- a/test/tests/parallel/set1/tls/dhparams.bats +++ b/test/tests/parallel/set1/tls/dhparams.bats @@ -38,7 +38,7 @@ function teardown() { _default_teardown ; } # - A warning is raised about usage of potentially insecure parameters. @test "Custom" { export CONTAINER_NAME=${CONTAINER2_NAME} - local DH_PARAMS_CUSTOM='test/test-files/ssl/custom-dhe-params.pem' + local DH_PARAMS_CUSTOM='test/files/ssl/custom-dhe-params.pem' local DH_CHECKSUM_CUSTOM=$(sha512sum "${DH_PARAMS_CUSTOM}" | awk '{print $1}') _init_with_defaults diff --git a/test/tests/parallel/set1/tls/letsencrypt.bats b/test/tests/parallel/set1/tls/letsencrypt.bats index 91a05997..bcdb1758 100644 --- a/test/tests/parallel/set1/tls/letsencrypt.bats +++ b/test/tests/parallel/set1/tls/letsencrypt.bats @@ -88,7 +88,7 @@ function _initial_setup() { # All of these certs support both FQDNs (`mail.example.test` and `example.test`), # Except for the wildcard cert (`*.example.test`), that was created with `example.test` intentionally excluded from SAN. # We want to maintain the same FQDN (`mail.example.test`) between the _acme_ecdsa and _acme_rsa tests. - local LOCAL_BASE_PATH="${PWD}/test/test-files/ssl/example.test/with_ca/rsa" + local LOCAL_BASE_PATH="${PWD}/test/files/ssl/example.test/with_ca/rsa" function _prepare() { # Default `acme.json` for _acme_ecdsa test: @@ -240,7 +240,7 @@ function _copy_to_letsencrypt_storage() { FQDN_DIR=$(echo "${DEST}" | cut -d '/' -f1) mkdir -p "${TEST_TMP_CONFIG}/letsencrypt/${FQDN_DIR}" - if ! cp "${PWD}/test/test-files/ssl/${SRC}" "${TEST_TMP_CONFIG}/letsencrypt/${DEST}"; then + if ! cp "${PWD}/test/files/ssl/${SRC}" "${TEST_TMP_CONFIG}/letsencrypt/${DEST}"; then echo "Could not copy cert file '${SRC}'' to '${DEST}'" >&2 exit 1 fi diff --git a/test/tests/parallel/set1/tls/manual.bats b/test/tests/parallel/set1/tls/manual.bats index 2a55f14f..c082d6ed 100644 --- a/test/tests/parallel/set1/tls/manual.bats +++ b/test/tests/parallel/set1/tls/manual.bats @@ -20,7 +20,7 @@ function setup_file() { export TEST_DOMAIN='example.test' local CUSTOM_SETUP_ARGUMENTS=( - --volume "${PWD}/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/:/config/ssl/:ro" + --volume "${PWD}/test/files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/:/config/ssl/:ro" --env LOG_LEVEL='trace' --env SSL_TYPE='manual' --env TLS_LEVEL='modern' @@ -108,10 +108,10 @@ function teardown_file() { _default_teardown ; } @test "manual cert changes are picked up by check-for-changes" { printf '%s' 'someThingsChangedHere' \ - >>"$(pwd)/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem" + >>"$(pwd)/test/files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem" run timeout 15 docker exec "${CONTAINER_NAME}" bash -c "tail -F /var/log/supervisor/changedetector.log | sed '/Manual certificates have changed/ q'" assert_success - sed -i '/someThingsChangedHere/d' "$(pwd)/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem" + sed -i '/someThingsChangedHere/d' "$(pwd)/test/files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem" } diff --git a/test/tests/parallel/set2/tls_cipherlists.bats b/test/tests/parallel/set2/tls_cipherlists.bats index 2b9511b9..3429f516 100644 --- a/test/tests/parallel/set2/tls_cipherlists.bats +++ b/test/tests/parallel/set2/tls_cipherlists.bats @@ -17,7 +17,7 @@ function setup_file() { # Contains various certs for testing TLS support (read-only): export TLS_CONFIG_VOLUME - TLS_CONFIG_VOLUME="${PWD}/test/test-files/ssl/${TEST_DOMAIN}/:/config/ssl/:ro" + TLS_CONFIG_VOLUME="${PWD}/test/files/ssl/${TEST_DOMAIN}/:/config/ssl/:ro" # Used for connecting testssl and DMS containers via network name `TEST_DOMAIN`: # NOTE: If the network already exists, the test will fail to start diff --git a/test/tests/parallel/set3/container_configuration/hostname.bats b/test/tests/parallel/set3/container_configuration/hostname.bats index fcb84b28..f5774eef 100644 --- a/test/tests/parallel/set3/container_configuration/hostname.bats +++ b/test/tests/parallel/set3/container_configuration/hostname.bats @@ -207,7 +207,7 @@ function _should_have_correct_mail_headers() { # (eg: OVERRIDE_HOSTNAME or `--hostname mail --domainname example.test`) local EXPECTED_HOSTNAME=${3:-${EXPECTED_FQDN}} - _send_email 'email-templates/existing-user1' + _send_email --from 'user@external.tld' --data 'existing/user1' _wait_for_empty_mail_queue_in_container _count_files_in_directory_in_container '/var/mail/localhost.localdomain/user1/new/' '1' diff --git a/test/tests/parallel/set3/mta/dsn.bats b/test/tests/parallel/set3/mta/dsn.bats index dcbb79b6..a5228cfc 100644 --- a/test/tests/parallel/set3/mta/dsn.bats +++ b/test/tests/parallel/set3/mta/dsn.bats @@ -47,9 +47,11 @@ function teardown_file() { @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' + # TODO replace with _send_email as soon as it supports DSN + # TODO ref: https://github.com/jetmore/swaks/issues/41 + _nc_wrapper 'emails/nc_raw/dsn/unauthenticated' + _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465' + _nc_wrapper 'emails/nc_raw/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 @@ -60,7 +62,7 @@ function teardown_file() { @test "should only send a DSN when requested from ports 465/587" { export CONTAINER_NAME=${CONTAINER2_NAME} - _send_email 'email-templates/dsn-unauthenticated' + _nc_wrapper 'emails/nc_raw/dsn/unauthenticated' _wait_for_empty_mail_queue_in_container # DSN requests can now only be made on ports 465 and 587, @@ -72,8 +74,8 @@ function teardown_file() { 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' + _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465' + _nc_wrapper 'emails/nc_raw/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 @@ -83,9 +85,9 @@ function teardown_file() { @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' + _nc_wrapper 'emails/nc_raw/dsn/unauthenticated' + _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465' + _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587' _wait_for_empty_mail_queue_in_container # DSN requests are rejected regardless of origin. diff --git a/test/tests/parallel/set3/mta/lmtp_ip.bats b/test/tests/parallel/set3/mta/lmtp_ip.bats index 8d35c062..d8be42d9 100644 --- a/test/tests/parallel/set3/mta/lmtp_ip.bats +++ b/test/tests/parallel/set3/mta/lmtp_ip.bats @@ -38,7 +38,7 @@ function teardown_file() { _default_teardown ; } @test "delivers mail to existing account" { _wait_for_smtp_port_in_container - _send_email 'email-templates/existing-user1' # send a test email + _send_email --data 'existing/user1' # send a test email # Verify delivery was successful, log line should look similar to: # postfix/lmtp[1274]: 0EA424ABE7D9: to=, relay=127.0.0.1[127.0.0.1]:24, delay=0.13, delays=0.07/0.01/0.01/0.05, dsn=2.0.0, status=sent (250 2.0.0 ixPpB+Zvv2P7BAAAUi6ngw Saved) diff --git a/test/tests/parallel/set3/mta/privacy.bats b/test/tests/parallel/set3/mta/privacy.bats index f8160827..4d4d82ba 100644 --- a/test/tests/parallel/set3/mta/privacy.bats +++ b/test/tests/parallel/set3/mta/privacy.bats @@ -25,7 +25,11 @@ function teardown_file() { _default_teardown ; } # this test covers https://github.com/docker-mailserver/docker-mailserver/issues/681 @test "(Postfix) remove privacy details of the sender" { - _run_in_container_bash "openssl s_client -quiet -starttls smtp -connect 0.0.0.0:587 < /tmp/docker-mailserver-test/email-templates/send-privacy-email.txt" + _send_email \ + --port 587 -tls --auth LOGIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --data 'privacy' assert_success _run_until_success_or_timeout 120 _exec_in_container_bash '[[ -d /var/mail/localhost.localdomain/user1/new ]]' diff --git a/test/tests/parallel/set3/mta/smtp_delivery.bats b/test/tests/parallel/set3/mta/smtp_delivery.bats index af98b2f4..169f374a 100644 --- a/test/tests/parallel/set3/mta/smtp_delivery.bats +++ b/test/tests/parallel/set3/mta/smtp_delivery.bats @@ -63,34 +63,55 @@ function setup_file() { # TODO: Move to clamav tests (For use when ClamAV is enabled): # _repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /var/run/clamav/clamd.ctl - # _send_email 'email-templates/amavis-virus' + # _send_email --from 'virus@external.tld' --data 'amavis/virus' # Required for 'delivers mail to existing alias': - _send_email 'email-templates/existing-alias-external' + _send_email --to alias1@localhost.localdomain --data 'existing/alias-external' # Required for 'delivers mail to existing alias with recipient delimiter': - _send_email 'email-templates/existing-alias-recipient-delimiter' + _send_email --to alias1~test@localhost.localdomain --data 'existing/alias-recipient-delimiter' # Required for 'delivers mail to existing catchall': - _send_email 'email-templates/existing-catchall-local' + _send_email --to wildcard@localdomain2.com --data 'existing/catchall-local' # Required for 'delivers mail to regexp alias': - _send_email 'email-templates/existing-regexp-alias-local' + _send_email --to test123@localhost.localdomain --data 'existing/regexp-alias-local' # Required for 'rejects mail to unknown user': - _send_email 'email-templates/non-existing-user' + _send_email --to nouser@localhost.localdomain --data 'non-existing-user' # Required for 'redirects mail to external aliases': - _send_email 'email-templates/existing-regexp-alias-external' - _send_email 'email-templates/existing-alias-local' + _send_email --to bounce-always@localhost.localdomain --data 'existing/regexp-alias-external' + _send_email --to alias2@localhost.localdomain --data 'existing/alias-local' # Required for 'rejects spam': - _send_email 'email-templates/amavis-spam' + _send_email --from 'spam@external.tld' --data 'amavis/spam' # Required for 'delivers mail to existing account': - _send_email 'email-templates/existing-user1' - _send_email 'email-templates/existing-user2' - _send_email 'email-templates/existing-user3' - _send_email 'email-templates/existing-added' - _send_email 'email-templates/existing-user-and-cc-local-alias' - _send_email 'email-templates/sieve-spam-folder' - _send_email 'email-templates/sieve-pipe' - _run_in_container_bash 'sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt' + _send_email --data 'existing/user1' + assert_success + _send_email --to user2@otherdomain.tld + assert_success + _send_email --to user3@localhost.localdomain + assert_success + _send_email --to added@localhost.localdomain --data 'existing/added' + assert_success + _send_email --to user1@localhost.localdomain --data 'existing/user-and-cc-local-alias' + assert_success + _send_email --data 'sieve/spam-folder' + assert_success + _send_email --to user2@otherdomain.tld --data 'sieve/pipe' + assert_success + _run_in_container_bash 'sendmail root < /tmp/docker-mailserver-test/emails/sendmail/root-email.txt' + assert_success +} + +function _unsuccessful() { + _send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password wrongpassword + assert_failure + assert_output --partial 'authentication failed' + assert_output --partial 'No authentication type succeeded' +} + +function _successful() { + _send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password mypassword --quit-after AUTH + assert_success + assert_output --partial 'Authentication successful' } @test "should succeed at emptying mail queue" { @@ -103,44 +124,35 @@ function setup_file() { } @test "should successfully authenticate with good password (plain)" { - _send_email 'auth/smtp-auth-plain' '-w 5 0.0.0.0 465' - assert_output --partial 'Authentication successful' + _successful PLAIN user1@localhost.localdomain } @test "should fail to authenticate with wrong password (plain)" { - _send_email 'auth/smtp-auth-plain-wrong' '-w 20 0.0.0.0 465' - assert_output --partial 'authentication failed' + _unsuccessful PLAIN user1@localhost.localdomain } @test "should successfully authenticate with good password (login)" { - _send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 465' - assert_output --partial 'Authentication successful' + _successful LOGIN user1@localhost.localdomain } @test "should fail to authenticate with wrong password (login)" { - _send_email 'auth/smtp-auth-login-wrong' '-w 20 0.0.0.0 465' - assert_output --partial 'authentication failed' + _unsuccessful LOGIN user1@localhost.localdomain } @test "[user: 'added'] should successfully authenticate with good password (plain)" { - _send_email 'auth/added-smtp-auth-plain' '-w 5 0.0.0.0 465' - assert_output --partial 'Authentication successful' + _successful PLAIN added@localhost.localdomain } @test "[user: 'added'] should fail to authenticate with wrong password (plain)" { - _send_email 'auth/added-smtp-auth-plain-wrong' '-w 20 0.0.0.0 465' - assert_output --partial 'authentication failed' + _unsuccessful PLAIN added@localhost.localdomain } @test "[user: 'added'] should successfully authenticate with good password (login)" { - _send_email 'auth/added-smtp-auth-login' '-w 5 0.0.0.0 465' - assert_success - assert_output --partial 'Authentication successful' + _successful LOGIN added@localhost.localdomain } @test "[user: 'added'] should fail to authenticate with wrong password (login)" { - _send_email 'auth/added-smtp-auth-login-wrong' '-w 20 0.0.0.0 465' - assert_output --partial 'authentication failed' + _unsuccessful LOGIN added@localhost.localdomain } # TODO: Add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) @@ -258,7 +270,13 @@ function setup_file() { # Dovecot does not support SMTPUTF8, so while we can send we cannot receive # Better disable SMTPUTF8 support entirely if we can't handle it correctly @test "not advertising smtputf8" { - _send_email 'email-templates/smtp-ehlo' + # Query supported extensions; SMTPUTF8 should not be available. + # - This query requires a EHLO greeting to the destination server. + _send_email \ + --ehlo mail.external.tld \ + --protocol ESMTP \ + --server mail.example.test \ + --quit-after FIRST-EHLO refute_output --partial 'SMTPUTF8' } diff --git a/test/tests/parallel/set3/mta/smtponly.bats b/test/tests/parallel/set3/mta/smtponly.bats index 66123de6..7b1f8699 100644 --- a/test/tests/parallel/set3/mta/smtponly.bats +++ b/test/tests/parallel/set3/mta/smtponly.bats @@ -32,7 +32,16 @@ function teardown_file() { _default_teardown ; } assert_success # it looks as if someone tries to send mail to another domain outside of DMS - _send_email 'email-templates/smtp-only' + _send_email \ + --ehlo mail.origin.test \ + --protocol SSMTPA \ + --server mail.origin.test \ + --from user@origin.test \ + --to user@destination.test \ + --auth PLAIN \ + --auth-user user@origin.test \ + --auth-password secret + assert_success _wait_for_empty_mail_queue_in_container # this seemingly succeeds, but looking at the logs, it doesn't diff --git a/test/tests/serial/mail_pop3.bats b/test/tests/serial/mail_pop3.bats index cb07484a..008921e4 100644 --- a/test/tests/serial/mail_pop3.bats +++ b/test/tests/serial/mail_pop3.bats @@ -24,11 +24,13 @@ function teardown_file() { _default_teardown ; } } @test 'authentication works' { - _send_email 'auth/pop3-auth' '-w 1 0.0.0.0 110' + _nc_wrapper 'auth/pop3-auth' '-w 1 0.0.0.0 110' + assert_success } @test 'added user authentication works' { - _send_email 'auth/added-pop3-auth' '-w 1 0.0.0.0 110' + _nc_wrapper 'auth/added-pop3-auth' '-w 1 0.0.0.0 110' + assert_success } @test '/var/log/mail/mail.log is error-free' { diff --git a/test/tests/serial/mail_with_imap.bats b/test/tests/serial/mail_with_imap.bats index d729c142..eeccf888 100644 --- a/test/tests/serial/mail_with_imap.bats +++ b/test/tests/serial/mail_with_imap.bats @@ -21,7 +21,8 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test '(Dovecot) LDAP RIMAP connection and authentication works' { - _send_email 'auth/imap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143' + assert_success } @test '(SASLauthd) SASL RIMAP authentication works' { @@ -30,13 +31,30 @@ function teardown_file() { _default_teardown ; } } @test '(SASLauthd) RIMAP SMTP authentication works' { - _send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 25' - assert_output --partial 'Error: authentication not enabled' + _send_email \ + --auth LOGIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --quit-after AUTH + assert_failure + assert_output --partial 'Host did not advertise authentication' - _send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 465' + _send_email \ + --port 465 \ + --auth LOGIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --quit-after AUTH + assert_success assert_output --partial 'Authentication successful' - _send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 587' + _send_email \ + --port 587 \ + --auth LOGIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --quit-after AUTH + assert_success assert_output --partial 'Authentication successful' } diff --git a/test/tests/serial/mail_with_ldap.bats b/test/tests/serial/mail_with_ldap.bats index b7b3884b..f2011d22 100644 --- a/test/tests/serial/mail_with_ldap.bats +++ b/test/tests/serial/mail_with_ldap.bats @@ -122,7 +122,6 @@ function setup_file() { # Extra ENV needed to support specific test-cases: local ENV_SUPPORT=( - --env PERMIT_DOCKER=container # Required for attempting SMTP auth on port 25 via nc # Required for openssl commands to be successul: # NOTE: snakeoil cert is created (for `docker-mailserver.invalid`) via Debian post-install script for Postfix package. # TODO: Use proper TLS cert @@ -249,7 +248,7 @@ function teardown() { # dovecot @test "dovecot: ldap imap connection and authentication works" { - _run_in_container_bash 'nc -w 1 0.0.0.0 143 < /tmp/docker-mailserver-test/auth/imap-ldap-auth.txt' + _nc_wrapper 'auth/imap-ldap-auth' '-w 1 0.0.0.0 143' assert_success } @@ -327,12 +326,25 @@ function teardown() { @test "spoofing (with LDAP): rejects sender forging" { _wait_for_smtp_port_in_container_to_respond dms-test_ldap - _run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed.txt' + _send_email \ + --port 465 -tlsc --auth LOGIN \ + --auth-user some.user@localhost.localdomain \ + --auth-password secret \ + --ehlo mail \ + --from ldap@localhost.localdomain \ + --data 'auth/ldap-smtp-auth-spoofed' assert_output --partial 'Sender address rejected: not owned by user' } @test "spoofing (with LDAP): accepts sending as alias" { - _run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed-alias.txt' + _send_email \ + --port 465 -tlsc --auth LOGIN \ + --auth-user some.user@localhost.localdomain \ + --auth-password secret \ + --ehlo mail \ + --from postmaster@localhost.localdomain \ + --to some.user@localhost.localdomain \ + --data 'auth/ldap-smtp-auth-spoofed-alias' assert_output --partial 'End data with' } @@ -341,19 +353,42 @@ function teardown() { # Template used has invalid AUTH: https://github.com/docker-mailserver/docker-mailserver/pull/3006#discussion_r1073321432 skip 'TODO: This test seems to have been broken from the start (?)' - _run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt' + _send_email \ + --port 465 -tlsc --auth LOGIN \ + --auth-user some.user.email@localhost.localdomain \ + --auth-password secret \ + --ehlo mail \ + --from randomspoofedaddress@localhost.localdomain \ + --to some.user@localhost.localdomain \ + --data 'auth/ldap-smtp-auth-spoofed-sender-with-filter-exception' assert_output --partial 'Sender address rejected: not owned by user' } @test "saslauthd: ldap smtp authentication" { - # Requires ENV `PERMIT_DOCKER=container` - _send_email 'auth/sasl-ldap-smtp-auth' '-w 5 0.0.0.0 25' - assert_output --partial 'Error: authentication not enabled' + _send_email \ + --auth LOGIN \ + --auth-user some.user@localhost.localdomain \ + --auth-password wrongpassword \ + --quit-after AUTH + assert_failure + assert_output --partial 'Host did not advertise authentication' - _run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/sasl-ldap-smtp-auth.txt' + _send_email \ + --port 465 -tlsc \ + --auth LOGIN \ + --auth-user some.user@localhost.localdomain \ + --auth-password secret \ + --quit-after AUTH + assert_success assert_output --partial 'Authentication successful' - _run_in_container_bash 'openssl s_client -quiet -starttls smtp -connect 0.0.0.0:587 < /tmp/docker-mailserver-test/auth/sasl-ldap-smtp-auth.txt' + _send_email \ + --port 587 -tls \ + --auth LOGIN \ + --auth-user some.user@localhost.localdomain \ + --auth-password secret \ + --quit-after AUTH + assert_success assert_output --partial 'Authentication successful' } @@ -391,7 +426,7 @@ function _should_successfully_deliver_mail_to() { local SENDER_ADDRESS='user@external.tld' local RECIPIENT_ADDRESS=${1:?Recipient address is required} local MAIL_STORAGE_RECIPIENT=${2:?Recipient storage location is required} - local MAIL_TEMPLATE='/tmp/docker-mailserver-test/email-templates/test-email.txt' + local MAIL_TEMPLATE='/tmp/docker-mailserver-test/emails/test-email.txt' _run_in_container_bash "sendmail -f ${SENDER_ADDRESS} ${RECIPIENT_ADDRESS} < ${MAIL_TEMPLATE}" _wait_for_empty_mail_queue_in_container diff --git a/test/tests/serial/permit_docker.bats b/test/tests/serial/permit_docker.bats index 85f00484..2ebf5e3e 100644 --- a/test/tests/serial/permit_docker.bats +++ b/test/tests/serial/permit_docker.bats @@ -13,7 +13,7 @@ setup_file() { PRIVATE_CONFIG=$(duplicate_config_for_container . mail_smtponly_second_network) docker create --name mail_smtponly_second_network \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \ -e SMTP_ONLY=1 \ -e PERMIT_DOCKER=connected-networks \ -e OVERRIDE_HOSTNAME=mail.my-domain.com \ @@ -26,7 +26,7 @@ setup_file() { PRIVATE_CONFIG=$(duplicate_config_for_container . mail_smtponly_second_network_sender) docker run -d --name mail_smtponly_second_network_sender \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \ -e SMTP_ONLY=1 \ -e PERMIT_DOCKER=connected-networks \ -e OVERRIDE_HOSTNAME=mail.my-domain.com \ @@ -39,7 +39,7 @@ setup_file() { # create another container that enforces authentication even on local connections docker run -d --name mail_smtponly_force_authentication \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \ -e SMTP_ONLY=1 \ -e PERMIT_DOCKER=none \ -e OVERRIDE_HOSTNAME=mail.my-domain.com \ @@ -68,7 +68,7 @@ teardown_file() { _reload_postfix mail_smtponly_second_network # we should be able to send from the other container on the second network! - run docker exec mail_smtponly_second_network_sender /bin/sh -c "nc mail_smtponly_second_network 25 < /tmp/docker-mailserver-test/email-templates/smtp-only.txt" + run docker exec mail_smtponly_second_network_sender /bin/sh -c "nc mail_smtponly_second_network 25 < /tmp/docker-mailserver-test/emails/nc_raw/smtp-only.txt" assert_output --partial "250 2.0.0 Ok: queued as " repeat_in_container_until_success_or_timeout 60 mail_smtponly_second_network /bin/sh -c 'grep -cE "to=.*status\=sent" /var/log/mail/mail.log' } @@ -80,7 +80,7 @@ teardown_file() { _reload_postfix mail_smtponly_force_authentication # the mailserver should require authentication and a protocol error should occur when using TLS - run docker exec mail_smtponly_force_authentication /bin/sh -c "nc localhost 25 < /tmp/docker-mailserver-test/email-templates/smtp-only.txt" + run docker exec mail_smtponly_force_authentication /bin/sh -c "nc localhost 25 < /tmp/docker-mailserver-test/emails/nc_raw/smtp-only.txt" assert_output --partial "550 5.5.1 Protocol error" [[ ${status} -ge 0 ]] } diff --git a/test/tests/serial/test_helper.bats b/test/tests/serial/test_helper.bats index ecca3d85..a3ffa6cf 100644 --- a/test/tests/serial/test_helper.bats +++ b/test/tests/serial/test_helper.bats @@ -171,7 +171,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:' # enable ClamAV to make message delivery slower, so we can detect it CONTAINER_NAME=$(docker run -d --rm \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \ -e ENABLE_CLAMAV=1 \ -h mail.my-domain.com \ -t "${NAME}") @@ -186,7 +186,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:' [[ ${SECONDS} -lt 5 ]] # fill the queue with a message - docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/emails/amavis-virus.txt" # that should still be stuck in the queue ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" @@ -203,7 +203,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:' # enable ClamAV to make message delivery slower, so we can detect it CONTAINER_NAME=$(docker run -d --rm \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \ -e ENABLE_CLAMAV=1 \ -h mail.my-domain.com \ -t "${NAME}") @@ -213,7 +213,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:' wait_for_smtp_port_in_container "${CONTAINER_NAME}" || docker logs "${CONTAINER_NAME}" # fill the queue with a message - docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/emails/amavis-virus.txt" # give it some time to clear the queue SECONDS=0 diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index 094454f0..26deb541 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -80,11 +80,13 @@ function teardown_file() { _default_teardown ; } } @test "imap: authentication works" { - _send_email 'auth/imap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143' + assert_success } @test "imap: added user authentication works" { - _send_email 'auth/added-imap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/added-imap-auth' '-w 1 0.0.0.0 143' + assert_success } # @@ -288,13 +290,34 @@ EOF @test "spoofing: rejects sender forging" { # rejection of spoofed sender _wait_for_smtp_port_in_container_to_respond - _run_in_container_bash "openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed.txt" + + # 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 \ + --port 465 -tlsc --auth LOGIN \ + --auth-user added@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from user2@localhost.localdomain \ + --data 'auth/added-smtp-auth-spoofed' assert_output --partial 'Sender address rejected: not owned by user' } @test "spoofing: accepts sending as alias" { - _run_in_container_bash "openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed-alias.txt | grep 'End data with'" + # 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 LOGIN \ + --auth-user user1@localhost.localdomain \ + --auth-password mypassword \ + --ehlo mail \ + --from alias1@localhost.localdomain \ + --data 'auth/added-smtp-auth-spoofed-alias' assert_success + assert_output --partial 'End data with' } # diff --git a/test/tests/serial/vmail-id.bats b/test/tests/serial/vmail-id.bats index b44670b2..0f54ea96 100644 --- a/test/tests/serial/vmail-id.bats +++ b/test/tests/serial/vmail-id.bats @@ -20,7 +20,7 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test 'should successfully deliver mail' { - _send_email 'email-templates/existing-user1' + _send_email --data 'existing/user1' _wait_for_empty_mail_queue_in_container # Should be successfully sent (received) by Postfix: From 25c7024cc4c7a6ee81be70144f6ecaf4fddf44ca Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Wed, 3 Jan 2024 02:02:59 +0100 Subject: [PATCH 16/34] security(Postfix): Protect against "SMTP Smuggling" attack (#3727) View `CHANGELOG.md` entry and PR for details. --------- Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- CHANGELOG.md | 15 ++++++++++++++- target/postfix/main.cf | 6 ++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eeeb843d..b6e6d906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v13.0.0...HEAD) +## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v13.1.0...HEAD) > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +### Security + +DMS is now secured against the [recently published spoofing attack "SMTP Smuggling"](https://www.postfix.org/smtp-smuggling.html) that affected Postfix ([#3727](https://github.com/docker-mailserver/docker-mailserver/pull/3727)): +- Postfix upgraded from `3.5.18` to `3.5.23` which provides the [long-term fix with `smtpd_forbid_bare_newline = yes`](https://www.postfix.org/smtp-smuggling.html#long) +- If you are unable to upgrade to this release of DMS, you may follow [these instructions](https://github.com/docker-mailserver/docker-mailserver/issues/3719#issuecomment-1870865118) for applying the [short-term workaround](https://www.postfix.org/smtp-smuggling.html#short). +- This change should not cause compatibility concerns for legitimate mail clients, however if you use software like `netcat` to send mail to DMS (_like our test-suite previously did_) it may now be rejected (_especially with the the short-term workaround `smtpd_data_restrictions = reject_unauth_pipelining`_). +- **NOTE:** This Postfix update also includes the new parameter [`smtpd_forbid_bare_newline_exclusions`](https://www.postfix.org/postconf.5.html#smtpd_forbid_bare_newline_exclusions) which defaults to `$mynetworks` for excluding trusted mail clients excluded from the restriction. + - With our default `PERMIT_DOCKER=none` this is not a concern. + - Presently the Docker daemon config has `user-proxy: true` enabled by default. + - On a host that can be reached by IPv6, this will route to a DMS IPv4 only container implicitly through the Docker network bridge gateway which rewrites the source address. + - If your `PERMIT_DOCKER` setting allows that gateway IP, then it is part of `$mynetworks` and this attack would not be prevented from such connections. + - If this affects your deployment, refer to [our IPv6 docs](https://docker-mailserver.github.io/docker-mailserver/v13.2/config/advanced/ipv6/) for advice on handling IPv6 correctly in Docker. Alternatively [use our `postfix-main.cf`](https://docker-mailserver.github.io/docker-mailserver/v13.2/config/advanced/override-defaults/postfix/) to set `smtpd_forbid_bare_newline_exclusions=` as empty. + ### Updates - The test suite now uses `swaks` instead of `nc`, which has multiple benefits ([#3732](https://github.com/docker-mailserver/docker-mailserver/pull/3732)): diff --git a/target/postfix/main.cf b/target/postfix/main.cf index 8c329c94..a9230347 100644 --- a/target/postfix/main.cf +++ b/target/postfix/main.cf @@ -57,6 +57,12 @@ smtpd_sender_restrictions = $dms_smtpd_sender_restrictions smtpd_discard_ehlo_keywords = silent-discard, dsn disable_vrfy_command = yes +# Security - Prevent SMTP Smuggling attack +# https://www.postfix.org/smtp-smuggling.html#long +smtpd_forbid_bare_newline = yes +# It is possible to exclude clients on trusted networks from this restriction (the upstream default is `$mynetwork`): +# smtpd_forbid_bare_newline_exclusions = $mynetworks + # Custom defined parameters for DMS: dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain # Submission ports 587 and 465 support for SPOOF_PROTECTION=1 From bf69ef248ead4bc4e9304383736f52e35183b48b Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Thu, 4 Jan 2024 22:13:13 +0100 Subject: [PATCH 17/34] Postfix: add `smtpd_data_restrictions = reject_unauth_pipelining` (#3744) * add `smtpd_data_restrictions = reject_unauth_pipelining` * fix: Skip restriction if trusted * add changelog entry * revert change to `postfix-amavis.cf` * Update CHANGELOG.md --------- Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ target/postfix/main.cf | 5 +++-- test/tests/parallel/set1/spam_virus/postgrey_enabled.bats | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6e6d906..0c490092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ DMS is now secured against the [recently published spoofing attack "SMTP Smuggli - `swaks` handles pipelining correctly, hence we can now use `reject_unauth_pipelining` in Postfix's configuration. - `swaks` provides better CLI options that make many files superflous. - `swaks` can also replace `openssl s_client` and handles authentication on submission ports better. +- **Postfix:** + - We now defer rejection from unauthorized pipelining until the SMTP `DATA` command via `smtpd_data_restrictions` (_i.e. at the end of the mail transfer transaction_) ([#3744](https://github.com/docker-mailserver/docker-mailserver/pull/3744)) + - Prevously our configuration only handled this during the client and recipient restriction stages. Postfix will flag this activity when encountered, but the rejection now is handled at `DATA` where unauthorized pipelining would have been valid from this point. + - If you had the Amavis service enabled (default), this restriction was already in place. Otherwise the concerns expressed with `smtpd_data_restrictions = reject_unauth_pipelining` from the security section above apply. We have permitted trusted clients (_`$mynetworks` or authenticated_) to bypass this restriction. ## [v13.1.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.1.0) diff --git a/target/postfix/main.cf b/target/postfix/main.cf index a9230347..1dc7bdbc 100644 --- a/target/postfix/main.cf +++ b/target/postfix/main.cf @@ -51,10 +51,11 @@ smtpd_helo_required = yes smtpd_delay_reject = yes smtpd_helo_restrictions = permit_mynetworks, reject_invalid_helo_hostname, permit smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination -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_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain +smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination smtpd_sender_restrictions = $dms_smtpd_sender_restrictions smtpd_discard_ehlo_keywords = silent-discard, dsn +smtpd_data_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_pipelining disable_vrfy_command = yes # Security - Prevent SMTP Smuggling attack diff --git a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats index 316e3350..389fc183 100644 --- a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats +++ b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats @@ -44,7 +44,7 @@ function teardown_file() { _default_teardown ; } # The other spam checks in `main.cf:smtpd_recipient_restrictions` would interfere with testing postgrey. _run_in_container sed -i \ -e 's/permit_sasl_authenticated.*policyd-spf,$//g' \ - -e 's/reject_unauth_pipelining.*reject_unknown_recipient_domain,$//g' \ + -e 's/reject_invalid_helo_hostname.*reject_unknown_recipient_domain,$//g' \ -e 's/reject_rbl_client.*inet:127\.0\.0\.1:10023$//g' \ -e 's/smtpd_recipient_restrictions =/smtpd_recipient_restrictions = check_policy_service inet:127.0.0.1:10023/g' \ /etc/postfix/main.cf From 8f391e4d5aa6449fcf8a4762df23cba98c52bc4c Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Thu, 4 Jan 2024 22:14:51 +0100 Subject: [PATCH 18/34] release: v13.2.0 (#3746) * bump `VERSION` & adjust `CHANGELOG.md` * chore: Bump the unreleased base compare tag in `CHANGELOG.md` --------- Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- CHANGELOG.md | 5 ++++- VERSION | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c490092..842b60c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v13.1.0...HEAD) +## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v13.2.0...HEAD) > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +## [v13.2.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.2.0) + ### Security DMS is now secured against the [recently published spoofing attack "SMTP Smuggling"](https://www.postfix.org/smtp-smuggling.html) that affected Postfix ([#3727](https://github.com/docker-mailserver/docker-mailserver/pull/3727)): + - Postfix upgraded from `3.5.18` to `3.5.23` which provides the [long-term fix with `smtpd_forbid_bare_newline = yes`](https://www.postfix.org/smtp-smuggling.html#long) - If you are unable to upgrade to this release of DMS, you may follow [these instructions](https://github.com/docker-mailserver/docker-mailserver/issues/3719#issuecomment-1870865118) for applying the [short-term workaround](https://www.postfix.org/smtp-smuggling.html#short). - This change should not cause compatibility concerns for legitimate mail clients, however if you use software like `netcat` to send mail to DMS (_like our test-suite previously did_) it may now be rejected (_especially with the the short-term workaround `smtpd_data_restrictions = reject_unauth_pipelining`_). diff --git a/VERSION b/VERSION index e6ba3513..67aee239 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -13.1.0 +13.2.0 From 04f4ae4569a2b5dcaffb510cd09ef1fb6ba038a2 Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Fri, 5 Jan 2024 09:07:31 +0100 Subject: [PATCH 19/34] Rspamd: add custom symbol scores for SPF, DKIM & DMARC (#3726) --- CHANGELOG.md | 5 + Dockerfile | 1 + target/rspamd/local.d/actions.conf | 11 ++- target/rspamd/scores.d/policies_group.conf | 108 +++++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 target/rspamd/scores.d/policies_group.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index 842b60c1..bccbe0e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file. The format > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +### Updates + +- **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)): + - symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24); please note though that complete alignment is undesirable, because other symbols might be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996) + ## [v13.2.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.2.0) ### Security diff --git a/Dockerfile b/Dockerfile index 4d0e3568..f9802c2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -106,6 +106,7 @@ EOF # ----------------------------------------------- COPY target/rspamd/local.d/ /etc/rspamd/local.d/ +COPY target/rspamd/scores.d/* /etc/rspamd/scores.d/ # ----------------------------------------------- # --- LDAP & SpamAssassin's Cron ---------------- diff --git a/target/rspamd/local.d/actions.conf b/target/rspamd/local.d/actions.conf index b214c339..fb4c15b9 100644 --- a/target/rspamd/local.d/actions.conf +++ b/target/rspamd/local.d/actions.conf @@ -1,9 +1,12 @@ # documentation: https://rspamd.com/doc/configuration/metrics.html#actions # and https://rspamd.com/doc/configuration/metrics.html -#greylist = 4; -#add_header = 6; -#rewrite_subject = 7; -#reject = 15; +# These values work in conjunction with the symbol scores in +# `scores.d/*.conf`. When adjusting them, make sure to understand +# and to be able to explain the impact on the whole system. +greylist = 4; +add_header = 6; +rewrite_subject = 7; +reject = 11; subject = "***SPAM*** %s" diff --git a/target/rspamd/scores.d/policies_group.conf b/target/rspamd/scores.d/policies_group.conf new file mode 100644 index 00000000..5f9426e9 --- /dev/null +++ b/target/rspamd/scores.d/policies_group.conf @@ -0,0 +1,108 @@ +# Please refer to +# https://github.com/docker-mailserver/docker-mailserver/issues/3690 +# for understanding this file and its scores' values. + +symbols = { + # SPF + "R_SPF_ALLOW" { + weight = -1; + description = "SPF verification allows sending"; + groups = ["spf"]; + } + "R_SPF_NA" { + weight = 1.5; + description = "Missing SPF record"; + one_shot = true; + groups = ["spf"]; + } + "R_SPF_SOFTFAIL" { + weight = 2.5; + description = "SPF verification soft-failed"; + groups = ["spf"]; + } + "R_SPF_FAIL" { + weight = 4.5; + description = "SPF verification failed"; + groups = ["spf"]; + } + + "R_SPF_NEUTRAL" { # == R_SPF_NA + weight = 1.5; + description = "SPF policy is neutral"; + groups = ["spf"]; + } + "R_SPF_DNSFAIL" { # == R_SPF_SOFTFAIL + weight = 2.5; + description = "SPF DNS failure"; + groups = ["spf"]; + } + "R_SPF_PERMFAIL" { # == R_SPF_FAIL + weight = 4.5; + description = "SPF record is malformed or persistent DNS error"; + groups = ["spf"]; + } + + # DKIM + "R_DKIM_ALLOW" { + weight = -1; + description = "DKIM verification succeed"; + one_shot = true; + groups = ["dkim"]; + } + "R_DKIM_NA" { + weight = 0; + description = "Missing DKIM signature"; + one_shot = true; + groups = ["dkim"]; + } + "R_DKIM_TEMPFAIL" { + weight = 1.5; + description = "DKIM verification soft-failed"; + groups = ["dkim"]; + } + "R_DKIM_PERMFAIL" { + weight = 4.5; + description = "DKIM verification hard-failed (invalid)"; + groups = ["dkim"]; + } + + "R_DKIM_REJECT" { # == R_DKIM_PERMFAIL + weight = 4.5; + description = "DKIM verification failed"; + one_shot = true; + groups = ["dkim"]; + } + + # DMARC + "DMARC_NA" { + weight = 1; + description = "No DMARC record"; + groups = ["dmarc"]; + } + "DMARC_POLICY_QUARANTINE" { + weight = 1.5; + description = "DMARC quarantine policy"; + groups = ["dmarc"]; + } + "DMARC_POLICY_REJECT" { + weight = 2; + description = "DMARC reject policy"; + groups = ["dmarc"]; + } + + "DMARC_POLICY_ALLOW" { # no equivalent + weight = -1; + description = "DMARC permit policy"; + groups = ["dmarc"]; + } + "DMARC_POLICY_ALLOW_WITH_FAILURES" { # no equivalent + weight = -0.5; + description = "DMARC permit policy with DKIM/SPF failure"; + groups = ["dmarc"]; + } + "DMARC_POLICY_SOFTFAIL" { # == DMARC_POLICY_QUARANTINE + weight = 1.5; + description = "DMARC soft-failed"; + groups = ["dmarc"]; + } +} From 6082d5f8d04c353dc1f9de723f3acbdfb4a8fa32 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Sat, 6 Jan 2024 11:18:30 +1300 Subject: [PATCH 20/34] chore: Disable `smtputf8` support in config directly (#3750) * chore: Disable `smtputf8` support in config This was always configured disabled at runtime, better to just set explicitly in `main.cf` unless config diverges when Dovecot is enabled to opt-out of this feature. --- CHANGELOG.md | 2 ++ target/postfix/main.cf | 3 +++ target/scripts/startup/setup.d/postfix.sh | 3 --- test/tests/parallel/set3/mta/smtp_delivery.bats | 4 ++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bccbe0e9..5f229530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. The format ### Updates +- **Internal:** + - Postfix is now configured with `smtputf8_enable = no` in our default `main.cf` config (_instead of during container startup_). ([#3750](https://github.com/docker-mailserver/docker-mailserver/pull/3750)) - **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)): - symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24); please note though that complete alignment is undesirable, because other symbols might be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996) diff --git a/target/postfix/main.cf b/target/postfix/main.cf index 1dc7bdbc..495ad8a9 100644 --- a/target/postfix/main.cf +++ b/target/postfix/main.cf @@ -5,6 +5,9 @@ biff = no append_dot_mydomain = no readme_directory = no +# Disabled as not compatible with Dovecot +smtputf8_enable = no + # Basic configuration # myhostname = alias_maps = hash:/etc/aliases diff --git a/target/scripts/startup/setup.d/postfix.sh b/target/scripts/startup/setup.d/postfix.sh index 58998376..5aec8636 100644 --- a/target/scripts/startup/setup.d/postfix.sh +++ b/target/scripts/startup/setup.d/postfix.sh @@ -19,9 +19,6 @@ function _setup_postfix_early() { postconf "inet_protocols = ${POSTFIX_INET_PROTOCOLS}" fi - __postfix__log 'trace' "Disabling SMTPUTF8 support" - postconf 'smtputf8_enable = no' - __postfix__log 'trace' "Configuring SASLauthd" if [[ ${ENABLE_SASLAUTHD} -eq 1 ]] && [[ ! -f /etc/postfix/sasl/smtpd.conf ]]; then cat >/etc/postfix/sasl/smtpd.conf << EOF diff --git a/test/tests/parallel/set3/mta/smtp_delivery.bats b/test/tests/parallel/set3/mta/smtp_delivery.bats index 169f374a..f87f11ed 100644 --- a/test/tests/parallel/set3/mta/smtp_delivery.bats +++ b/test/tests/parallel/set3/mta/smtp_delivery.bats @@ -277,6 +277,10 @@ function _successful() { --protocol ESMTP \ --server mail.example.test \ --quit-after FIRST-EHLO + + # Ensure the output is actually related to what we want to refute against: + assert_output --partial 'EHLO mail.external.tld' + assert_output --partial '221 2.0.0 Bye' refute_output --partial 'SMTPUTF8' } From 0eb4ac771498e1eff91944e79c19c8605995bc07 Mon Sep 17 00:00:00 2001 From: Casper Date: Sat, 6 Jan 2024 17:07:00 +0100 Subject: [PATCH 21/34] tests: Replace `wc -l` with `grep -c` (#3752) --- CHANGELOG.md | 1 + .../parallel/set1/spam_virus/clamav.bats | 2 +- .../parallel/set3/scripts/setup_cli.bats | 20 +++++++++---------- test/tests/serial/tests.bats | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f229530..d8f8a0d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. The format ### Updates - **Internal:** + - tests: Replace `wc -l` with `grep -c` ([#3752](https://github.com/docker-mailserver/docker-mailserver/pull/3752)) - Postfix is now configured with `smtputf8_enable = no` in our default `main.cf` config (_instead of during container startup_). ([#3750](https://github.com/docker-mailserver/docker-mailserver/pull/3750)) - **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)): - symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24); please note though that complete alignment is undesirable, because other symbols might be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996) diff --git a/test/tests/parallel/set1/spam_virus/clamav.bats b/test/tests/parallel/set1/spam_virus/clamav.bats index 9232f90f..9c035f5b 100644 --- a/test/tests/parallel/set1/spam_virus/clamav.bats +++ b/test/tests/parallel/set1/spam_virus/clamav.bats @@ -33,7 +33,7 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test 'log files exist at /var/log/mail directory' { - _run_in_container_bash "ls -1 /var/log/mail/ | grep -E 'clamav|freshclam|mail.log' | wc -l" + _run_in_container_bash "ls -1 /var/log/mail/ | grep -c -E 'clamav|freshclam|mail.log'" assert_success assert_output 3 } diff --git a/test/tests/parallel/set3/scripts/setup_cli.bats b/test/tests/parallel/set3/scripts/setup_cli.bats index dca61358..76f16dce 100644 --- a/test/tests/parallel/set3/scripts/setup_cli.bats +++ b/test/tests/parallel/set3/scripts/setup_cli.bats @@ -204,12 +204,12 @@ function teardown_file() { _default_teardown ; } run ./setup.sh -c "${CONTAINER_NAME}" quota set quota_user2 51M assert_failure - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:12M\$' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:12M\$' | grep 1" assert_success run ./setup.sh -c "${CONTAINER_NAME}" quota set quota_user@example.com 26M assert_success - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:26M\$' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:26M\$' | grep 1" assert_success run grep "quota_user2@example.com" "${TEST_TMP_CONFIG}/dovecot-quotas.cf" @@ -220,12 +220,12 @@ function teardown_file() { _default_teardown ; } @test "delquota" { run ./setup.sh -c "${CONTAINER_NAME}" quota set quota_user@example.com 12M assert_success - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:12M\$' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:12M\$' | grep 1" assert_success run ./setup.sh -c "${CONTAINER_NAME}" quota del unknown@domain.com assert_failure - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:12M\$' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:12M\$' | grep 1" assert_success run ./setup.sh -c "${CONTAINER_NAME}" quota del quota_user@example.com @@ -260,13 +260,13 @@ function teardown_file() { _default_teardown ; } ./setup.sh -c "${CONTAINER_NAME}" relay add-domain example3.org smtp.relay.com 587 # check adding - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example1.org\s\+\[smtp.relay1.com\]:2525' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example1.org\s\+\[smtp.relay1.com\]:2525' | grep 1" assert_success # test default port - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example2.org\s\+\[smtp.relay2.com\]:25' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example2.org\s\+\[smtp.relay2.com\]:25' | grep 1" assert_success # test modifying - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example3.org\s\+\[smtp.relay.com\]:587' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example3.org\s\+\[smtp.relay.com\]:587' | grep 1" assert_success } @@ -276,16 +276,16 @@ function teardown_file() { _default_teardown ; } ./setup.sh -c "${CONTAINER_NAME}" relay add-auth example2.org smtp_user2 smtp_pass_new # test adding - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -e '^@example.org\s\+smtp_user:smtp_pass' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -c -e '^@example.org\s\+smtp_user:smtp_pass' | grep 1" assert_success # test updating - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -e '^@example2.org\s\+smtp_user2:smtp_pass_new' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -c -e '^@example2.org\s\+smtp_user2:smtp_pass_new' | grep 1" assert_success } @test "relay exclude-domain" { ./setup.sh -c "${CONTAINER_NAME}" relay exclude-domain example.org - run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example.org\s*$' | wc -l | grep 1" + run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example.org\s*$' | grep 1" assert_success } diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index 26deb541..752e325e 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -264,7 +264,7 @@ EOF # @test "amavis: config overrides" { - _run_in_container_bash "grep 'Test Verification' /etc/amavis/conf.d/50-user | wc -l" + _run_in_container_bash "grep -c 'Test Verification' /etc/amavis/conf.d/50-user" assert_success assert_output 1 } From 6d666512c1c2f8307b5d851d066a38eeb367d306 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:34:24 +1300 Subject: [PATCH 22/34] ci: `.gitattributes` - Ensure `eol=lf` for shell scripts (#3755) * ci: `.gitattributes` - Ensure `eol=lf` for shell scripts - These files should always use LF for line endings during a checkout. - `Dockerfile` does not like building with HereDoc `RUN` scripts that expect LF. --- .gitattributes | 7 +++---- CHANGELOG.md | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index 869c153e..da5dadda 100644 --- a/.gitattributes +++ b/.gitattributes @@ -20,9 +20,8 @@ ## BUILD: .dockerignore text -Dockerfile text +Dockerfile text eol=lf Makefile -VERSION ## EXAMPLE (RUNTIME): *.env text @@ -75,8 +74,8 @@ target/postsrsd/** text ################################################# ## BATS -*.bash text -*.bats text +*.bash text eol=lf +*.bats text eol=lf ## CONFIG (test/config/) ### OpenLDAP image diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f8a0d0..39a0ab66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ All notable changes to this project will be documented in this file. The format - **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)): - symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24); please note though that complete alignment is undesirable, because other symbols might be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996) +### Fixes + +- **Internal:** + - `.gitattributes`: Always use LF line endings on checkout for files with shell script content ([#3755](https://github.com/docker-mailserver/docker-mailserver/pull/3755)) + ## [v13.2.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.2.0) ### Security From 8e2017320092a2db2cb56ae7255271774e0470c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:40:01 +1300 Subject: [PATCH 23/34] docs: updated `CONTRIBUTORS.md` (#3757) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CONTRIBUTORS.md | 2108 +++++++++++++++++++++++------------------------ 1 file changed, 1054 insertions(+), 1054 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4103ad5f..05a4e6e4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -55,13 +55,6 @@ Thanks goes to these wonderful people ✨ tomav - - - erik-wramner -
- erik-wramner -
- polarathene @@ -69,6 +62,13 @@ Thanks goes to these wonderful people ✨ polarathene + + + erik-wramner +
+ erik-wramner +
+ chikamichi @@ -162,19 +162,19 @@ Thanks goes to these wonderful people ✨ crazystick - - - swiesend -
- swiesend -
- svenyonson
svenyonson
+ + + + swiesend +
+ swiesend +
@@ -213,10 +213,10 @@ Thanks goes to these wonderful people ✨ - - KyleOndy + + mindrunner
- KyleOndy + mindrunner
@@ -228,17 +228,10 @@ Thanks goes to these wonderful people ✨ - - mindrunner + + KyleOndy
- mindrunner -
- - - - m-a-v -
- m-a-v + KyleOndy
@@ -248,6 +241,13 @@ Thanks goes to these wonderful people ✨ bilak + + + m-a-v +
+ m-a-v +
+ vortex852456 @@ -278,10 +278,46 @@ Thanks goes to these wonderful people ✨ - - dashohoxha + + moqmar
- dashohoxha + moqmar +
+ + + + pyy +
+ pyy +
+ + + + dennis95stumm +
+ dennis95stumm +
+ + + + arneke +
+ arneke +
+ + + + + akmet +
+ akmet +
+ + + + diiigle +
+ diiigle
@@ -298,6 +334,13 @@ Thanks goes to these wonderful people ✨ mathuin + + + dashohoxha +
+ dashohoxha +
+ jamebus @@ -335,46 +378,68 @@ Thanks goes to these wonderful people ✨ - - diiigle + + m-schmoock
- diiigle + m-schmoock
- - akmet + + mjung
- akmet + mjung
- - arneke + + VanVan
- arneke + VanVan
- - dennis95stumm + + andreasgerstmayr
- dennis95stumm + andreasgerstmayr
- - moqmar + + davidszp
- moqmar + davidszp
- - pyy + + kamuri
- pyy + kamuri +
+ + + + guardiande +
+ guardiande +
+ + + + Zehir +
+ Zehir +
+ + + + + weo +
+ weo
@@ -390,8 +455,7 @@ Thanks goes to these wonderful people ✨
Birkenstab - - + BrandonSchmitt @@ -412,7 +476,8 @@ Thanks goes to these wonderful people ✨
citec
- + + yajo @@ -420,6 +485,13 @@ Thanks goes to these wonderful people ✨ yajo + + + analogue +
+ analogue +
+ MakerMatrix @@ -427,6 +499,20 @@ Thanks goes to these wonderful people ✨ MakerMatrix + + + Rubytastic2 +
+ Rubytastic2 +
+ + + + reneploetz +
+ reneploetz +
+ jsonn @@ -443,110 +529,139 @@ Thanks goes to these wonderful people ✨ - - reneploetz + + keslerm
- reneploetz + keslerm
- - Rubytastic2 + + castorinop
- Rubytastic2 + castorinop
- - analogue + + p-fruck
- analogue + p-fruck
- - weo + + Rillke
- weo + Rillke
- - Zehir + + bobbravo2
- Zehir + bobbravo2
- - guardiande + + r-pufky
- guardiande + r-pufky
- - kamuri + + vincentDcmps
- kamuri + vincentDcmps
- - davidszp + + andymel123
- davidszp + andymel123
- - andreasgerstmayr + + bigpigeon
- andreasgerstmayr + bigpigeon
- - mjung + + engelant
- mjung + engelant
- - m-schmoock + + j-marz
- m-schmoock + j-marz
- - VanVan + + lokipo
- VanVan + lokipo
- - elbracht + + msheakoski
- elbracht + msheakoski
- - aminvakil + + GoliathLabs
- aminvakil + GoliathLabs
- - andrewlow + + tbutter
- andrewlow + tbutter +
+ + + + yogo1212 +
+ yogo1212 +
+ + + + mpanneck +
+ mpanneck +
+ + + + + willtho89 +
+ willtho89 +
+ + + + ubenmackin +
+ ubenmackin
@@ -557,10 +672,24 @@ Thanks goes to these wonderful people ✨ - - ubenmackin + + andrewlow
- ubenmackin + andrewlow +
+ + + + aminvakil +
+ aminvakil +
+ + + + elbracht +
+ elbracht
@@ -608,38 +737,38 @@ Thanks goes to these wonderful people ✨ - - fl42 + + nueaf
- fl42 + nueaf
- - ipernet + + martinwepner
- ipernet + martinwepner
- - H4R0 + + artonge
- H4R0 + artonge
- - eltociear + + spacecowboy
- eltociear + spacecowboy
- - jamesfryer + + jedateach
- jamesfryer + jedateach
@@ -651,891 +780,38 @@ Thanks goes to these wonderful people ✨ - - jedateach + + fl42
- jedateach + fl42
- - spacecowboy + + H4R0
- spacecowboy + H4R0
- - artonge + + ipernet
- artonge + ipernet
- - martinwepner + + jamesfryer
- martinwepner + jamesfryer
- - nueaf + + eltociear
- nueaf -
- - - - keslerm -
- keslerm -
- - - - - castorinop -
- castorinop -
- - - - p-fruck -
- p-fruck -
- - - - Rillke -
- Rillke -
- - - - bobbravo2 -
- bobbravo2 -
- - - - r-pufky -
- r-pufky -
- - - - vincentDcmps -
- vincentDcmps -
- - - - - andymel123 -
- andymel123 -
- - - - bigpigeon -
- bigpigeon -
- - - - engelant -
- engelant -
- - - - j-marz -
- j-marz -
- - - - lokipo -
- lokipo -
- - - - msheakoski -
- msheakoski -
- - - - - GoliathLabs -
- GoliathLabs -
- - - - yogo1212 -
- yogo1212 -
- - - - mpanneck -
- mpanneck -
- - - - willtho89 -
- willtho89 -
- - - - tbutter -
- tbutter -
- - - - 0xflotus -
- 0xflotus -
- - - - - ifokeev -
- ifokeev -
- - - - 20th -
- 20th -
- - - - 2b -
- 2b -
- - - - askz -
- askz -
- - - - acch -
- acch -
- - - - vifino -
- vifino -
- - - - - kachkaev -
- kachkaev -
- - - - alexanderneu -
- alexanderneu -
- - - - ch3sh1r -
- ch3sh1r -
- - - - eglia -
- eglia -
- - - - groupmsl -
- groupmsl -
- - - - green-anger -
- green-anger -
- - - - - iRhonin -
- iRhonin -
- - - - MrFreezeex -
- MrFreezeex -
- - - - arunvc -
- arunvc -
- - - - astrocket -
- astrocket -
- - - - baxerus -
- baxerus -
- - - - spock -
- spock -
- - - - - erdos4d -
- erdos4d -
- - - - crash7 -
- crash7 -
- - - - auchri -
- auchri -
- - - - arkanovicz -
- arkanovicz -
- - - - CBeerta -
- CBeerta -
- - - - damianmoore -
- damianmoore -
- - - - - espitall -
- espitall -
- - - - dkarski -
- dkarski -
- - - - dbellavista -
- dbellavista -
- - - - danielvandenberg95 -
- danielvandenberg95 -
- - - - mlatorre31 -
- mlatorre31 -
- - - - mazzz1y -
- mazzz1y -
- - - - - aydodo -
- aydodo -
- - - - vedtam -
- vedtam -
- - - - edvorg -
- edvorg -
- - - - eliroca -
- eliroca -
- - - - ekkis -
- ekkis -
- - - - ErikEngerd -
- ErikEngerd -
- - - - - huncode -
- huncode -
- - - - felixn -
- felixn -
- - - - flole -
- flole -
- - - - froks -
- froks -
- - - - fkefer -
- fkefer -
- - - - frugan-dev -
- frugan-dev -
- - - - - Marsu31 -
- Marsu31 -
- - - - glandais -
- glandais -
- - - - GiovanH -
- GiovanH -
- - - - harryyoud -
- harryyoud -
- - - - HeySora -
- HeySora -
- - - - sirgantrithon -
- sirgantrithon -
- - - - - Influencer -
- Influencer -
- - - - jcalfee -
- jcalfee -
- - - - mivek -
- mivek -
- - - - init-js -
- init-js -
- - - - Jeidnx -
- Jeidnx -
- - - - JiLleON -
- JiLleON -
- - - - - jirislav -
- jirislav -
- - - - jmccl -
- jmccl -
- - - - jurekbarth -
- jurekbarth -
- - - - JOduMonT -
- JOduMonT -
- - - - Kaan88 -
- Kaan88 -
- - - - akkumar -
- akkumar -
- - - - - KCrawley -
- KCrawley -
- - - - khuedoan -
- khuedoan -
- - - - JustAnother1 -
- JustAnother1 -
- - - - LeoWinterDE -
- LeoWinterDE -
- - - - linhandev -
- linhandev -
- - - - luke- -
- luke- -
- - - - - LucidityCrash -
- LucidityCrash -
- - - - MadsRC -
- MadsRC -
- - - - madmath03 -
- madmath03 -
- - - - maxemann96 -
- maxemann96 -
- - - - dragetd -
- dragetd -
- - - - michaeljensen -
- michaeljensen -
- - - - - exhuma -
- exhuma -
- - - - milas -
- milas -
- - - - mcchots -
- mcchots -
- - - - MohammedNoureldin -
- MohammedNoureldin -
- - - - mpldr -
- mpldr -
- - - - naveensrinivasan -
- naveensrinivasan -
- - - - - neuralp -
- neuralp -
- - - - radicand -
- radicand -
- - - - nilshoell -
- nilshoell -
- - - - nknapp -
- nknapp -
- - - - pcqnt -
- pcqnt -
- - - - OrvilleQ -
- OrvilleQ -
- - - - - ovidiucp -
- ovidiucp -
- - - - mrPjer -
- mrPjer -
- - - - p3dda -
- p3dda -
- - - - peter-hartmann -
- peter-hartmann -
- - - - piwai -
- piwai -
- - - - remoe -
- remoe -
- - - - - romansey -
- romansey -
- - - - MightySCollins -
- MightySCollins -
- - - - 501st-alpha1 -
- 501st-alpha1 -
- - - - klamann -
- klamann -
- - - - svdb0 -
- svdb0 -
- - - - 3ap -
- 3ap -
- - - - - shyim -
- shyim -
- - - - sjmudd -
- sjmudd -
- - - - simonsystem -
- simonsystem -
- - - - stephan-devop -
- stephan-devop + eltociear
@@ -1544,15 +820,15 @@ Thanks goes to these wonderful people ✨
stigok - + + 5ven
5ven
- - + syl20bnr @@ -1587,15 +863,15 @@ Thanks goes to these wonderful people ✨
Thiritin
- + + tweibert
tweibert
- - + torus @@ -1630,15 +906,15 @@ Thanks goes to these wonderful people ✨
Drakulix
- + + vilisas
vilisas
- - + 42wim @@ -1667,6 +943,300 @@ Thanks goes to these wonderful people ✨ allddd + + + 0xflotus +
+ 0xflotus +
+ + + + + nknapp +
+ nknapp +
+ + + + pcqnt +
+ pcqnt +
+ + + + OrvilleQ +
+ OrvilleQ +
+ + + + ovidiucp +
+ ovidiucp +
+ + + + mrPjer +
+ mrPjer +
+ + + + p3dda +
+ p3dda +
+ + + + + peter-hartmann +
+ peter-hartmann +
+ + + + piwai +
+ piwai +
+ + + + remoe +
+ remoe +
+ + + + romansey +
+ romansey +
+ + + + MightySCollins +
+ MightySCollins +
+ + + + 501st-alpha1 +
+ 501st-alpha1 +
+ + + + + klamann +
+ klamann +
+ + + + svdb0 +
+ svdb0 +
+ + + + 3ap +
+ 3ap +
+ + + + shyim +
+ shyim +
+ + + + sjmudd +
+ sjmudd +
+ + + + simonsystem +
+ simonsystem +
+ + + + + stephan-devop +
+ stephan-devop +
+ + + + millerjason +
+ millerjason +
+ + + + mplx +
+ mplx +
+ + + + odinis +
+ odinis +
+ + + + okamidash +
+ okamidash +
+ + + + olaf-mandel +
+ olaf-mandel +
+ + + + + ontheair81 +
+ ontheair81 +
+ + + + pravynandas +
+ pravynandas +
+ + + + presocratics +
+ presocratics +
+ + + + rhyst +
+ rhyst +
+ + + + rmlhuk +
+ rmlhuk +
+ + + + rriski +
+ rriski +
+ + + + + schnippl0r +
+ schnippl0r +
+ + + + smargold476 +
+ smargold476 +
+ + + + sportshead +
+ sportshead +
+ + + + squash +
+ squash +
+ + + + strarsis +
+ strarsis +
+ + + + tamueller +
+ tamueller +
+ + + + + vivacarvajalito +
+ vivacarvajalito +
+ + + + wligtenberg +
+ wligtenberg +
+ + + + wolkenschieber +
+ wolkenschieber +
+ + + + worldworm +
+ worldworm +
+ arcaine2 @@ -1819,153 +1389,583 @@ Thanks goes to these wonderful people ✨ - - millerjason + + auchri
- millerjason + auchri
- - mplx + + arkanovicz
- mplx + arkanovicz
- - odinis + + CBeerta
- odinis + CBeerta
- - okamidash + + damianmoore
- okamidash + damianmoore
- - olaf-mandel + + espitall
- olaf-mandel + espitall
- - ontheair81 + + dkarski
- ontheair81 + dkarski
- - pravynandas + + dbellavista
- pravynandas + dbellavista
- - presocratics + + danielvandenberg95
- presocratics + danielvandenberg95
- - rhyst + + mlatorre31
- rhyst + mlatorre31
- - rmlhuk + + mazzz1y
- rmlhuk + mazzz1y
- - rriski + + aydodo
- rriski + aydodo
- - schnippl0r + + vedtam
- schnippl0r + vedtam
- - smargold476 + + edvorg
- smargold476 + edvorg
- - sportshead + + eliroca
- sportshead + eliroca
- - squash + + ekkis
- squash + ekkis
- - strarsis + + ErikEngerd
- strarsis + ErikEngerd
- - tamueller + + huncode
- tamueller + huncode
- - vivacarvajalito + + felixn
- vivacarvajalito + felixn
- - wligtenberg + + flole
- wligtenberg + flole
- - wolkenschieber + + froks
- wolkenschieber + froks
- - worldworm + + ifokeev
- worldworm + ifokeev +
+ + + + 20th +
+ 20th +
+ + + + 2b +
+ 2b +
+ + + + + askz +
+ askz +
+ + + + acch +
+ acch +
+ + + + vifino +
+ vifino +
+ + + + kachkaev +
+ kachkaev +
+ + + + alexanderneu +
+ alexanderneu +
+ + + + ch3sh1r +
+ ch3sh1r +
+ + + + + eglia +
+ eglia +
+ + + + groupmsl +
+ groupmsl +
+ + + + green-anger +
+ green-anger +
+ + + + iRhonin +
+ iRhonin +
+ + + + MrFreezeex +
+ MrFreezeex +
+ + + + arunvc +
+ arunvc +
+ + + + + astrocket +
+ astrocket +
+ + + + baxerus +
+ baxerus +
+ + + + spock +
+ spock +
+ + + + erdos4d +
+ erdos4d +
+ + + + crash7 +
+ crash7 +
+ + + + fkefer +
+ fkefer +
+ + + + + khuedoan +
+ khuedoan +
+ + + + JustAnother1 +
+ JustAnother1 +
+ + + + LeoWinterDE +
+ LeoWinterDE +
+ + + + linhandev +
+ linhandev +
+ + + + luke- +
+ luke- +
+ + + + LucidityCrash +
+ LucidityCrash +
+ + + + + MadsRC +
+ MadsRC +
+ + + + madmath03 +
+ madmath03 +
+ + + + maxemann96 +
+ maxemann96 +
+ + + + dragetd +
+ dragetd +
+ + + + michaeljensen +
+ michaeljensen +
+ + + + exhuma +
+ exhuma +
+ + + + + milas +
+ milas +
+ + + + mcchots +
+ mcchots +
+ + + + MohammedNoureldin +
+ MohammedNoureldin +
+ + + + mpldr +
+ mpldr +
+ + + + naveensrinivasan +
+ naveensrinivasan +
+ + + + neuralp +
+ neuralp +
+ + + + + radicand +
+ radicand +
+ + + + nilshoell +
+ nilshoell +
+ + + + frugan-dev +
+ frugan-dev +
+ + + + Marsu31 +
+ Marsu31 +
+ + + + glandais +
+ glandais +
+ + + + GiovanH +
+ GiovanH +
+ + + + + harryyoud +
+ harryyoud +
+ + + + HeySora +
+ HeySora +
+ + + + sirgantrithon +
+ sirgantrithon +
+ + + + Influencer +
+ Influencer +
+ + + + jcalfee +
+ jcalfee +
+ + + + mivek +
+ mivek +
+ + + + + init-js +
+ init-js +
+ + + + Jeidnx +
+ Jeidnx +
+ + + + JiLleON +
+ JiLleON +
+ + + + jirislav +
+ jirislav +
+ + + + jmccl +
+ jmccl +
+ + + + jurekbarth +
+ jurekbarth +
+ + + + + JOduMonT +
+ JOduMonT +
+ + + + Kaan88 +
+ Kaan88 +
+ + + + akkumar +
+ akkumar +
+ + + + KCrawley +
+ KCrawley
From 5e28c17cf4c72d6b1ebda35c5af9f411d2652159 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:07:38 +1300 Subject: [PATCH 24/34] docs: SpamAssassin ENV docs refactor (#3756) * chore: Log `SPAMASSASSIN_SPAM_TO_INBOX=1` ENV correctly ENV name logged was incomplete. * docs: Update SA related ENV docs * fix: Log level `warning` should be `warn` * docs: FAQ - Revise outdated SA entry * chore: Antispam / Antivirus => Anti-spam / Anti-virus * docs: ENV - Additional revisions to SA ENV * docs: ENV - Move `ENABLE_SPAMASSASSIN_KAM` --- CHANGELOG.md | 3 + Dockerfile | 2 +- README.md | 2 +- .../config/advanced/optional-config.md | 2 +- docs/content/config/environment.md | 179 ++++++++++++++---- docs/content/faq.md | 70 ++----- docs/content/index.md | 2 +- docs/mkdocs.yml | 2 +- mailserver.env | 12 +- .../scripts/startup/setup.d/security/misc.sh | 6 +- 10 files changed, 181 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a0ab66..074c3ead 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ All notable changes to this project will be documented in this file. The format - Postfix is now configured with `smtputf8_enable = no` in our default `main.cf` config (_instead of during container startup_). ([#3750](https://github.com/docker-mailserver/docker-mailserver/pull/3750)) - **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)): - symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24); please note though that complete alignment is undesirable, because other symbols might be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996) +- **Docs:** + - Revised the SpamAssassin ENV docs to better communicate configuration and their relation to other ENV settings. ([#3756](https://github.com/docker-mailserver/docker-mailserver/pull/3756)) + ### Fixes diff --git a/Dockerfile b/Dockerfile index f9802c2a..65d818ab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -319,7 +319,7 @@ LABEL org.opencontainers.image.title="docker-mailserver" LABEL org.opencontainers.image.vendor="The Docker Mailserver Organization" LABEL org.opencontainers.image.authors="The Docker Mailserver Organization on GitHub" LABEL org.opencontainers.image.licenses="MIT" -LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database." +LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database." LABEL org.opencontainers.image.url="https://github.com/docker-mailserver" LABEL org.opencontainers.image.documentation="https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md" LABEL org.opencontainers.image.source="https://github.com/docker-mailserver/docker-mailserver" diff --git a/README.md b/README.md index b3072cd9..8918c2e9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## :page_with_curl: About -A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Originally created by @tomav, this project is now maintained by volunteers since January 2021. +A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Originally created by @tomav, this project is now maintained by volunteers since January 2021. ## :bulb: Documentation diff --git a/docs/content/config/advanced/optional-config.md b/docs/content/config/advanced/optional-config.md index 8a43e4db..21f82a3b 100644 --- a/docs/content/config/advanced/optional-config.md +++ b/docs/content/config/advanced/optional-config.md @@ -33,7 +33,7 @@ This is a list of all configuration files and directories which are optional or - **ldap-aliases.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script. - **ldap-domains.cf:** Configuration for the virtual domain mapping `virtual_mailbox_domains`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script. - **whitelist_clients.local:** Whitelisted domains, not considered by postgrey. Enter one host or domain per line. -- **spamassassin-rules.cf:** Antispam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules]) +- **spamassassin-rules.cf:** Anti-spam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules]) - **fail2ban-fail2ban.cf:** Additional config options for `fail2ban.cf`. (Docs: [Fail2Ban][docs-fail2ban]) - **fail2ban-jail.cf:** Additional config options for fail2ban's jail behaviour. (Docs: [Fail2Ban][docs-fail2ban]) - **amavis.cf:** replaces the `/etc/amavis/conf.d/50-user` file diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index 71807c87..c3c074fd 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -316,28 +316,32 @@ Note: More information at ##### MOVE_SPAM_TO_JUNK -When enabled, e-mails marked with the - -1. `X-Spam: Yes` header added by Rspamd -2. `X-Spam-Flag: YES` header added by SpamAssassin (requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)) - -will be automatically moved to the Junk folder (with the help of a Sieve script). - - 0 => Spam messages will be delivered in the mailbox. - **1** => Spam messages will be delivered in the `Junk` folder. +Routes mail identified as spam into the recipient(s) Junk folder (_via a Dovecot Sieve script_). + +!!! info + + Mail is received as spam when it has been marked with either header: + + - `X-Spam: Yes` (_added by Rspamd_) + - `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_) + ##### MARK_SPAM_AS_READ -Enable to treat received spam as "read" (_avoids notification to MUA client of new mail_). - -Mail is received as spam when it has been marked with either header: - -1. `X-Spam: Yes` (_by Rspamd_) -2. `X-Spam-Flag: YES` (_by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_) - - **0** => disabled - 1 => Spam messages will be marked as read +Enable to treat received spam as "read" (_avoids notification to MUA client of new mail_). + +!!! info + + Mail is received as spam when it has been marked with either header: + + - `X-Spam: Yes` (_added by Rspamd_) + - `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_) + #### Rspamd ##### ENABLE_RSPAMD @@ -515,63 +519,170 @@ Changes the interval in which log files are rotated. - **0** => SpamAssassin is disabled - 1 => SpamAssassin is enabled -##### SPAMASSASSIN_SPAM_TO_INBOX +??? info "SpamAssassin analyzes incoming mail and assigns a spam score" -- 0 => Spam messages will be bounced (_rejected_) without any notification (_dangerous_). -- **1** => Spam messages will be delivered to the inbox and tagged as spam using `SA_SPAM_SUBJECT`. + Integration with Amavis involves processing mail based on the assigned spam score via [`SA_TAG`, `SA_TAG2` and `SA_KILL`][amavis-docs::spam-score]. + + These settings have equivalent ENV supported by DMS for easy adjustments, as documented below. + +[amavis-docs::spam-score]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#tagkill ##### ENABLE_SPAMASSASSIN_KAM -[KAM](https://mcgrail.com/template/projects#KAM1) is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. - - **0** => KAM disabled - 1 => KAM enabled +[KAM](https://mcgrail.com/template/projects#KAM1) is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. + +##### SPAMASSASSIN_SPAM_TO_INBOX + +- 0 => (_Amavis action: `D_BOUNCE`_): Spam messages will be bounced (_rejected_) without any notification (_dangerous_). +- **1** => (_Amavis action: `D_PASS`_): Spam messages will be delivered to the inbox. + +!!! note + + The Amavis action configured by this setting: + + - Influences the behaviour of the [`SA_KILL`](#sa_kill) setting. + - Applies to the Amavis config parameters `$final_spam_destiny` and `$final_bad_header_destiny`. + +!!! note "This ENV setting is related to" + + - [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk) + - [`MARK_SPAM_AS_READ=1`](#mark_spam_as_read) + - [`SA_SPAM_SUBJECT`](#sa_spam_subject) + ##### SA_TAG -- **2.0** => add spam info headers if at, or above that level +- **2.0** => add 'spam info' headers at, or above this spam score -Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1` +Mail is not yet considered spam at this spam score, but for purposes like diagnostics it can be useful to identify mail with a spam score at a lower bound than `SA_TAG2`. + +??? example "`X-Spam` headers appended to mail" + + Send a simple mail to a local DMS account `hello@example.com`: + + ```bash + docker exec dms swaks --server 0.0.0.0 --to hello@example.com --body 'spam' + ``` + + Inspecting the raw mail you will notice several `X-Spam` headers were added to the mail like this: + + ``` + X-Spam-Flag: NO + X-Spam-Score: 4.162 + X-Spam-Level: **** + X-Spam-Status: No, score=4.162 tagged_above=2 required=4 + tests=[BODY_SINGLE_WORD=1, DKIM_ADSP_NXDOMAIN=0.8, + NO_DNS_FOR_FROM=0.379, NO_RECEIVED=-0.001, NO_RELAYS=-0.001, + PYZOR_CHECK=1.985] autolearn=no autolearn_force=no + ``` + + !!! info "The `X-Spam-Score` is `4.162`" + + High enough for `SA_TAG` to trigger adding these headers, but not high enough for `SA_TAG2` (_which would set `X-Spam-Flag: YES` instead_). ##### SA_TAG2 -- **6.31** => add 'spam detected' headers at that level +- **6.31** => add 'spam detected' headers at, or above this level -Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1` +When a spam score is high enough, mark mail as spam (_Appends the mail header: `X-Spam-Flag: YES`_). + +!!! info "Interaction with other ENV" + + - [`SA_SPAM_SUBJECT`](#sa_spam_subject) modifies the mail subject to better communicate spam mail to the user. + - [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk): The mail is still delivered, but to the recipient(s) junk folder instead. This feature reduces the usefulness of `SA_SPAM_SUBJECT`. ##### SA_KILL -- **10.0** => triggers spam evasive actions +- **10.0** => quarantine + triggers action to handle spam -!!! note "This SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`" +Controls the spam score threshold for triggering an action on mail that has a high spam score. - By default, DMS is configured to quarantine spam emails. +??? tip "Choosing an appropriate `SA_KILL` value" - If emails are quarantined, they are compressed and stored in a location dependent on the `ONE_DIR` setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. `100.0`. + The value should be high enough to be represent confidence in mail as spam: - If `ONE_DIR=1` (default) the location is `/var/mail-state/lib-amavis/virusmails/`, or if `ONE_DIR=0`: `/var/lib/amavis/virusmails/`. These paths are inside the docker container. + - Too low: The action taken may prevent legitimate mail (ham) that was incorrectly detected as spam from being delivered successfully. + - Too high: Allows more spam to bypass the `SA_KILL` trigger (_how to treat mail with high confidence that it is actually spam_). + + Experiences from DMS users with these settings has been [collected here][gh-issue::sa-tunables-insights], along with [some direct configuration guides][gh-issue::sa-tunables-guides] (_under "Resources for references"_). + +[gh-issue::sa-tunables-insights]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1420268148 +[gh-issue::sa-tunables-guides]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1416547911 + +??? info "Trigger action" + + DMS will configure Amavis with either of these actions based on the DMS [`SPAMASSASSIN_SPAM_TO_INBOX`](#spamassassin_spam_to_inbox) ENV setting: + + - `D_PASS` (**default**): + - Accept mail and deliver it to the recipient(s), despite the high spam score. A copy is still stored in quarantine. + - This is a good default to start with until you are more confident in an `SA_KILL` threshold that won't accidentally discard / bounce legitimate mail users are expecting to arrive but is detected as spam. + - `D_BOUNCE`: + - Additionally sends a bounce notification (DSN). + - The [DSN is suppressed][amavis-docs::actions] (_no bounce sent_) when the spam score exceeds the Amavis `$sa_dsn_cutoff_level` config setting (default: `10`). With the DMS `SA_KILL` default also being `10`, no DSN will ever be sent. + - `D_REJECT` / `D_DISCARD`: + - These two aren't configured by DMS, but are valid alternative action values if configuring Amavis directly. + +??? note "Quarantined mail" + + When mail has a spam score that reaches the `SA_KILL` threshold: + + - [It will be quarantined][amavis-docs::quarantine] regardless of the `SA_KILL` action to perform. + - With `D_PASS` the delivered mail also appends an `X-Quarantine-ID` mail header. The ID value of this header is part of the quarantined file name. + + If emails are quarantined, they are compressed and stored at a location dependent on the [`ONE_DIR`](#one_dir) setting: + + - `ONE_DIR=1` (default): `/var/mail-state/lib-amavis/virusmails/` + - `ONE_DIR=0`: `/var/lib/amavis/virusmails/` + + !!! tip + + Easily list mail stored in quarantine with `find` and the quarantine path: + + ```bash + find /var/lib/amavis/virusmails -type f + ``` + +[amavis-docs::actions]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#actions +[amavis-docs::quarantine]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#quarantine ##### SA_SPAM_SUBJECT -- **\*\*\*SPAM\*\*\*** => add tag to subject if spam detected +Adds a prefix to the subject header when mail is marked as spam (_via [`SA_TAG2`](#sa_tag2)_). -Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`. Add the SpamAssassin score to the subject line by inserting the keyword \_SCORE\_: **\*\*\*SPAM(\_SCORE\_)\*\*\***. +- **`'***SPAM*** '`** => A string value to use as a mail subject prefix. +- `undef` => Opt-out of modifying the subject for mail marked as spam. + +??? example "Including trailing white-space" + + Add trailing white-space by quote wrapping the value: `SA_SPAM_SUBJECT='[SPAM] '` + +??? example "Including the associated spam score" + + The [`_SCORE_` tag][sa-docs::score-tag] will be substituted with the SpamAssassin score: `SA_SPAM_SUBJECT=***SPAM(_SCORE_)***`. + +[sa-docs::score-tag]: https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin_Conf.html#rewrite_header-subject-from-to-STRING ##### SA_SHORTCIRCUIT_BAYES_SPAM - **1** => will activate SpamAssassin short circuiting for bayes spam detection. -This will uncomment the respective line in ```/etc/spamassasin/local.cf``` +This will uncomment the respective line in `/etc/spamassasin/local.cf` -Note: activate this only if you are confident in your bayes database for identifying spam. +!!! warning + + Activate this only if you are confident in your bayes database for identifying spam. ##### SA_SHORTCIRCUIT_BAYES_HAM - **1** => will activate SpamAssassin short circuiting for bayes ham detection -This will uncomment the respective line in ```/etc/spamassasin/local.cf``` +This will uncomment the respective line in `/etc/spamassasin/local.cf` -Note: activate this only if you are confident in your bayes database for identifying ham. +!!! warning + + Activate this only if you are confident in your bayes database for identifying ham. #### Fetchmail diff --git a/docs/content/faq.md b/docs/content/faq.md index 4da64b60..f666b102 100644 --- a/docs/content/faq.md +++ b/docs/content/faq.md @@ -378,18 +378,7 @@ When you run DMS with the ENV variable `ONE_DIR=1` (default), this directory wil #### How can I manage my custom SpamAssassin rules? -Antispam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`. - -#### What are acceptable `SA_SPAM_SUBJECT` values? - -For no subject set `SA_SPAM_SUBJECT=undef`. - -For a trailing white-space subject one can define the whole variable with quotes in `compose.yaml`: - -```yaml -environment: - - "SA_SPAM_SUBJECT=[SPAM] " -``` +Anti-spam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`. #### Why are SpamAssassin `x-headers` not inserted into my `subdomain.example.com` subdomain emails? @@ -479,59 +468,39 @@ The following configuration works nicely: file: ./docker-data/dms/cron/sa-learn ``` -With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using `--ham` and providing it with some ham mails). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the [SpamAssassin Wiki](https://wiki.apache.org/spamassassin/BayesNotWorking). +With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using `--ham` and providing it with some ham mails). + +- Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. +- For further reference, see the [SpamAssassin Wiki](https://wiki.apache.org/spamassassin/BayesNotWorking). #### How do I have more control about what SpamAssassin is filtering? -By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior: +This is related to Amavis processing the mail after SpamAssassin has analyzed it and assigned a spam score. -First, make sure you have the proper thresholds set: +- DMS provides some [common SA tunables via ENV][docs::env::sa_env]. +- Additional configuration can be managed with the DMS config volume by providing `docker-data/dms/config/amavis.cf`. -```conf -SA_TAG=-100000.0 -SA_TAG2=3.75 -SA_KILL=100000.0 -``` +#### How can I send quarantined mail to a mailbox? -- The very negative value in `SA_TAG` makes sure, that all emails have the SpamAssassin headers included. -- `SA_TAG2` is the actual threshold to set the YES/NO flag for spam detection. -- `SA_KILL` needs to be very high, to make sure nothing is bounced at all (`SA_KILL` superseeds `SPAMASSASSIN_SPAM_TO_INBOX`) +SPAM and INFECTED emails that [reach the `SA_KILL` threshold are archived into quarantine][docs::env::sa_kill]. -Make sure everything (including SPAM) is delivered to the inbox and not quarantined: - -```conf -SPAMASSASSIN_SPAM_TO_INBOX=1 -``` - -Use `MOVE_SPAM_TO_JUNK=1` or create a sieve script which puts spam to the Junk folder: - -```sieve -require ["comparator-i;ascii-numeric","relational","fileinto"]; - -if header :contains "X-Spam-Flag" "YES" { - fileinto "Junk"; -} elsif allof ( - not header :matches "x-spam-score" "-*", - header :value "ge" :comparator "i;ascii-numeric" "x-spam-score" "3.75" -) { - fileinto "Junk"; -} -``` - -Create a dedicated mailbox for emails which are infected/bad header and everything amavis is blocking by default and put its address into `docker-data/dms/config/amavis.cf` +Instead of a quarantine folder, you can use a dedicated mailbox instead. Create an account like `quarantine@example.com` and create `docker-data/dms/config/amavis.cf`: ```cf -$clean_quarantine_to = "amavis\@example.com"; -$virus_quarantine_to = "amavis\@example.com"; -$banned_quarantine_to = "amavis\@example.com"; -$bad_header_quarantine_to = "amavis\@example.com"; -$spam_quarantine_to = "amavis\@example.com"; +$clean_quarantine_to = "quarantine\@example.com"; +$virus_quarantine_to = "quarantine\@example.com"; +$banned_quarantine_to = "quarantine\@example.com"; +$bad_header_quarantine_to = "quarantine\@example.com"; +$spam_quarantine_to = "quarantine\@example.com"; ``` [fail2ban-customize]: ./config/security/fail2ban.md [docs-maintenance]: ./config/advanced/maintenance/update-and-cleanup.md [docs-override-postfix]: ./config/advanced/override-defaults/postfix.md [docs-userpatches]: ./config/advanced/override-defaults/user-patches.md +[docs-optional-configuration]: ./config/advanced/optional-config.md +[docs::env::sa_env]: ./config/environment.md#spamassassin +[docs::env::sa_kill]: ./config/environment.md#sa_kill [github-comment-baredomain]: https://github.com/docker-mailserver/docker-mailserver/issues/3048#issuecomment-1432358353 [github-comment-override-hostname]: https://github.com/docker-mailserver/docker-mailserver/issues/1731#issuecomment-753968425 [github-issue-95]: https://github.com/docker-mailserver/docker-mailserver/issues/95 @@ -542,4 +511,3 @@ $spam_quarantine_to = "amavis\@example.com"; [github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792 [hanscees-userpatches]: https://github.com/hanscees/dockerscripts/blob/master/scripts/tomav-user-patches.sh [mail-state-folders]: https://github.com/docker-mailserver/docker-mailserver/blob/c7e498194546416fb7231cb03254e77e085d18df/target/scripts/startup/misc-stack.sh#L24-L33 -[docs-optional-configuration]: ./config/advanced/optional-config.md diff --git a/docs/content/index.md b/docs/content/index.md index 51be4fb8..ff1214b1 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -14,7 +14,7 @@ This documentation provides you not only with the basic setup and configuration ## About -`docker-mailserver`, or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan "Keep it simple and versioned". +`docker-mailserver`, or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan "Keep it simple and versioned". ## Contents diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index bd41a798..6441dffe 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,6 +1,6 @@ # Site specific: site_name: 'Docker Mailserver' -site_description: 'A fullstack but simple mail-server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.) using Docker.' +site_description: 'A fullstack but simple mail-server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.) using Docker.' site_author: 'docker-mailserver (Github Organization)' copyright: '

© Docker Mailserver Organization
This project is licensed under the MIT license.

' diff --git a/mailserver.env b/mailserver.env index e84157a2..0c2e1e40 100644 --- a/mailserver.env +++ b/mailserver.env @@ -368,9 +368,6 @@ DOVECOT_INET_PROTOCOLS=all ENABLE_SPAMASSASSIN=0 -# deliver spam messages in the inbox (eventually tagged using SA_SPAM_SUBJECT) -SPAMASSASSIN_SPAM_TO_INBOX=1 - # KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. # If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. # - **0** => KAM disabled @@ -379,23 +376,26 @@ SPAMASSASSIN_SPAM_TO_INBOX=1 # Note: only has an effect if `ENABLE_SPAMASSASSIN=1` ENABLE_SPAMASSASSIN_KAM=0 +# deliver spam messages to the inbox (tagged using SA_SPAM_SUBJECT) +SPAMASSASSIN_SPAM_TO_INBOX=1 + # spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required) MOVE_SPAM_TO_JUNK=1 # spam messages wil be marked as read MARK_SPAM_AS_READ=0 -# add spam info headers if at, or above that level: +# add 'spam info' headers at, or above this level SA_TAG=2.0 -# add 'spam detected' headers at that level +# add 'spam detected' headers at, or above this level SA_TAG2=6.31 # triggers spam evasive actions SA_KILL=10.0 # add tag to subject if spam detected -SA_SPAM_SUBJECT=***SPAM***** +SA_SPAM_SUBJECT='***SPAM*** ' # ----------------------------------------------- # --- Fetchmail Section ------------------------- diff --git a/target/scripts/startup/setup.d/security/misc.sh b/target/scripts/startup/setup.d/security/misc.sh index 78c1e60a..170f46fb 100644 --- a/target/scripts/startup/setup.d/security/misc.sh +++ b/target/scripts/startup/setup.d/security/misc.sh @@ -111,7 +111,7 @@ function __setup__security__spamassassin() { if [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 1 ]]; then _log 'trace' 'Configuring Spamassassin/Amavis to send SPAM to inbox' - _log 'debug' 'SPAM_TO_INBOX=1 is set. SA_KILL will be ignored.' + _log 'debug' "'SPAMASSASSIN_SPAM_TO_INBOX=1' is set. The 'SA_KILL' ENV will be ignored." sed -i "s|\$final_spam_destiny.*=.*$|\$final_spam_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver sed -i "s|\$final_bad_header_destiny.*=.*$|\$final_bad_header_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver @@ -265,7 +265,7 @@ EOF chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_to_junk.{sieve,svbin} if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then - _log 'warning' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MOVE_SPAM_TO_JUNK=1' to work" + _log 'warn' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MOVE_SPAM_TO_JUNK=1' to work" fi else _log 'debug' 'Spam emails will not be moved to the Junk folder' @@ -290,7 +290,7 @@ EOF chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_mark_as_read.{sieve,svbin} if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then - _log 'warning' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MARK_SPAM_AS_READ=1' to work" + _log 'warn' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MARK_SPAM_AS_READ=1' to work" fi else _log 'debug' 'Spam emails will not be marked as read' From ac25fb495bce9b21c1b643126aca7b0b26556ea2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:39:28 +0100 Subject: [PATCH 25/34] chore(deps): Bump docker/metadata-action from 5.4.0 to 5.5.0 (#3762) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/generic_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generic_publish.yml b/.github/workflows/generic_publish.yml index b0a32b93..00771221 100644 --- a/.github/workflows/generic_publish.yml +++ b/.github/workflows/generic_publish.yml @@ -23,7 +23,7 @@ jobs: - name: 'Prepare tags' id: prep - uses: docker/metadata-action@v5.4.0 + uses: docker/metadata-action@v5.5.0 with: images: | ${{ secrets.DOCKER_REPOSITORY }} From 8d8936dfac81d762c03177d83876c69cb321e342 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:40:50 +0000 Subject: [PATCH 26/34] chore(deps): Bump anchore/scan-action from 3.3.8 to 3.4.0 (#3761) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/generic_vulnerability-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generic_vulnerability-scan.yml b/.github/workflows/generic_vulnerability-scan.yml index 08b4fd29..d7cf6932 100644 --- a/.github/workflows/generic_vulnerability-scan.yml +++ b/.github/workflows/generic_vulnerability-scan.yml @@ -55,7 +55,7 @@ jobs: provenance: false - name: 'Run the Anchore Grype scan action' - uses: anchore/scan-action@v3.3.8 + uses: anchore/scan-action@v3.4.0 id: scan with: image: mailserver-testing:ci From aba218e6d7f30fbf4717178581c3e04537bea178 Mon Sep 17 00:00:00 2001 From: Casper Date: Wed, 10 Jan 2024 00:31:30 +0100 Subject: [PATCH 27/34] Fix jaq: Download platform specific binary (#3766) * choose architecture dynamically --- CHANGELOG.md | 1 + target/scripts/build/packages.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 074c3ead..86302566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file. The format - **Internal:** - `.gitattributes`: Always use LF line endings on checkout for files with shell script content ([#3755](https://github.com/docker-mailserver/docker-mailserver/pull/3755)) + - Fix missing 'jaq' binary for ARM architecture ([#3766](https://github.com/docker-mailserver/docker-mailserver/pull/3766)) ## [v13.2.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.2.0) diff --git a/target/scripts/build/packages.sh b/target/scripts/build/packages.sh index 566d5441..7a4b60f6 100644 --- a/target/scripts/build/packages.sh +++ b/target/scripts/build/packages.sh @@ -192,7 +192,7 @@ function _install_getmail() { function _install_utils() { _log 'debug' 'Installing utils sourced from Github' - curl -sL https://github.com/01mf02/jaq/releases/latest/download/jaq-v1.2.0-x86_64-unknown-linux-musl -o /usr/bin/jaq && chmod +x /usr/bin/jaq + curl -sL "https://github.com/01mf02/jaq/releases/latest/download/jaq-v1.2.0-$(uname -m)-unknown-linux-gnu" -o /usr/bin/jaq && chmod +x /usr/bin/jaq } function _remove_data_after_package_installations() { From 06fab3f12964b9ce5d5d6e889de28598635c6cf5 Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:34:08 +0100 Subject: [PATCH 28/34] tests: streamline tests and helpers further (#3747) Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- CHANGELOG.md | 7 + target/scripts/build/packages.sh | 10 +- test/files/emails/existing/added.txt | 5 - test/files/emails/existing/alias-external.txt | 5 - test/files/emails/existing/alias-local.txt | 5 - .../existing/alias-recipient-delimiter.txt | 5 - test/files/emails/existing/catchall-local.txt | 5 - .../emails/existing/regexp-alias-external.txt | 5 - .../emails/existing/regexp-alias-local.txt | 5 - .../existing/user-and-cc-local-alias.txt | 6 - test/files/emails/existing/user1.txt | 5 - test/files/emails/non-existing-user.txt | 5 - test/files/emails/rspamd/pass.txt | 5 - test/files/emails/rspamd/spam-header.txt | 5 - test/files/emails/rspamd/spam.txt | 5 - test/files/emails/rspamd/virus.txt | 5 - test/helper/common.bash | 4 +- test/helper/sending.bash | 171 ++++++++++++------ .../parallel/set1/dovecot/dovecot_quotas.bats | 9 +- .../parallel/set1/dovecot/dovecot_sieve.bats | 6 +- .../set1/dovecot/mailbox_format_dbox.bats | 6 +- .../set1/dovecot/special_use_folders.bats | 5 +- .../parallel/set1/spam_virus/clamav.bats | 3 +- .../disabled_clamav_spamassassin.bats | 3 +- .../parallel/set1/spam_virus/fail2ban.bats | 2 +- .../set1/spam_virus/postgrey_enabled.bats | 9 +- .../parallel/set1/spam_virus/postscreen.bats | 14 +- .../parallel/set1/spam_virus/rspamd_full.bats | 38 ++-- .../set1/spam_virus/spam_junk_folder.bats | 2 +- .../container_configuration/hostname.bats | 2 +- test/tests/parallel/set3/mta/dsn.bats | 18 +- test/tests/parallel/set3/mta/lmtp_ip.bats | 2 +- test/tests/parallel/set3/mta/privacy.bats | 5 +- .../parallel/set3/mta/smtp_delivery.bats | 42 ++--- test/tests/serial/mail_pop3.bats | 4 +- test/tests/serial/mail_with_imap.bats | 12 +- test/tests/serial/mail_with_ldap.bats | 27 +-- test/tests/serial/tests.bats | 14 +- test/tests/serial/vmail-id.bats | 4 +- 39 files changed, 243 insertions(+), 247 deletions(-) delete mode 100644 test/files/emails/existing/added.txt delete mode 100644 test/files/emails/existing/alias-external.txt delete mode 100644 test/files/emails/existing/alias-local.txt delete mode 100644 test/files/emails/existing/alias-recipient-delimiter.txt delete mode 100644 test/files/emails/existing/catchall-local.txt delete mode 100644 test/files/emails/existing/regexp-alias-external.txt delete mode 100644 test/files/emails/existing/regexp-alias-local.txt delete mode 100644 test/files/emails/existing/user-and-cc-local-alias.txt delete mode 100644 test/files/emails/existing/user1.txt delete mode 100644 test/files/emails/non-existing-user.txt delete mode 100644 test/files/emails/rspamd/pass.txt delete mode 100644 test/files/emails/rspamd/spam-header.txt delete mode 100644 test/files/emails/rspamd/spam.txt delete mode 100644 test/files/emails/rspamd/virus.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 86302566..382b9efa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ All notable changes to this project will be documented in this file. The format ### Updates +- **Tests**: + - Refactored mail sending ([#3747](https://github.com/docker-mailserver/docker-mailserver/pull/3747)): + - This change is a follow-up to [#3732](https://github.com/docker-mailserver/docker-mailserver/pull/3732) from DMS v13.2. + - `swaks` version is now the latest from Github releases instead of the Debian package. + - `_nc_wrapper`, `_send_mail` and related helpers expect the `.txt` filepath extension again. + - `sending.bash` helper methods were refactored to better integrate `swaks` and accommodate different usage contexts. + - `test/files/emails/existing/` files were removed similar to previous removal of SMTP auth files as they became redundant with `swaks`. - **Internal:** - tests: Replace `wc -l` with `grep -c` ([#3752](https://github.com/docker-mailserver/docker-mailserver/pull/3752)) - Postfix is now configured with `smtputf8_enable = no` in our default `main.cf` config (_instead of during container startup_). ([#3750](https://github.com/docker-mailserver/docker-mailserver/pull/3750)) diff --git a/target/scripts/build/packages.sh b/target/scripts/build/packages.sh index 7a4b60f6..e3607f48 100644 --- a/target/scripts/build/packages.sh +++ b/target/scripts/build/packages.sh @@ -80,7 +80,7 @@ function _install_packages() { # `bind9-dnsutils` provides the `dig` command # `iputils-ping` provides the `ping` command DEBUG_PACKAGES=( - bind9-dnsutils iputils-ping less nano swaks + bind9-dnsutils iputils-ping less nano ) apt-get "${QUIET}" --no-install-recommends install \ @@ -192,7 +192,15 @@ function _install_getmail() { function _install_utils() { _log 'debug' 'Installing utils sourced from Github' + _log 'trace' 'Installing jaq' curl -sL "https://github.com/01mf02/jaq/releases/latest/download/jaq-v1.2.0-$(uname -m)-unknown-linux-gnu" -o /usr/bin/jaq && chmod +x /usr/bin/jaq + + _log 'trace' 'Installing swaks' + local SWAKS_VERSION='20240103.0' + local SWAKS_RELEASE="swaks-${SWAKS_VERSION}" + curl -sSfL "https://github.com/jetmore/swaks/releases/download/v${SWAKS_VERSION}/${SWAKS_RELEASE}.tar.gz" | tar -xz + mv "${SWAKS_RELEASE}/swaks" /usr/local/bin + rm -r "${SWAKS_RELEASE}" } function _remove_data_after_package_installations() { diff --git a/test/files/emails/existing/added.txt b/test/files/emails/existing/added.txt deleted file mode 100644 index 827b681f..00000000 --- a/test/files/emails/existing/added.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-added.txt -This is a test mail. diff --git a/test/files/emails/existing/alias-external.txt b/test/files/emails/existing/alias-external.txt deleted file mode 100644 index 03f1af6c..00000000 --- a/test/files/emails/existing/alias-external.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-alias-external.txt -This is a test mail. diff --git a/test/files/emails/existing/alias-local.txt b/test/files/emails/existing/alias-local.txt deleted file mode 100644 index 9b481a98..00000000 --- a/test/files/emails/existing/alias-local.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local Alias -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-alias-local.txt -This is a test mail. diff --git a/test/files/emails/existing/alias-recipient-delimiter.txt b/test/files/emails/existing/alias-recipient-delimiter.txt deleted file mode 100644 index 07cb8d40..00000000 --- a/test/files/emails/existing/alias-recipient-delimiter.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local Alias With Delimiter -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-alias-recipient-delimiter.txt -This is a test mail. diff --git a/test/files/emails/existing/catchall-local.txt b/test/files/emails/existing/catchall-local.txt deleted file mode 100644 index ab3e1988..00000000 --- a/test/files/emails/existing/catchall-local.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-catchall-local.txt -This is a test mail. diff --git a/test/files/emails/existing/regexp-alias-external.txt b/test/files/emails/existing/regexp-alias-external.txt deleted file mode 100644 index b50ac90f..00000000 --- a/test/files/emails/existing/regexp-alias-external.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-regexp-alias-external.txt -This is a test mail. diff --git a/test/files/emails/existing/regexp-alias-local.txt b/test/files/emails/existing/regexp-alias-local.txt deleted file mode 100644 index e45b7c6c..00000000 --- a/test/files/emails/existing/regexp-alias-local.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-regexp-alias-local.txt -This is a test mail. diff --git a/test/files/emails/existing/user-and-cc-local-alias.txt b/test/files/emails/existing/user-and-cc-local-alias.txt deleted file mode 100644 index 37814f91..00000000 --- a/test/files/emails/existing/user-and-cc-local-alias.txt +++ /dev/null @@ -1,6 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Cc: Existing Local Alias -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-user-and-cc-local-alias.txt -This is a test mail. diff --git a/test/files/emails/existing/user1.txt b/test/files/emails/existing/user1.txt deleted file mode 100644 index 23d49dc9..00000000 --- a/test/files/emails/existing/user1.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message existing-user1.txt -This is a test mail. diff --git a/test/files/emails/non-existing-user.txt b/test/files/emails/non-existing-user.txt deleted file mode 100644 index 3d92470e..00000000 --- a/test/files/emails/non-existing-user.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message non-existing-user.txt -This is a test mail. diff --git a/test/files/emails/rspamd/pass.txt b/test/files/emails/rspamd/pass.txt deleted file mode 100644 index ce9286b1..00000000 --- a/test/files/emails/rspamd/pass.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message rspamd/pass.txt -This mail should pass and Rspamd should not mark it. diff --git a/test/files/emails/rspamd/spam-header.txt b/test/files/emails/rspamd/spam-header.txt deleted file mode 100644 index 8722e42f..00000000 --- a/test/files/emails/rspamd/spam-header.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 21 Jan 2023 11:11:11 +0000 -Subject: Test Message rspamd-spam-header.txt -YJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X diff --git a/test/files/emails/rspamd/spam.txt b/test/files/emails/rspamd/spam.txt deleted file mode 100644 index c561e779..00000000 --- a/test/files/emails/rspamd/spam.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 21 Jan 2023 11:11:11 +0000 -Subject: Test Message rspamd-spam.txt -XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X diff --git a/test/files/emails/rspamd/virus.txt b/test/files/emails/rspamd/virus.txt deleted file mode 100644 index cb18927d..00000000 --- a/test/files/emails/rspamd/virus.txt +++ /dev/null @@ -1,5 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 21 Jan 2023 11:11:11 +0000 -Subject: Test Message rspamd-virus.txt -X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* diff --git a/test/helper/common.bash b/test/helper/common.bash index ab21ef60..0891bf8c 100644 --- a/test/helper/common.bash +++ b/test/helper/common.bash @@ -466,7 +466,7 @@ function _print_mail_log_for_id() { local MAIL_ID=${1:?Mail ID must be provided} local CONTAINER_NAME=$(__handle_container_name "${2:-}") - _run_in_container grep -F "${MAIL_ID}" /var/log/mail.log + _run_in_container grep -E "${MAIL_ID}" /var/log/mail.log } # A simple wrapper for netcat (`nc`). This is useful when sending @@ -480,7 +480,7 @@ function _nc_wrapper() { [[ -v CONTAINER_NAME ]] || return 1 - _run_in_container_bash "nc ${NC_PARAMETERS} < /tmp/docker-mailserver-test/${FILE}.txt" + _run_in_container_bash "nc ${NC_PARAMETERS} < /tmp/docker-mailserver-test/${FILE}" } # ? << Miscellaneous helper functions diff --git a/test/helper/sending.bash b/test/helper/sending.bash index 48012178..529e36aa 100644 --- a/test/helper/sending.bash +++ b/test/helper/sending.bash @@ -7,68 +7,118 @@ # ! ATTENTION: This file is loaded by `common.sh` - do not load it yourself! # ! ATTENTION: This file requires helper functions from `common.sh`! -# Sends a mail from localhost (127.0.0.1) to a container. To send -# a custom email, create a file at `test/files/`, -# and provide `` as an argument to this function. +# Sends an e-mail from the container named by the environment variable `CONTAINER_NAME` +# to the same or another container. # -# Parameters include all options that one can supply to `swaks` -# itself. The `--data` parameter expects a relative path from `emails/` -# where the contents will be implicitly provided to `swaks` via STDIN. +# To send a custom email, you can +# +# 1. create a file at `test/files/` and provide `` via `--data` as an argument to this function; +# 2. use this function without the `--data` argument, in which case we provide a default; +# 3. provide data inline (`--data `). +# +# The very first parameter **may** be `--expect-rejection` - use it of you expect the mail transaction to not finish +# successfully. All other (following) parameters include all options that one can supply to `swaks` itself. +# As mentioned before, the `--data` parameter expects a value of either: +# +# - A relative path from `test/files/emails/` +# - An "inline" data string (e.g., `Date: 1 Jan 2024\nSubject: This is a test`) +# +# ## Output +# +# This functions prints the output of the transaction that `swaks` prints. # # ## Attention # # This function assumes `CONTAINER_NAME` to be properly set (to the container # name the command should be executed in)! # -# This function will just send the email in an "asynchronous" fashion, i.e. it will -# send the email but it will not make sure the mail queue is empty after the mail -# has been sent. +# This function will send the email in an "asynchronous" fashion, +# it will return without waiting for the Postfix mail queue to be emptied. function _send_email() { - [[ -v CONTAINER_NAME ]] || return 1 + local RETURN_VALUE=0 + local COMMAND_STRING - # Parameter defaults common to our testing needs: - local EHLO='mail.external.tld' - local FROM='user@external.tld' - local TO='user1@localhost.localdomain' - local SERVER='0.0.0.0' - local PORT=25 - # Extra options for `swaks` that aren't covered by the default options above: - local ADDITIONAL_SWAKS_OPTIONS=() - # Specifically for handling `--data` option below: - local FINAL_SWAKS_OPTIONS=() + function __parse_arguments() { + [[ -v CONTAINER_NAME ]] || return 1 - while [[ ${#} -gt 0 ]]; do - case "${1}" in - ( '--ehlo' ) EHLO=${2:?--ehlo given but no argument} ; shift 2 ;; - ( '--from' ) FROM=${2:?--from given but no argument} ; shift 2 ;; - ( '--to' ) TO=${2:?--to given but no argument} ; shift 2 ;; - ( '--server' ) SERVER=${2:?--server given but no argument} ; shift 2 ;; - ( '--port' ) PORT=${2:?--port given but no argument} ; shift 2 ;; - ( '--data' ) - local TEMPLATE_FILE="/tmp/docker-mailserver-test/emails/${2:?--data given but no argument provided}.txt" - FINAL_SWAKS_OPTIONS+=('--data') - FINAL_SWAKS_OPTIONS+=('-') - FINAL_SWAKS_OPTIONS+=('<') - FINAL_SWAKS_OPTIONS+=("${TEMPLATE_FILE}") - shift 2 - ;; - ( * ) ADDITIONAL_SWAKS_OPTIONS+=("${1}") ; shift 1 ;; - esac - done + # Parameter defaults common to our testing needs: + local EHLO='mail.external.tld' + local FROM='user@external.tld' + local TO='user1@localhost.localdomain' + local SERVER='0.0.0.0' + local PORT=25 + # Extra options for `swaks` that aren't covered by the default options above: + local ADDITIONAL_SWAKS_OPTIONS=() + local DATA_WAS_SUPPLIED=0 - _run_in_container_bash "swaks --server ${SERVER} --port ${PORT} --ehlo ${EHLO} --from ${FROM} --to ${TO} ${ADDITIONAL_SWAKS_OPTIONS[*]} ${FINAL_SWAKS_OPTIONS[*]}" + while [[ ${#} -gt 0 ]]; do + case "${1}" in + ( '--ehlo' ) EHLO=${2:?--ehlo given but no argument} ; shift 2 ;; + ( '--from' ) FROM=${2:?--from given but no argument} ; shift 2 ;; + ( '--to' ) TO=${2:?--to given but no argument} ; shift 2 ;; + ( '--server' ) SERVER=${2:?--server given but no argument} ; shift 2 ;; + ( '--port' ) PORT=${2:?--port given but no argument} ; shift 2 ;; + ( '--data' ) + ADDITIONAL_SWAKS_OPTIONS+=('--data') + local FILE_PATH="/tmp/docker-mailserver-test/emails/${2:?--data given but no argument provided}" + if _exec_in_container_bash "[[ -e ${FILE_PATH} ]]"; then + ADDITIONAL_SWAKS_OPTIONS+=("@${FILE_PATH}") + else + ADDITIONAL_SWAKS_OPTIONS+=("'${2}'") + fi + shift 2 + DATA_WAS_SUPPLIED=1 + ;; + ( * ) ADDITIONAL_SWAKS_OPTIONS+=("'${1}'") ; shift 1 ;; + esac + done + + if [[ ${DATA_WAS_SUPPLIED} -eq 0 ]]; then + # Fallback template (without the implicit `Message-Id` + `X-Mailer` headers from swaks): + # NOTE: It is better to let Postfix generate and append the `Message-Id` header itself, + # as it will contain the Queue ID for tracking in logs (which is also returned in swaks output). + ADDITIONAL_SWAKS_OPTIONS+=('--data') + ADDITIONAL_SWAKS_OPTIONS+=("'Date: %DATE%\nTo: %TO_ADDRESS%\nFrom: %FROM_ADDRESS%\nSubject: test %DATE%\n%NEW_HEADERS%\n%BODY%\n'") + fi + + echo "swaks --server '${SERVER}' --port '${PORT}' --ehlo '${EHLO}' --from '${FROM}' --to '${TO}' ${ADDITIONAL_SWAKS_OPTIONS[*]}" + } + + if [[ ${1:-} == --expect-rejection ]]; then + shift 1 + COMMAND_STRING=$(__parse_arguments "${@}") + _run_in_container_bash "${COMMAND_STRING}" + RETURN_VALUE=${?} + else + COMMAND_STRING=$(__parse_arguments "${@}") + _run_in_container_bash "${COMMAND_STRING}" + assert_success + fi + + # shellcheck disable=SC2154 + echo "${output}" + return "${RETURN_VALUE}" } # Like `_send_email` with two major differences: # # 1. this function waits for the mail to be processed; there is no asynchronicity -# because filtering the logs in a synchronous way is easier and safer! -# 2. this function prints an ID one can later filter by to check logs +# because filtering the logs in a synchronous way is easier and safer; +# 2. this function takes the name of a variable and inserts IDs one can later +# filter by to check logs. # # No. 2 is especially useful in case you send more than one email in a single # test file and need to assert certain log entries for each mail individually. # -# This function takes the same arguments as `_send_mail`. +# The first argument has to be the name of the variable that the e-mail ID is stored in. +# The second argument **can** be the flag `--expect-rejection`. +# +# - If this flag is supplied, the function does not check whether the whole mail delivery +# transaction was successful. Additionally the queue ID will be retrieved differently. +# - CAUTION: It must still be possible to `grep` for the Message-ID that Postfix +# generated in the mail log; otherwise this function fails. +# +# The rest of the arguments are the same as `_send_email`. # # ## Attention # @@ -82,20 +132,35 @@ function _send_email() { # chosen. Sending more than one mail at any given point in time with this function # is UNDEFINED BEHAVIOR! function _send_email_and_get_id() { - [[ -v CONTAINER_NAME ]] || return 1 + # Export the variable denoted by ${1} so everyone has access + export "${1:?Mail ID must be set for _send_email_and_get_id}" + # Get a "reference" to the content of the variable denoted by ${1} so we can manipulate the content + local -n ID_ENV_VAR_REF=${1:?} + # Prepare the message ID header here because we will shift away ${1} later + local MID="<${1}@dms-tests>" + # Get rid of ${1} so only the arguments for swaks remain + shift 1 - _wait_for_empty_mail_queue_in_container - _send_email "${@}" - _wait_for_empty_mail_queue_in_container - - local MAIL_ID + local QUEUE_ID # The unique ID Postfix (and other services) use may be different in length # on different systems (e.g. amd64 (11) vs aarch64 (10)). Hence, we use a # range to safely capture it. - MAIL_ID=$(_exec_in_container tac /var/log/mail.log \ - | grep -E -m 1 'postfix/smtpd.*: [A-Z0-9]+: client=localhost' \ - | grep -E -o '[A-Z0-9]{9,12}' || true) + local QUEUE_ID_REGEX='[A-Z0-9]{9,12}' - assert_not_equal "${MAIL_ID}" '' - echo "${MAIL_ID}" + _wait_for_empty_mail_queue_in_container + local OUTPUT=$(_send_email "${@}" --header "Message-Id: ${MID}") + _wait_for_empty_mail_queue_in_container + + # We store Postfix's queue ID first + ID_ENV_VAR_REF=$(_exec_in_container tac /var/log/mail.log \ + | grep -E "postfix/cleanup.*: ${QUEUE_ID_REGEX}:.*message-id=${MID}" \ + | grep -E --only-matching --max-count 1 "${QUEUE_ID_REGEX}" || :) + # But we also requre potential Dovecot sieve output, which requires the mesage ID, + # so we need to provide the message ID too. + ID_ENV_VAR_REF+="|${MID}" + + # Last but not least, we perform plausibility checks on the IDs. + assert_not_equal "${ID_ENV_VAR_REF}" '' + run echo "${ID_ENV_VAR_REF}" + assert_line --regexp "^${QUEUE_ID_REGEX}\|${MID}$" } diff --git a/test/tests/parallel/set1/dovecot/dovecot_quotas.bats b/test/tests/parallel/set1/dovecot/dovecot_quotas.bats index 81cf9bc1..8e71bf73 100644 --- a/test/tests/parallel/set1/dovecot/dovecot_quotas.bats +++ b/test/tests/parallel/set1/dovecot/dovecot_quotas.bats @@ -225,12 +225,9 @@ function teardown_file() { _default_teardown ; } sleep 10 # send some big emails - _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded' - assert_success - _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded' - assert_success - _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded' - assert_success + _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded.txt' + _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded.txt' + _send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded.txt' # check for quota warn message existence run _repeat_until_success_or_timeout 20 _exec_in_container grep -R 'Subject: quota warning' /var/mail/otherdomain.tld/quotauser/new/ assert_success diff --git a/test/tests/parallel/set1/dovecot/dovecot_sieve.bats b/test/tests/parallel/set1/dovecot/dovecot_sieve.bats index e3e076a5..6acbc4a5 100644 --- a/test/tests/parallel/set1/dovecot/dovecot_sieve.bats +++ b/test/tests/parallel/set1/dovecot/dovecot_sieve.bats @@ -26,11 +26,9 @@ function setup_file() { _wait_for_smtp_port_in_container # Single mail sent from 'spam@spam.com' that is handled by User (relocate) and Global (copy) sieves for user1: - _send_email --data 'sieve/spam-folder' - assert_success + _send_email --data 'sieve/spam-folder.txt' # Mail for user2 triggers the sieve-pipe: - _send_email --to 'user2@otherdomain.tld' --data 'sieve/pipe' - assert_success + _send_email --to 'user2@otherdomain.tld' --data 'sieve/pipe.txt' _wait_for_empty_mail_queue_in_container } diff --git a/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats b/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats index 033a5bde..b9f6d8f6 100644 --- a/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats +++ b/test/tests/parallel/set1/dovecot/mailbox_format_dbox.bats @@ -26,8 +26,7 @@ function teardown() { _default_teardown ; } _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' _wait_for_smtp_port_in_container - _send_email --data 'existing/user1' - assert_success + _send_email _wait_for_empty_mail_queue_in_container # Mail received should be stored as `u.1` (one file per message) @@ -48,8 +47,7 @@ function teardown() { _default_teardown ; } _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' _wait_for_smtp_port_in_container - _send_email --data 'existing/user1' - assert_success + _send_email _wait_for_empty_mail_queue_in_container # Mail received should be stored in `m.1` (1 or more messages) diff --git a/test/tests/parallel/set1/dovecot/special_use_folders.bats b/test/tests/parallel/set1/dovecot/special_use_folders.bats index fe1f554e..4965b844 100644 --- a/test/tests/parallel/set1/dovecot/special_use_folders.bats +++ b/test/tests/parallel/set1/dovecot/special_use_folders.bats @@ -14,8 +14,7 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test 'normal delivery works' { - _send_email --data 'existing/user1' - assert_success + _send_email _count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new 1 } @@ -27,7 +26,7 @@ function teardown_file() { _default_teardown ; } } @test "(IMAP) special-use folders should be created when necessary" { - _nc_wrapper 'nc/imap_special_use_folders' '-w 8 0.0.0.0 143' + _nc_wrapper 'nc/imap_special_use_folders.txt' '-w 8 0.0.0.0 143' assert_output --partial 'Drafts' assert_output --partial 'Junk' assert_output --partial 'Trash' diff --git a/test/tests/parallel/set1/spam_virus/clamav.bats b/test/tests/parallel/set1/spam_virus/clamav.bats index 9c035f5b..a916896a 100644 --- a/test/tests/parallel/set1/spam_virus/clamav.bats +++ b/test/tests/parallel/set1/spam_virus/clamav.bats @@ -25,8 +25,7 @@ function setup_file() { _wait_for_service postfix _wait_for_smtp_port_in_container - _send_email --from 'virus@external.tld' --data 'amavis/virus' - assert_success + _send_email --from 'virus@external.tld' --data 'amavis/virus.txt' _wait_for_empty_mail_queue_in_container } diff --git a/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats b/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats index f2474cc0..5ec28396 100644 --- a/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats +++ b/test/tests/parallel/set1/spam_virus/disabled_clamav_spamassassin.bats @@ -18,8 +18,7 @@ function setup_file() { _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' _wait_for_smtp_port_in_container - _send_email --data 'existing/user1' - assert_success + _send_email _wait_for_empty_mail_queue_in_container } diff --git a/test/tests/parallel/set1/spam_virus/fail2ban.bats b/test/tests/parallel/set1/spam_virus/fail2ban.bats index 8a03ba04..f451befd 100644 --- a/test/tests/parallel/set1/spam_virus/fail2ban.bats +++ b/test/tests/parallel/set1/spam_virus/fail2ban.bats @@ -74,7 +74,7 @@ function teardown_file() { CONTAINER1_IP=$(_get_container_ip "${CONTAINER1_NAME}") # Trigger a ban by failing to login twice: for _ in {1..2}; do - CONTAINER_NAME=${CONTAINER2_NAME} _send_email \ + CONTAINER_NAME=${CONTAINER2_NAME} _send_email --expect-rejection \ --server "${CONTAINER1_IP}" \ --port 465 \ --auth PLAIN \ diff --git a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats index 389fc183..5538d3bf 100644 --- a/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats +++ b/test/tests/parallel/set1/spam_virus/postgrey_enabled.bats @@ -51,7 +51,7 @@ function teardown_file() { _default_teardown ; } _reload_postfix # Send test mail (it should fail to deliver): - _send_email --from 'user@external.tld' --port 25 --data 'postgrey' + _send_email --expect-rejection --from 'user@external.tld' --port 25 --data 'postgrey.txt' assert_failure assert_output --partial 'Recipient address rejected: Delayed by Postgrey' @@ -67,8 +67,7 @@ function teardown_file() { _default_teardown ; } # Wait until `$POSTGREY_DELAY` seconds pass before trying again: sleep 3 # Retry delivering test mail (it should be trusted this time): - _send_email --from 'user@external.tld' --port 25 --data 'postgrey' - assert_success + _send_email --from 'user@external.tld' --port 25 --data 'postgrey.txt' # Confirm postgrey permitted delivery (triplet is now trusted): _should_have_log_entry \ @@ -87,7 +86,7 @@ function teardown_file() { _default_teardown ; } # - It'd also cause the earlier greylist test to fail. # - TODO: Actually confirm whitelist feature works correctly as these test cases are using a workaround: @test "should whitelist sender 'user@whitelist.tld'" { - _nc_wrapper 'nc/postgrey_whitelist' '-w 0 0.0.0.0 10023' + _nc_wrapper 'nc/postgrey_whitelist.txt' '-w 0 0.0.0.0 10023' _should_have_log_entry \ 'action=pass' \ @@ -96,7 +95,7 @@ function teardown_file() { _default_teardown ; } } @test "should whitelist recipient 'user2@otherdomain.tld'" { - _nc_wrapper 'nc/postgrey_whitelist_recipients' '-w 0 0.0.0.0 10023' + _nc_wrapper 'nc/postgrey_whitelist_recipients.txt' '-w 0 0.0.0.0 10023' _should_have_log_entry \ 'action=pass' \ diff --git a/test/tests/parallel/set1/spam_virus/postscreen.bats b/test/tests/parallel/set1/spam_virus/postscreen.bats index 377b2479..92333f98 100644 --- a/test/tests/parallel/set1/spam_virus/postscreen.bats +++ b/test/tests/parallel/set1/spam_virus/postscreen.bats @@ -44,7 +44,7 @@ function teardown_file() { # Use `nc` to send all SMTP commands at once instead (emulate a misbehaving client that should be rejected) # NOTE: Postscreen only runs on port 25, avoid implicit ports in test methods @test 'should fail send when talking out of turn' { - CONTAINER_NAME=${CONTAINER2_NAME} _nc_wrapper 'emails/nc_raw/postscreen' "${CONTAINER1_IP} 25" + CONTAINER_NAME=${CONTAINER2_NAME} _nc_wrapper 'emails/nc_raw/postscreen.txt' "${CONTAINER1_IP} 25" # Expected postscreen log entry: assert_output --partial 'Protocol error' @@ -56,14 +56,10 @@ function teardown_file() { @test "should successfully pass postscreen and get postfix greeting message (respecting postscreen_greet_wait time)" { # Configure `send_email()` to send from the mail client container (CONTAINER2_NAME) via ENV override, # mail is sent to the DMS server container (CONTAINER1_NAME) via `--server` parameter: - CONTAINER_NAME=${CONTAINER2_NAME} _send_email --server "${CONTAINER1_IP}" --port 25 --data 'postscreen' - # NOTE: Cannot assert_success due to sender address not being resolvable. - # TODO: Uncomment when proper resolution of domain names is possible: - # assert_success - - # TODO: Prefer this approach when `_send_email_and_get_id()` can support separate client and server containers: - # local MAIL_ID=$(_send_email_and_get_id --port 25 --data 'postscreen') - # _print_mail_log_for_id "${MAIL_ID}" + # TODO: Use _send_email_and_get_id when proper resolution of domain names is possible: + CONTAINER_NAME=${CONTAINER2_NAME} _send_email --expect-rejection --server "${CONTAINER1_IP}" --port 25 --data 'postscreen.txt' + # CONTAINER_NAME=${CONTAINER2_NAME} _send_email_and_get_id MAIL_ID_POSTSCREEN --server "${CONTAINER1_IP}" --data 'postscreen.txt' + # _print_mail_log_for_id "${MAIL_ID_POSTSCREEN}" # assert_output --partial "stored mail into mailbox 'INBOX'" _run_in_container cat /var/log/mail.log diff --git a/test/tests/parallel/set1/spam_virus/rspamd_full.bats b/test/tests/parallel/set1/spam_virus/rspamd_full.bats index 2e610d72..b83ac353 100644 --- a/test/tests/parallel/set1/spam_virus/rspamd_full.bats +++ b/test/tests/parallel/set1/spam_virus/rspamd_full.bats @@ -43,16 +43,24 @@ function setup_file() { _wait_for_service postfix _wait_for_smtp_port_in_container - # We will send 3 emails: the first one should pass just fine; the second one should - # be rejected due to spam; the third one should be rejected due to a virus. - export MAIL_ID1=$(_send_email_and_get_id --from 'rspamd-pass@example.test' --data 'rspamd/pass') - export MAIL_ID2=$(_send_email_and_get_id --from 'rspamd-spam@example.test' --data 'rspamd/spam') - export MAIL_ID3=$(_send_email_and_get_id --from 'rspamd-virus@example.test' --data 'rspamd/virus') - export MAIL_ID4=$(_send_email_and_get_id --from 'rspamd-spam-header@example.test' --data 'rspamd/spam-header') + # ref: https://rspamd.com/doc/gtube_patterns.html + local GTUBE_SUFFIX='*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X' - for ID in MAIL_ID{1,2,3,4}; do - [[ -n ${!ID} ]] || { echo "${ID} is empty - aborting!" ; return 1 ; } - done + # We will send 4 emails: + # 1. The first one should pass just fine + _send_email_and_get_id MAIL_ID_PASS + # 2. The second one should be rejected (GTUBE pattern) + _send_email_and_get_id MAIL_ID_REJECT --expect-rejection --body "XJS${GTUBE_SUFFIX}" + # 3. The third one should be rejected due to a virus (ClamAV EICAR pattern) + # shellcheck disable=SC2016 + _send_email_and_get_id MAIL_ID_VIRUS --expect-rejection \ + --body 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' + # 4. The fourth one will receive an added header (GTUBE pattern) + _send_email_and_get_id MAIL_ID_HEADER --body "YJS${GTUBE_SUFFIX}" + + _run_in_container cat /var/log/mail.log + assert_success + refute_output --partial 'inet:localhost:11332: Connection refused' } function teardown_file() { _default_teardown ; } @@ -104,7 +112,7 @@ function teardown_file() { _default_teardown ; } @test 'normal mail passes fine' { _service_log_should_contain_string 'rspamd' 'F \(no action\)' - _print_mail_log_for_id "${MAIL_ID1}" + _print_mail_log_for_id "${MAIL_ID_PASS}" assert_output --partial "stored mail into mailbox 'INBOX'" _count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new/ 1 @@ -114,7 +122,7 @@ function teardown_file() { _default_teardown ; } _service_log_should_contain_string 'rspamd' 'S \(reject\)' _service_log_should_contain_string 'rspamd' 'reject "Gtube pattern"' - _print_mail_log_for_id "${MAIL_ID2}" + _print_mail_log_for_id "${MAIL_ID_REJECT}" assert_output --partial 'milter-reject' assert_output --partial '5.7.1 Gtube pattern' @@ -125,7 +133,7 @@ function teardown_file() { _default_teardown ; } _service_log_should_contain_string 'rspamd' 'T \(reject\)' _service_log_should_contain_string 'rspamd' 'reject "ClamAV FOUND VIRUS "Eicar-Signature"' - _print_mail_log_for_id "${MAIL_ID3}" + _print_mail_log_for_id "${MAIL_ID_VIRUS}" assert_output --partial 'milter-reject' assert_output --partial '5.7.1 ClamAV FOUND VIRUS "Eicar-Signature"' refute_output --partial "stored mail into mailbox 'INBOX'" @@ -214,7 +222,7 @@ function teardown_file() { _default_teardown ; } _service_log_should_contain_string 'rspamd' 'S \(add header\)' _service_log_should_contain_string 'rspamd' 'add header "Gtube pattern"' - _print_mail_log_for_id "${MAIL_ID4}" + _print_mail_log_for_id "${MAIL_ID_HEADER}" assert_output --partial "fileinto action: stored mail into mailbox 'Junk'" _count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new/ 1 @@ -256,7 +264,7 @@ function teardown_file() { _default_teardown ; } # Move an email to the "Junk" folder from "INBOX"; the first email we # sent should pass fine, hence we can now move it. - _nc_wrapper 'nc/rspamd_imap_move_to_junk' '0.0.0.0 143' + _nc_wrapper 'nc/rspamd_imap_move_to_junk.txt' '0.0.0.0 143' sleep 1 # wait for the transaction to finish _run_in_container cat /var/log/mail/mail.log @@ -270,7 +278,7 @@ function teardown_file() { _default_teardown ; } # Move an email to the "INBOX" folder from "Junk"; there should be two mails # in the "Junk" folder, since the second email we sent during setup should # have landed in the Junk folder already. - _nc_wrapper 'nc/rspamd_imap_move_to_inbox' '0.0.0.0 143' + _nc_wrapper 'nc/rspamd_imap_move_to_inbox.txt' '0.0.0.0 143' sleep 1 # wait for the transaction to finish _run_in_container cat /var/log/mail/mail.log diff --git a/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats b/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats index fea23b0b..2c3a522d 100644 --- a/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats +++ b/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats @@ -95,7 +95,7 @@ function teardown() { _default_teardown ; } function _should_send_spam_message() { _wait_for_smtp_port_in_container _wait_for_tcp_port_in_container 10024 # port 10024 is for Amavis - _send_email --from 'spam@external.tld' --data 'amavis/spam' + _send_email --from 'spam@external.tld' --data 'amavis/spam.txt' } function _should_be_received_by_amavis() { diff --git a/test/tests/parallel/set3/container_configuration/hostname.bats b/test/tests/parallel/set3/container_configuration/hostname.bats index f5774eef..a525ecb2 100644 --- a/test/tests/parallel/set3/container_configuration/hostname.bats +++ b/test/tests/parallel/set3/container_configuration/hostname.bats @@ -207,7 +207,7 @@ function _should_have_correct_mail_headers() { # (eg: OVERRIDE_HOSTNAME or `--hostname mail --domainname example.test`) local EXPECTED_HOSTNAME=${3:-${EXPECTED_FQDN}} - _send_email --from 'user@external.tld' --data 'existing/user1' + _send_email --from 'user@external.tld' _wait_for_empty_mail_queue_in_container _count_files_in_directory_in_container '/var/mail/localhost.localdomain/user1/new/' '1' diff --git a/test/tests/parallel/set3/mta/dsn.bats b/test/tests/parallel/set3/mta/dsn.bats index a5228cfc..9a65b147 100644 --- a/test/tests/parallel/set3/mta/dsn.bats +++ b/test/tests/parallel/set3/mta/dsn.bats @@ -49,9 +49,9 @@ function teardown_file() { # TODO replace with _send_email as soon as it supports DSN # TODO ref: https://github.com/jetmore/swaks/issues/41 - _nc_wrapper 'emails/nc_raw/dsn/unauthenticated' - _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465' - _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587' + _nc_wrapper 'emails/nc_raw/dsn/unauthenticated.txt' + _nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 465' + _nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 587' _wait_for_empty_mail_queue_in_container _run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log @@ -62,7 +62,7 @@ function teardown_file() { @test "should only send a DSN when requested from ports 465/587" { export CONTAINER_NAME=${CONTAINER2_NAME} - _nc_wrapper 'emails/nc_raw/dsn/unauthenticated' + _nc_wrapper 'emails/nc_raw/dsn/unauthenticated.txt' _wait_for_empty_mail_queue_in_container # DSN requests can now only be made on ports 465 and 587, @@ -74,8 +74,8 @@ function teardown_file() { assert_failure # These ports are excluded via master.cf. - _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465' - _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587' + _nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 465' + _nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 587' _wait_for_empty_mail_queue_in_container _run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log @@ -85,9 +85,9 @@ function teardown_file() { @test "should never send a DSN" { export CONTAINER_NAME=${CONTAINER3_NAME} - _nc_wrapper 'emails/nc_raw/dsn/unauthenticated' - _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465' - _nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587' + _nc_wrapper 'emails/nc_raw/dsn/unauthenticated.txt' + _nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 465' + _nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 587' _wait_for_empty_mail_queue_in_container # DSN requests are rejected regardless of origin. diff --git a/test/tests/parallel/set3/mta/lmtp_ip.bats b/test/tests/parallel/set3/mta/lmtp_ip.bats index d8be42d9..201cb237 100644 --- a/test/tests/parallel/set3/mta/lmtp_ip.bats +++ b/test/tests/parallel/set3/mta/lmtp_ip.bats @@ -38,7 +38,7 @@ function teardown_file() { _default_teardown ; } @test "delivers mail to existing account" { _wait_for_smtp_port_in_container - _send_email --data 'existing/user1' # send a test email + _send_email # Verify delivery was successful, log line should look similar to: # postfix/lmtp[1274]: 0EA424ABE7D9: to=, relay=127.0.0.1[127.0.0.1]:24, delay=0.13, delays=0.07/0.01/0.01/0.05, dsn=2.0.0, status=sent (250 2.0.0 ixPpB+Zvv2P7BAAAUi6ngw Saved) diff --git a/test/tests/parallel/set3/mta/privacy.bats b/test/tests/parallel/set3/mta/privacy.bats index 4d4d82ba..614e2e87 100644 --- a/test/tests/parallel/set3/mta/privacy.bats +++ b/test/tests/parallel/set3/mta/privacy.bats @@ -26,11 +26,10 @@ function teardown_file() { _default_teardown ; } # this test covers https://github.com/docker-mailserver/docker-mailserver/issues/681 @test "(Postfix) remove privacy details of the sender" { _send_email \ - --port 587 -tls --auth LOGIN \ + --port 587 -tls --auth PLAIN \ --auth-user user1@localhost.localdomain \ --auth-password mypassword \ - --data 'privacy' - assert_success + --data 'privacy.txt' _run_until_success_or_timeout 120 _exec_in_container_bash '[[ -d /var/mail/localhost.localdomain/user1/new ]]' assert_success diff --git a/test/tests/parallel/set3/mta/smtp_delivery.bats b/test/tests/parallel/set3/mta/smtp_delivery.bats index f87f11ed..329f36b2 100644 --- a/test/tests/parallel/set3/mta/smtp_delivery.bats +++ b/test/tests/parallel/set3/mta/smtp_delivery.bats @@ -63,46 +63,43 @@ function setup_file() { # TODO: Move to clamav tests (For use when ClamAV is enabled): # _repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /var/run/clamav/clamd.ctl - # _send_email --from 'virus@external.tld' --data 'amavis/virus' + # _send_email --from 'virus@external.tld' --data 'amavis/virus.txt' # Required for 'delivers mail to existing alias': - _send_email --to alias1@localhost.localdomain --data 'existing/alias-external' + _send_email --to alias1@localhost.localdomain --header "Subject: Test Message existing-alias-external" # Required for 'delivers mail to existing alias with recipient delimiter': - _send_email --to alias1~test@localhost.localdomain --data 'existing/alias-recipient-delimiter' + _send_email --to alias1~test@localhost.localdomain --header 'Subject: Test Message existing-alias-recipient-delimiter' # Required for 'delivers mail to existing catchall': - _send_email --to wildcard@localdomain2.com --data 'existing/catchall-local' + _send_email --to wildcard@localdomain2.com --header 'Subject: Test Message existing-catchall-local' # Required for 'delivers mail to regexp alias': - _send_email --to test123@localhost.localdomain --data 'existing/regexp-alias-local' + _send_email --to test123@localhost.localdomain --header 'Subject: Test Message existing-regexp-alias-local' # Required for 'rejects mail to unknown user': - _send_email --to nouser@localhost.localdomain --data 'non-existing-user' + _send_email --expect-rejection --to nouser@localhost.localdomain + assert_failure # Required for 'redirects mail to external aliases': - _send_email --to bounce-always@localhost.localdomain --data 'existing/regexp-alias-external' - _send_email --to alias2@localhost.localdomain --data 'existing/alias-local' + _send_email --to bounce-always@localhost.localdomain + _send_email --to alias2@localhost.localdomain # Required for 'rejects spam': - _send_email --from 'spam@external.tld' --data 'amavis/spam' + _send_email --from 'spam@external.tld' --data 'amavis/spam.txt' # Required for 'delivers mail to existing account': - _send_email --data 'existing/user1' - assert_success + _send_email --header 'Subject: Test Message existing-user1' _send_email --to user2@otherdomain.tld - assert_success _send_email --to user3@localhost.localdomain - assert_success - _send_email --to added@localhost.localdomain --data 'existing/added' - assert_success - _send_email --to user1@localhost.localdomain --data 'existing/user-and-cc-local-alias' - assert_success - _send_email --data 'sieve/spam-folder' - assert_success - _send_email --to user2@otherdomain.tld --data 'sieve/pipe' - assert_success + _send_email --to added@localhost.localdomain --header 'Subject: Test Message existing-added' + _send_email \ + --to user1@localhost.localdomain \ + --header 'Subject: Test Message existing-user-and-cc-local-alias' \ + --cc 'alias2@localhost.localdomain' + _send_email --data 'sieve/spam-folder.txt' + _send_email --to user2@otherdomain.tld --data 'sieve/pipe.txt' _run_in_container_bash 'sendmail root < /tmp/docker-mailserver-test/emails/sendmail/root-email.txt' assert_success } function _unsuccessful() { - _send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password wrongpassword + _send_email --expect-rejection --port 465 --auth "${1}" --auth-user "${2}" --auth-password wrongpassword --quit-after AUTH assert_failure assert_output --partial 'authentication failed' assert_output --partial 'No authentication type succeeded' @@ -110,7 +107,6 @@ function _unsuccessful() { function _successful() { _send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password mypassword --quit-after AUTH - assert_success assert_output --partial 'Authentication successful' } diff --git a/test/tests/serial/mail_pop3.bats b/test/tests/serial/mail_pop3.bats index 008921e4..d815e1ee 100644 --- a/test/tests/serial/mail_pop3.bats +++ b/test/tests/serial/mail_pop3.bats @@ -24,12 +24,12 @@ function teardown_file() { _default_teardown ; } } @test 'authentication works' { - _nc_wrapper 'auth/pop3-auth' '-w 1 0.0.0.0 110' + _nc_wrapper 'auth/pop3-auth.txt' '-w 1 0.0.0.0 110' assert_success } @test 'added user authentication works' { - _nc_wrapper 'auth/added-pop3-auth' '-w 1 0.0.0.0 110' + _nc_wrapper 'auth/added-pop3-auth.txt' '-w 1 0.0.0.0 110' assert_success } diff --git a/test/tests/serial/mail_with_imap.bats b/test/tests/serial/mail_with_imap.bats index eeccf888..94f1d519 100644 --- a/test/tests/serial/mail_with_imap.bats +++ b/test/tests/serial/mail_with_imap.bats @@ -21,7 +21,7 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test '(Dovecot) LDAP RIMAP connection and authentication works' { - _nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/imap-auth.txt' '-w 1 0.0.0.0 143' assert_success } @@ -31,8 +31,8 @@ function teardown_file() { _default_teardown ; } } @test '(SASLauthd) RIMAP SMTP authentication works' { - _send_email \ - --auth LOGIN \ + _send_email --expect-rejection \ + --auth PLAIN \ --auth-user user1@localhost.localdomain \ --auth-password mypassword \ --quit-after AUTH @@ -41,20 +41,18 @@ function teardown_file() { _default_teardown ; } _send_email \ --port 465 \ - --auth LOGIN \ + --auth PLAIN \ --auth-user user1@localhost.localdomain \ --auth-password mypassword \ --quit-after AUTH - assert_success assert_output --partial 'Authentication successful' _send_email \ --port 587 \ - --auth LOGIN \ + --auth PLAIN \ --auth-user user1@localhost.localdomain \ --auth-password mypassword \ --quit-after AUTH - assert_success assert_output --partial 'Authentication successful' } diff --git a/test/tests/serial/mail_with_ldap.bats b/test/tests/serial/mail_with_ldap.bats index f2011d22..f3ee1cc1 100644 --- a/test/tests/serial/mail_with_ldap.bats +++ b/test/tests/serial/mail_with_ldap.bats @@ -248,7 +248,7 @@ function teardown() { # dovecot @test "dovecot: ldap imap connection and authentication works" { - _nc_wrapper 'auth/imap-ldap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/imap-ldap-auth.txt' '-w 1 0.0.0.0 143' assert_success } @@ -326,25 +326,26 @@ function teardown() { @test "spoofing (with LDAP): rejects sender forging" { _wait_for_smtp_port_in_container_to_respond dms-test_ldap - _send_email \ - --port 465 -tlsc --auth LOGIN \ + _send_email --expect-rejection \ + --port 465 -tlsc --auth PLAIN \ --auth-user some.user@localhost.localdomain \ --auth-password secret \ --ehlo mail \ --from ldap@localhost.localdomain \ - --data 'auth/ldap-smtp-auth-spoofed' + --data 'auth/ldap-smtp-auth-spoofed.txt' + assert_failure assert_output --partial 'Sender address rejected: not owned by user' } @test "spoofing (with LDAP): accepts sending as alias" { _send_email \ - --port 465 -tlsc --auth LOGIN \ + --port 465 -tlsc --auth PLAIN \ --auth-user some.user@localhost.localdomain \ --auth-password secret \ --ehlo mail \ --from postmaster@localhost.localdomain \ --to some.user@localhost.localdomain \ - --data 'auth/ldap-smtp-auth-spoofed-alias' + --data 'auth/ldap-smtp-auth-spoofed-alias.txt' assert_output --partial 'End data with' } @@ -353,20 +354,21 @@ function teardown() { # Template used has invalid AUTH: https://github.com/docker-mailserver/docker-mailserver/pull/3006#discussion_r1073321432 skip 'TODO: This test seems to have been broken from the start (?)' - _send_email \ - --port 465 -tlsc --auth LOGIN \ + _send_email --expect-rejection \ + --port 465 -tlsc --auth PLAIN \ --auth-user some.user.email@localhost.localdomain \ --auth-password secret \ --ehlo mail \ --from randomspoofedaddress@localhost.localdomain \ --to some.user@localhost.localdomain \ - --data 'auth/ldap-smtp-auth-spoofed-sender-with-filter-exception' + --data 'auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt' + assert_failure assert_output --partial 'Sender address rejected: not owned by user' } @test "saslauthd: ldap smtp authentication" { - _send_email \ - --auth LOGIN \ + _send_email --expect-rejection \ + --auth PLAIN \ --auth-user some.user@localhost.localdomain \ --auth-password wrongpassword \ --quit-after AUTH @@ -379,12 +381,11 @@ function teardown() { --auth-user some.user@localhost.localdomain \ --auth-password secret \ --quit-after AUTH - assert_success assert_output --partial 'Authentication successful' _send_email \ --port 587 -tls \ - --auth LOGIN \ + --auth PLAIN \ --auth-user some.user@localhost.localdomain \ --auth-password secret \ --quit-after AUTH diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index 752e325e..a344bd8d 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -80,12 +80,12 @@ function teardown_file() { _default_teardown ; } } @test "imap: authentication works" { - _nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/imap-auth.txt' '-w 1 0.0.0.0 143' assert_success } @test "imap: added user authentication works" { - _nc_wrapper 'auth/added-imap-auth' '-w 1 0.0.0.0 143' + _nc_wrapper 'auth/added-imap-auth.txt' '-w 1 0.0.0.0 143' assert_success } @@ -293,13 +293,13 @@ EOF # 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 \ - --port 465 -tlsc --auth LOGIN \ + _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' + --data 'auth/added-smtp-auth-spoofed.txt' assert_output --partial 'Sender address rejected: not owned by user' } @@ -310,12 +310,12 @@ EOF # to each table. Address is authorized when a result that maps to # the DMS account is returned. _send_email \ - --port 465 -tlsc --auth LOGIN \ + --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' + --data 'auth/added-smtp-auth-spoofed-alias.txt' assert_success assert_output --partial 'End data with' } diff --git a/test/tests/serial/vmail-id.bats b/test/tests/serial/vmail-id.bats index 0f54ea96..2541f4f8 100644 --- a/test/tests/serial/vmail-id.bats +++ b/test/tests/serial/vmail-id.bats @@ -20,7 +20,7 @@ function setup_file() { function teardown_file() { _default_teardown ; } @test 'should successfully deliver mail' { - _send_email --data 'existing/user1' + _send_email --header 'Subject: Test Message existing-user1' _wait_for_empty_mail_queue_in_container # Should be successfully sent (received) by Postfix: @@ -31,7 +31,7 @@ function teardown_file() { _default_teardown ; } # Verify successful delivery via Dovecot to `/var/mail` account by searching for the subject: _repeat_in_container_until_success_or_timeout 20 "${CONTAINER_NAME}" grep -R \ - 'Subject: Test Message existing-user1.txt' \ + 'Subject: Test Message existing-user1' \ '/var/mail/localhost.localdomain/user1/new/' assert_success _should_output_number_of_lines 1 From 52c4582f7b431e69fd8eae67b97bbb330614d0b5 Mon Sep 17 00:00:00 2001 From: Keval Kapdee Date: Fri, 12 Jan 2024 20:45:14 +0000 Subject: [PATCH 29/34] feat: Auth - OAuth2 (Dovecot PassDB) (#3480) Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- .gitignore | 1 + CHANGELOG.md | 7 +++ Dockerfile | 7 +++ README.md | 1 + docs/content/config/advanced/auth-oauth2.md | 69 +++++++++++++++++++++ docs/content/config/environment.md | 24 ++++++- docs/mkdocs.yml | 1 + mailserver.env | 12 ++++ target/dovecot/10-auth.conf | 1 + target/dovecot/auth-oauth2.conf.ext | 7 +++ target/dovecot/dovecot-oauth2.conf.ext | 4 ++ target/scripts/start-mailserver.sh | 5 ++ target/scripts/startup/setup.d/oauth2.sh | 11 ++++ target/scripts/startup/variables-stack.sh | 6 ++ test/config/oauth2/provider.py | 56 +++++++++++++++++ test/files/auth/imap-oauth2-auth.txt | 4 ++ test/tests/serial/mail_with_oauth2.bats | 66 ++++++++++++++++++++ 17 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 docs/content/config/advanced/auth-oauth2.md create mode 100644 target/dovecot/auth-oauth2.conf.ext create mode 100644 target/dovecot/dovecot-oauth2.conf.ext create mode 100644 target/scripts/startup/setup.d/oauth2.sh create mode 100644 test/config/oauth2/provider.py create mode 100644 test/files/auth/imap-oauth2-auth.txt create mode 100644 test/tests/serial/mail_with_oauth2.bats diff --git a/.gitignore b/.gitignore index 50d22a22..79a4dc3c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ ################################################# .env +compose.override.yaml docs/site/ docker-data/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 382b9efa..e3881941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file. The format > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +### Features + + - **Authentication with OIDC / OAuth 2.0** 🎉 + - DMS now supports authentication via OAuth2 (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) from capable services (_like Roundcube_). + - This does not replace the need for an `ACCOUNT_PROVISIONER` (`FILE` / `LDAP`), which is required for an account to receive or send mail. + - Successful authentication (_via Dovecot PassDB_) still requires an existing account (_lookup via Dovecot UserDB_). + ### Updates - **Tests**: diff --git a/Dockerfile b/Dockerfile index 65d818ab..2118622d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -108,6 +108,13 @@ EOF COPY target/rspamd/local.d/ /etc/rspamd/local.d/ COPY target/rspamd/scores.d/* /etc/rspamd/scores.d/ +# ----------------------------------------------- +# --- OAUTH2 ------------------------------------ +# ----------------------------------------------- + +COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d +COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot + # ----------------------------------------------- # --- LDAP & SpamAssassin's Cron ---------------- # ----------------------------------------------- diff --git a/README.md b/README.md index 8918c2e9..e81d21ad 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,4 @@ If you have issues, please search through [the documentation][documentation::web - Support for [LetsEncrypt](https://letsencrypt.org/), manual and self-signed certificates - A [setup script](https://docker-mailserver.github.io/docker-mailserver/latest/config/setup.sh) for easy configuration and maintenance - SASLauthd with LDAP authentication +- OAuth2 authentication (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) diff --git a/docs/content/config/advanced/auth-oauth2.md b/docs/content/config/advanced/auth-oauth2.md new file mode 100644 index 00000000..963a6c2c --- /dev/null +++ b/docs/content/config/advanced/auth-oauth2.md @@ -0,0 +1,69 @@ +--- +title: 'Advanced | Basic OAuth2 Authentication' +--- + +## Introduction + +!!! warning "This is only a supplement to the existing account provisioners" + + Accounts must still be managed via the configured [`ACCOUNT_PROVISIONER`][env::account-provisioner] (FILE or LDAP). + + Reasoning for this can be found in [#3480][gh-pr::oauth2]. Future iterations on this feature may allow it to become a full account provisioner. + +[gh-pr::oauth2]: https://github.com/docker-mailserver/docker-mailserver/pull/3480 +[env::account-provisioner]: ../environment.md#account_provisioner + +The present OAuth2 support provides the capability for 3rd-party applications such as Roundcube to authenticate with DMS (dovecot) by using a token obtained from an OAuth2 provider, instead of passing passwords around. + +## Example (Authentik & Roundcube) + +This example assumes you have: + +- A working DMS server set up +- An Authentik server set up ([documentation](https://goauthentik.io/docs/installation/)) +- A Roundcube server set up (either [docker](https://hub.docker.com/r/roundcube/roundcubemail/) or [bare metal](https://github.com/roundcube/roundcubemail/wiki/Installation)) + +!!! example "Setup Instructions" + + === "1. Docker Mailserver" + Edit the following values in `mailserver.env`: + ```env + # ----------------------------------------------- + # --- OAUTH2 Section ---------------------------- + # ----------------------------------------------- + + # empty => OAUTH2 authentication is disabled + # 1 => OAUTH2 authentication is enabled + ENABLE_OAUTH2=1 + + # Specify the user info endpoint URL of the oauth2 provider + OAUTH2_INTROSPECTION_URL=https://authentik.example.com/application/o/userinfo/ + ``` + + === "2. Authentik" + 1. Create a new OAuth2 provider + 2. Note the client id and client secret + 3. Set the allowed redirect url to the equivalent of `https://roundcube.example.com/index.php/login/oauth` for your RoundCube instance. + + === "3. Roundcube" + Add the following to `oauth2.inc.php` ([documentation](https://github.com/roundcube/roundcubemail/wiki/Configuration)): + + ```php + $config['oauth_provider'] = 'generic'; + $config['oauth_provider_name'] = 'Authentik'; + $config['oauth_client_id'] = ''; + $config['oauth_client_secret'] = ''; + $config['oauth_auth_uri'] = 'https://authentik.example.com/application/o/authorize/'; + $config['oauth_token_uri'] = 'https://authentik.example.com/application/o/token/'; + $config['oauth_identity_uri'] = 'https://authentik.example.com/application/o/userinfo/'; + + // Optional: disable SSL certificate check on HTTP requests to OAuth server. For possible values, see: + // http://docs.guzzlephp.org/en/stable/request-options.html#verify + $config['oauth_verify_peer'] = false; + + $config['oauth_scope'] = 'email openid profile'; + $config['oauth_identity_fields'] = ['email']; + + // Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session + $config['oauth_login_redirect'] = false; + ``` diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index c3c074fd..61e604aa 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -54,7 +54,15 @@ The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage m Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_). -User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713). +!!! tip "OAuth2 Support" + + Presently DMS supports OAuth2 only as an supplementary authentication method. + + - A third-party service must provide a valid token for the user which Dovecot validates with the authentication service provider. To enable this feature reference the [OAuth2 configuration example guide][docs::auth::oauth2-config-guide]. + - User accounts must be provisioned to receive mail via one of the supported `ACCOUNT_PROVISIONER` providers. + - User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713). + +[docs::auth::oauth2-config-guide]: ./advanced/auth-oauth2.md - **empty** => use FILE - LDAP => use LDAP authentication @@ -716,10 +724,20 @@ Enable or disable `getmail`. - **5** => `getmail` The number of minutes for the interval. Min: 1; Max: 30; Default: 5. + +#### OAUTH2 + +##### ENABLE_OAUTH2 + +- **empty** => OAUTH2 authentication is disabled +- 1 => OAUTH2 authentication is enabled + +##### OAUTH2_INTROSPECTION_URL + +- => Specify the user info endpoint URL of the oauth2 provider (_eg: `https://oauth2.example.com/userinfo/`_) + #### LDAP - - ##### LDAP_START_TLS - **empty** => no diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 6441dffe..7df1acbe 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -142,6 +142,7 @@ nav: - 'Postfix': config/advanced/override-defaults/postfix.md - 'Modifications via Script': config/advanced/override-defaults/user-patches.md - 'LDAP Authentication': config/advanced/auth-ldap.md + - 'OAuth2 Authentication': config/advanced/auth-oauth2.md - 'Email Filtering with Sieve': config/advanced/mail-sieve.md - 'Email Gathering with Fetchmail': config/advanced/mail-fetchmail.md - 'Email Gathering with Getmail': config/advanced/mail-getmail.md diff --git a/mailserver.env b/mailserver.env index 0c2e1e40..c9793a99 100644 --- a/mailserver.env +++ b/mailserver.env @@ -419,6 +419,18 @@ ENABLE_GETMAIL=0 # The number of minutes for the interval. Min: 1; Max: 30. GETMAIL_POLL=5 +# ----------------------------------------------- +# --- OAUTH2 Section ---------------------------- +# ----------------------------------------------- + +# empty => OAUTH2 authentication is disabled +# 1 => OAUTH2 authentication is enabled +ENABLE_OAUTH2= + +# Specify the user info endpoint URL of the oauth2 provider +# Example: https://oauth2.example.com/userinfo/ +OAUTH2_INTROSPECTION_URL= + # ----------------------------------------------- # --- LDAP Section ------------------------------ # ----------------------------------------------- diff --git a/target/dovecot/10-auth.conf b/target/dovecot/10-auth.conf index f71289e9..260832fb 100644 --- a/target/dovecot/10-auth.conf +++ b/target/dovecot/10-auth.conf @@ -123,6 +123,7 @@ auth_mechanisms = plain login #!include auth-sql.conf.ext #!include auth-ldap.conf.ext !include auth-passwdfile.inc +#!include auth-oauth2.conf.ext #!include auth-checkpassword.conf.ext #!include auth-vpopmail.conf.ext #!include auth-static.conf.ext diff --git a/target/dovecot/auth-oauth2.conf.ext b/target/dovecot/auth-oauth2.conf.ext new file mode 100644 index 00000000..6096d1e4 --- /dev/null +++ b/target/dovecot/auth-oauth2.conf.ext @@ -0,0 +1,7 @@ +auth_mechanisms = $auth_mechanisms oauthbearer xoauth2 + +passdb { + driver = oauth2 + mechanisms = xoauth2 oauthbearer + args = /etc/dovecot/dovecot-oauth2.conf.ext +} diff --git a/target/dovecot/dovecot-oauth2.conf.ext b/target/dovecot/dovecot-oauth2.conf.ext new file mode 100644 index 00000000..6998ed08 --- /dev/null +++ b/target/dovecot/dovecot-oauth2.conf.ext @@ -0,0 +1,4 @@ +introspection_url = +# Dovecot defaults: +introspection_mode = auth +username_attribute = email diff --git a/target/scripts/start-mailserver.sh b/target/scripts/start-mailserver.sh index 2129b74a..1f352229 100755 --- a/target/scripts/start-mailserver.sh +++ b/target/scripts/start-mailserver.sh @@ -71,6 +71,11 @@ function _register_functions() { ;; esac + if [[ ${ENABLE_OAUTH2} -eq 1 ]]; then + _environment_variables_oauth2 + _register_setup_function '_setup_oauth2' + fi + if [[ ${ENABLE_SASLAUTHD} -eq 1 ]]; then _environment_variables_saslauthd _register_setup_function '_setup_saslauthd' diff --git a/target/scripts/startup/setup.d/oauth2.sh b/target/scripts/startup/setup.d/oauth2.sh new file mode 100644 index 00000000..20e9ffd1 --- /dev/null +++ b/target/scripts/startup/setup.d/oauth2.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +function _setup_oauth2() { + _log 'debug' 'Setting up OAUTH2' + + # Enable OAuth2 PassDB (Authentication): + sedfile -i -e '/\!include auth-oauth2\.conf\.ext/s/^#//' /etc/dovecot/conf.d/10-auth.conf + _replace_by_env_in_file 'OAUTH2_' '/etc/dovecot/dovecot-oauth2.conf.ext' + + return 0 +} diff --git a/target/scripts/startup/variables-stack.sh b/target/scripts/startup/variables-stack.sh index 2660ce89..fc38a39d 100644 --- a/target/scripts/startup/variables-stack.sh +++ b/target/scripts/startup/variables-stack.sh @@ -151,6 +151,12 @@ function __environment_variables_general_setup() { VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}" } +function _environment_variables_oauth2() { + _log 'debug' 'Setting OAUTH2-related environment variables now' + + VARS[OAUTH2_INTROSPECTION_URL]="${OAUTH2_INTROSPECTION_URL:=}" +} + # This function handles environment variables related to LDAP. # NOTE: SASLAuthd and Dovecot LDAP support inherit these common ENV. function _environment_variables_ldap() { diff --git a/test/config/oauth2/provider.py b/test/config/oauth2/provider.py new file mode 100644 index 00000000..22fc8129 --- /dev/null +++ b/test/config/oauth2/provider.py @@ -0,0 +1,56 @@ +# OAuth2 mock service +# +# Dovecot will query this service with the token it was provided. +# If the session for the token is valid, a response provides an attribute to perform a UserDB lookup on (default: email). + +import json +import base64 +from http.server import BaseHTTPRequestHandler, HTTPServer + +# OAuth2.0 Bearer token (paste into https://jwt.io/ to check it's contents). +# You should never need to edit this unless you REALLY need to change the issuer. +token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vcHJvdmlkZXIuZXhhbXBsZS50ZXN0OjgwMDAvIiwic3ViIjoiODJjMWMzMzRkY2M2ZTMxMWFlNGFhZWJmZTk0NmM1ZTg1OGYwNTVhZmYxY2U1YTM3YWE3Y2M5MWFhYjE3ZTM1YyIsImF1ZCI6Im1haWxzZXJ2ZXIiLCJ1aWQiOiI4OU4zR0NuN1M1Y090WkZNRTVBeVhNbmxURFdVcnEzRmd4YWlyWWhFIn0.zuCytArbphhJn9XT_y9cBdGqDCNo68tBrtOwPIsuKNyF340SaOuZa0xarZofygytdDpLtYr56QlPTKImi-n1ZWrHkRZkwrQi5jQ-j_n2hEAL0vUToLbDnXYfc5q2w7z7X0aoCmiK8-fV7Kx4CVTM7riBgpElf6F3wNAIcX6R1ijUh6ISCL0XYsdogf8WUNZipXY-O4R7YHXdOENuOp3G48hWhxuUh9PsUqE5yxDwLsOVzCTqg9S5gxPQzF2eCN9J0I2XiIlLKvLQPIZ2Y_K7iYvVwjpNdgb4xhm9wuKoIVinYkF_6CwIzAawBWIDJAbix1IslkUPQMGbupTDtOgTiQ" + +# This is the string the user-facing client (e.g. Roundcube) should send via IMAP to Dovecot. +# We include the user and the above token separated by '\1' chars as per the XOAUTH2 spec. +xoauth2 = base64.b64encode(f"user=user1@localhost.localdomain\1auth=Bearer {token}\1\1".encode("utf-8")) +# If changing the user above, use the new output from the below line with the contents of the AUTHENTICATE command in test/test-files/auth/imap-oauth2-auth.txt +print("XOAUTH2 string: " + str(xoauth2)) + + +class HTTPRequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + auth = self.headers.get("Authorization") + if auth is None: + self.send_response(401) + self.end_headers() + return + if len(auth.split()) != 2: + self.send_response(401) + self.end_headers() + return + auth = auth.split()[1] + # Valid session, respond with JSON containing the expected `email` claim to match as Dovecot username: + if auth == token: + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps({ + "email": "user1@localhost.localdomain", + "email_verified": True, + "sub": "82c1c334dcc6e311ae4aaebfe946c5e858f055aff1ce5a37aa7cc91aab17e35c" + }).encode("utf-8")) + else: + self.send_response(401) + self.end_headers() + +server = HTTPServer(('', 80), HTTPRequestHandler) +print("Starting server", flush=True) + +try: + server.serve_forever() +except KeyboardInterrupt: + print() + print("Received keyboard interrupt") +finally: + print("Exiting") diff --git a/test/files/auth/imap-oauth2-auth.txt b/test/files/auth/imap-oauth2-auth.txt new file mode 100644 index 00000000..825fabda --- /dev/null +++ b/test/files/auth/imap-oauth2-auth.txt @@ -0,0 +1,4 @@ +a0 NOOP See test/config/oauth2/provider.py to generate the below XOAUTH2 string +a1 AUTHENTICATE XOAUTH2 dXNlcj11c2VyMUBsb2NhbGhvc3QubG9jYWxkb21haW4BYXV0aD1CZWFyZXIgZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKb2RIUndPaTh2Y0hKdmRtbGtaWEl1WlhoaGJYQnNaUzUwWlhOME9qZ3dNREF2SWl3aWMzVmlJam9pT0RKak1XTXpNelJrWTJNMlpUTXhNV0ZsTkdGaFpXSm1aVGswTm1NMVpUZzFPR1l3TlRWaFptWXhZMlUxWVRNM1lXRTNZMk01TVdGaFlqRTNaVE0xWXlJc0ltRjFaQ0k2SW0xaGFXeHpaWEoyWlhJaUxDSjFhV1FpT2lJNE9VNHpSME51TjFNMVkwOTBXa1pOUlRWQmVWaE5ibXhVUkZkVmNuRXpSbWQ0WVdseVdXaEZJbjAuenVDeXRBcmJwaGhKbjlYVF95OWNCZEdxRENObzY4dEJydE93UElzdUtOeUYzNDBTYU91WmEweGFyWm9meWd5dGREcEx0WXI1NlFsUFRLSW1pLW4xWldySGtSWmt3clFpNWpRLWpfbjJoRUFMMHZVVG9MYkRuWFlmYzVxMnc3ejdYMGFvQ21pSzgtZlY3S3g0Q1ZUTTdyaUJncEVsZjZGM3dOQUljWDZSMWlqVWg2SVNDTDBYWXNkb2dmOFdVTlppcFhZLU80UjdZSFhkT0VOdU9wM0c0OGhXaHh1VWg5UHNVcUU1eXhEd0xzT1Z6Q1RxZzlTNWd4UFF6RjJlQ045SjBJMlhpSWxMS3ZMUVBJWjJZX0s3aVl2VndqcE5kZ2I0eGhtOXd1S29JVmluWWtGXzZDd0l6QWF3QldJREpBYml4MUlzbGtVUFFNR2J1cFREdE9nVGlRAQE= +a2 EXAMINE INBOX +a3 LOGOUT diff --git a/test/tests/serial/mail_with_oauth2.bats b/test/tests/serial/mail_with_oauth2.bats new file mode 100644 index 00000000..0d73bc54 --- /dev/null +++ b/test/tests/serial/mail_with_oauth2.bats @@ -0,0 +1,66 @@ +load "${REPOSITORY_ROOT}/test/helper/setup" +load "${REPOSITORY_ROOT}/test/helper/common" + +BATS_TEST_NAME_PREFIX='[OAuth2] ' +CONTAINER1_NAME='dms-test_oauth2' +CONTAINER2_NAME='dms-test_oauth2_provider' + +function setup_file() { + export DMS_TEST_NETWORK='test-network-oauth2' + export DMS_DOMAIN='example.test' + export FQDN_MAIL="mail.${DMS_DOMAIN}" + export FQDN_OAUTH2="oauth2.${DMS_DOMAIN}" + + # Link the test containers to separate network: + # NOTE: If the network already exists, test will fail to start. + docker network create "${DMS_TEST_NETWORK}" + + # Setup local oauth2 provider service: + docker run --rm -d --name "${CONTAINER2_NAME}" \ + --hostname "${FQDN_OAUTH2}" \ + --network "${DMS_TEST_NETWORK}" \ + --volume "${REPOSITORY_ROOT}/test/config/oauth2/:/app/" \ + docker.io/library/python:latest \ + python /app/provider.py + + _run_until_success_or_timeout 20 sh -c "docker logs ${CONTAINER2_NAME} 2>&1 | grep 'Starting server'" + + # + # Setup DMS container + # + + # Add OAUTH2 configuration so that Dovecot can reach out to our mock provider (CONTAINER2) + local ENV_OAUTH2_CONFIG=( + --env ENABLE_OAUTH2=1 + --env OAUTH2_INTROSPECTION_URL=http://oauth2.example.test/userinfo/ + ) + + export CONTAINER_NAME=${CONTAINER1_NAME} + local CUSTOM_SETUP_ARGUMENTS=( + "${ENV_OAUTH2_CONFIG[@]}" + + --hostname "${FQDN_MAIL}" + --network "${DMS_TEST_NETWORK}" + ) + + _init_with_defaults + _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' + _wait_for_tcp_port_in_container 143 + + # Set default implicit container fallback for helpers: + export CONTAINER_NAME=${CONTAINER1_NAME} +} + +function teardown_file() { + docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}" + docker network rm "${DMS_TEST_NETWORK}" +} + + +@test "oauth2: imap connect and authentication works" { + # An initial connection needs to be made first, otherwise the auth attempt fails + _run_in_container_bash 'nc -vz 0.0.0.0 143' + + _nc_wrapper 'auth/imap-oauth2-auth.txt' '-w 1 0.0.0.0 143' + assert_output --partial 'Examine completed' +} From 71e1102749680545f1ac6cb1a2863ea1714e989c Mon Sep 17 00:00:00 2001 From: Casper Date: Fri, 12 Jan 2024 23:48:14 +0100 Subject: [PATCH 30/34] Tiny #3480 follow up: Add missing ENABLE_OAUTH2 var (#3775) --- target/scripts/startup/variables-stack.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/target/scripts/startup/variables-stack.sh b/target/scripts/startup/variables-stack.sh index fc38a39d..0b351a9e 100644 --- a/target/scripts/startup/variables-stack.sh +++ b/target/scripts/startup/variables-stack.sh @@ -83,6 +83,7 @@ function __environment_variables_general_setup() { VARS[ENABLE_FETCHMAIL]="${ENABLE_FETCHMAIL:=0}" VARS[ENABLE_GETMAIL]="${ENABLE_GETMAIL:=0}" VARS[ENABLE_MANAGESIEVE]="${ENABLE_MANAGESIEVE:=0}" + VARS[ENABLE_OAUTH2]="${ENABLE_OAUTH2:=0}" VARS[ENABLE_OPENDKIM]="${ENABLE_OPENDKIM:=1}" VARS[ENABLE_OPENDMARC]="${ENABLE_OPENDMARC:=1}" VARS[ENABLE_POLICYD_SPF]="${ENABLE_POLICYD_SPF:=1}" From e3331b0f44b1d9c1c513389ad5b6f3b09e51dc16 Mon Sep 17 00:00:00 2001 From: Joerg Sonnenberger Date: Sat, 13 Jan 2024 09:37:20 +0100 Subject: [PATCH 31/34] feat: Add MTA-STS support for outbound mail (#3592) * feat: add support for MTA-STS for outgoing mails * Hook-up mta-sts-daemon into basic process handling test * fix: Call python script directly The python3 shebang will run it, which will now meet the expectations of the process testing via pgrep. fail2ban has the same approach. --------- Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- CHANGELOG.md | 8 +++-- Dockerfile | 9 ++++++ docs/content/config/best-practices/mta-sts.md | 30 +++++++++++++++++++ docs/content/config/environment.md | 9 ++++++ docs/mkdocs.yml | 3 +- mailserver.env | 6 ++++ target/mta-sts-daemon/mta-sts-daemon.yml | 7 +++++ target/scripts/build/packages.sh | 2 +- target/scripts/start-mailserver.sh | 5 ++++ target/scripts/startup/daemons-stack.sh | 1 + target/scripts/startup/setup.d/mail_state.sh | 2 ++ target/scripts/startup/setup.d/mta-sts.sh | 7 +++++ target/supervisor/conf.d/supervisor-app.conf | 12 ++++++++ .../process_check_restart.bats | 4 +++ 14 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 docs/content/config/best-practices/mta-sts.md create mode 100644 target/mta-sts-daemon/mta-sts-daemon.yml create mode 100644 target/scripts/startup/setup.d/mta-sts.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index e3881941..df9c6987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,14 @@ All notable changes to this project will be documented in this file. The format ### Features - - **Authentication with OIDC / OAuth 2.0** 🎉 - - DMS now supports authentication via OAuth2 (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) from capable services (_like Roundcube_). +- **Authentication with OIDC / OAuth 2.0** 🎉 + - DMS now supports authentication via OAuth2 (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) from capable services (_like Roundcube_). - This does not replace the need for an `ACCOUNT_PROVISIONER` (`FILE` / `LDAP`), which is required for an account to receive or send mail. - Successful authentication (_via Dovecot PassDB_) still requires an existing account (_lookup via Dovecot UserDB_). +- **MTA-STS** (_Optional support for mandatory outgoing TLS encryption_) + - If enabled and the outbound recipient has an MTA-STS policy set, TLS is mandatory for delivering to that recipient. + - Enable via the ENV `ENABLE_MTA_STS=1` + - Supported by major email service providers like Gmail, Yahoo and Outlook. ### Updates diff --git a/Dockerfile b/Dockerfile index 2118622d..e822632a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -199,6 +199,15 @@ COPY target/opendmarc/opendmarc.conf /etc/opendmarc.conf COPY target/opendmarc/default-opendmarc /etc/default/opendmarc COPY target/opendmarc/ignore.hosts /etc/opendmarc/ignore.hosts +# -------------------------------------------------- +# --- postfix-mta-sts-daemon ----------------------- +# -------------------------------------------------- +COPY target/mta-sts-daemon/mta-sts-daemon.yml /etc/mta-sts-daemon.yml +RUN < DNS block lists are disabled - 1 => DNS block lists are enabled +##### ENABLE_MTA_STS + +Enables MTA-STS support for outbound mail. + +- **0** => Disabled +- 1 => Enabled + +See [MTA-STS](best-practices/mta-sts.md) for further explanation. + ##### ENABLE_OPENDKIM Enables the OpenDKIM service. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7df1acbe..aaaaf51b 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -122,8 +122,9 @@ nav: - 'Environment Variables': config/environment.md - 'User Management': config/user-management.md - 'Best Practices': - - 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md - 'Auto-discovery': config/best-practices/autodiscover.md + - 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md + - 'MTA-STS': config/best-practices/mta-sts.md - 'Security': - 'Understanding the Ports': config/security/understanding-the-ports.md - 'SSL/TLS': config/security/ssl.md diff --git a/mailserver.env b/mailserver.env index c9793a99..49bc2cca 100644 --- a/mailserver.env +++ b/mailserver.env @@ -354,6 +354,12 @@ POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0 # Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols POSTFIX_INET_PROTOCOLS=all +# Enables MTA-STS support for outbound mail. +# More details: https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/mail-mta-sts/ +# - **0** ==> MTA-STS disabled +# - 1 => MTA-STS enabled +ENABLE_MTA_STS=0 + # Choose TCP/IP protocols for dovecot to use # **all** => Listen on all interfaces # ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. diff --git a/target/mta-sts-daemon/mta-sts-daemon.yml b/target/mta-sts-daemon/mta-sts-daemon.yml new file mode 100644 index 00000000..4d5d5e55 --- /dev/null +++ b/target/mta-sts-daemon/mta-sts-daemon.yml @@ -0,0 +1,7 @@ +# Docs: https://github.com/Snawoot/postfix-mta-sts-resolver/blob/master/man/mta-sts-daemon.yml.5.adoc +path: /var/run/mta-sts/daemon.sock +mode: 0666 +cache: + type: sqlite + options: + filename: "/var/lib/mta-sts/cache.db" diff --git a/target/scripts/build/packages.sh b/target/scripts/build/packages.sh index e3607f48..e57cfe07 100644 --- a/target/scripts/build/packages.sh +++ b/target/scripts/build/packages.sh @@ -68,7 +68,7 @@ function _install_packages() { ) POSTFIX_PACKAGES=( - pflogsumm postgrey postfix-ldap + pflogsumm postgrey postfix-ldap postfix-mta-sts-resolver postfix-pcre postfix-policyd-spf-python postsrsd ) diff --git a/target/scripts/start-mailserver.sh b/target/scripts/start-mailserver.sh index 1f352229..56dfa1fb 100755 --- a/target/scripts/start-mailserver.sh +++ b/target/scripts/start-mailserver.sh @@ -120,6 +120,11 @@ function _register_functions() { _register_setup_function '_setup_apply_fixes_after_configuration' _register_setup_function '_environment_variables_export' + if [[ ${ENABLE_MTA_STS} -eq 1 ]]; then + _register_setup_function '_setup_mta_sts' + _register_start_daemon '_start_daemon_mta_sts_daemon' + fi + # ? >> Daemons _register_start_daemon '_start_daemon_cron' diff --git a/target/scripts/startup/daemons-stack.sh b/target/scripts/startup/daemons-stack.sh index 5476fc9f..a4cecf67 100644 --- a/target/scripts/startup/daemons-stack.sh +++ b/target/scripts/startup/daemons-stack.sh @@ -38,6 +38,7 @@ function _start_daemon_opendkim { _default_start_daemon 'opendkim' ; function _start_daemon_opendmarc { _default_start_daemon 'opendmarc' ; } function _start_daemon_postgrey { _default_start_daemon 'postgrey' ; } function _start_daemon_postsrsd { _default_start_daemon 'postsrsd' ; } +function _start_daemon_mta_sts_daemon { _default_start_daemon 'mta-sts-daemon' ; } function _start_daemon_rspamd { _default_start_daemon 'rspamd' ; } function _start_daemon_rspamd_redis { _default_start_daemon 'rspamd-redis' ; } function _start_daemon_rsyslog { _default_start_daemon 'rsyslog' ; } diff --git a/target/scripts/startup/setup.d/mail_state.sh b/target/scripts/startup/setup.d/mail_state.sh index 73c2515b..9963bbcc 100644 --- a/target/scripts/startup/setup.d/mail_state.sh +++ b/target/scripts/startup/setup.d/mail_state.sh @@ -24,6 +24,7 @@ function _setup_save_states() { [[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban') [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail') [[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail') + [[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts') [[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey') [[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd') [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis') @@ -84,6 +85,7 @@ function _setup_save_states() { [[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${STATEDIR}/lib-amavis" [[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${STATEDIR}/lib-clamav" [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${STATEDIR}/lib-fetchmail" + [[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${STATEDIR}/lib-mta-sts" [[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${STATEDIR}/lib-postgrey" [[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${STATEDIR}/lib-rspamd" [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${STATEDIR}/lib-redis" diff --git a/target/scripts/startup/setup.d/mta-sts.sh b/target/scripts/startup/setup.d/mta-sts.sh new file mode 100644 index 00000000..7d1f88ea --- /dev/null +++ b/target/scripts/startup/setup.d/mta-sts.sh @@ -0,0 +1,7 @@ +#!/bin/bash + + +function _setup_mta_sts() { + _log 'trace' 'Adding MTA-STS lookup to the Postfix TLS policy map' + _add_to_or_update_postfix_main smtp_tls_policy_maps 'socketmap:unix:/var/run/mta-sts/daemon.sock:postfix' +} diff --git a/target/supervisor/conf.d/supervisor-app.conf b/target/supervisor/conf.d/supervisor-app.conf index 431357d8..d64d3d72 100644 --- a/target/supervisor/conf.d/supervisor-app.conf +++ b/target/supervisor/conf.d/supervisor-app.conf @@ -157,3 +157,15 @@ autostart=false stdout_logfile=/var/log/supervisor/%(program_name)s.log stderr_logfile=/var/log/supervisor/%(program_name)s.log command=/bin/bash -l -c /usr/local/bin/update-check.sh + +# Docs: https://github.com/Snawoot/postfix-mta-sts-resolver/blob/master/man/mta-sts-daemon.1.adoc +[program:mta-sts-daemon] +startsecs=0 +stopwaitsecs=55 +autostart=false +autorestart=true +stdout_logfile=/var/log/supervisor/%(program_name)s.log +stderr_logfile=/var/log/supervisor/%(program_name)s.log +command=/usr/bin/mta-sts-daemon --config /etc/mta-sts-daemon.yml +user=_mta-sts +environment=HOME=/var/lib/mta-sts diff --git a/test/tests/parallel/set3/container_configuration/process_check_restart.bats b/test/tests/parallel/set3/container_configuration/process_check_restart.bats index b559d21d..4b01454e 100644 --- a/test/tests/parallel/set3/container_configuration/process_check_restart.bats +++ b/test/tests/parallel/set3/container_configuration/process_check_restart.bats @@ -21,6 +21,7 @@ function teardown() { _default_teardown ; } # dovecot (/usr/sbin/dovecot) # fetchmail (/usr/bin/fetchmail) # fail2ban-server (/usr/bin/python3 /usr/bin/fail2ban-server) - Started by fail2ban-wrapper.sh +# mta-sts-daemon (/usr/bin/bin/python3 /usr/bin/mta-sts-daemon) # postgrey (postgrey) - NOTE: This process lacks path information to match with `--full` in pgrep / pkill # postsrsd (/usr/sbin/postsrsd) - NOTE: Also matches the wrapper: `/bin/bash /usr/local/bin/postsrsd-wrapper.sh` # saslauthd (/usr/sbin/saslauthd) - x5 of the same process are found running (1 is a parent of 4) @@ -44,6 +45,7 @@ ENV_PROCESS_LIST=( dovecot fail2ban-server fetchmail + mta-sts-daemon opendkim opendmarc postgrey @@ -58,6 +60,7 @@ ENV_PROCESS_LIST=( --env ENABLE_CLAMAV=0 --env ENABLE_FAIL2BAN=0 --env ENABLE_FETCHMAIL=0 + --env ENABLE_MTA_STS=0 --env ENABLE_OPENDKIM=0 --env ENABLE_OPENDMARC=0 --env ENABLE_POSTGREY=0 @@ -93,6 +96,7 @@ ENV_PROCESS_LIST=( --env ENABLE_AMAVIS=1 --env ENABLE_FAIL2BAN=1 --env ENABLE_FETCHMAIL=1 + --env ENABLE_MTA_STS=1 --env ENABLE_OPENDKIM=1 --env ENABLE_OPENDMARC=1 --env FETCHMAIL_PARALLEL=1 From f794f65caa5479763fe9ce149b175d08f9f37b18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jan 2024 14:08:27 +0100 Subject: [PATCH 32/34] docs: updated `CONTRIBUTORS.md` (#3777) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CONTRIBUTORS.md | 345 ++++++++++++++++++++++++------------------------ 1 file changed, 176 insertions(+), 169 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 05a4e6e4..53254c78 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -321,46 +321,10 @@ Thanks goes to these wonderful people ✨ - - egavard + + kiliant
- egavard -
- - - - mathuin -
- mathuin -
- - - - dashohoxha -
- dashohoxha -
- - - - jamebus -
- jamebus -
- - - - - lukecyca -
- lukecyca -
- - - - okainov -
- okainov + kiliant
@@ -371,53 +335,68 @@ Thanks goes to these wonderful people ✨ - - kiliant + + okainov
- kiliant + okainov
- - m-schmoock + + lukecyca
- m-schmoock -
- - - - mjung -
- mjung + lukecyca
- - VanVan + + jsonn
- VanVan + jsonn
- - andreasgerstmayr + + jamebus
- andreasgerstmayr + jamebus
- - davidszp + + dashohoxha
- davidszp + dashohoxha
- - kamuri + + mathuin
- kamuri + mathuin +
+ + + + egavard +
+ egavard +
+ + + + weo +
+ weo +
+ + + + + Zehir +
+ Zehir
@@ -428,18 +407,46 @@ Thanks goes to these wonderful people ✨ - - Zehir + + kamuri
- Zehir + kamuri +
+ + + + davidszp +
+ davidszp +
+ + + + andreasgerstmayr +
+ andreasgerstmayr +
+ + + + VanVan +
+ VanVan
- - weo + + mjung
- weo + mjung +
+ + + + m-schmoock +
+ m-schmoock
@@ -469,15 +476,15 @@ Thanks goes to these wonderful people ✨
Starbix - + + citec
citec
- - + yajo @@ -492,13 +499,6 @@ Thanks goes to these wonderful people ✨ analogue - - - MakerMatrix -
- MakerMatrix -
- Rubytastic2 @@ -514,10 +514,10 @@ Thanks goes to these wonderful people ✨ - - jsonn + + MakerMatrix
- jsonn + MakerMatrix
@@ -642,26 +642,33 @@ Thanks goes to these wonderful people ✨ yogo1212 - - - mpanneck -
- mpanneck -
- - willtho89
willtho89
+ + + + + mpanneck +
+ mpanneck +
- - ubenmackin + + aminvakil
- ubenmackin + aminvakil +
+ + + + elbracht +
+ elbracht
@@ -679,17 +686,10 @@ Thanks goes to these wonderful people ✨ - - aminvakil + + ubenmackin
- aminvakil -
- - - - elbracht -
- elbracht + ubenmackin
@@ -780,10 +780,17 @@ Thanks goes to these wonderful people ✨ - - fl42 + + jamesfryer
- fl42 + jamesfryer +
+ + + + eltociear +
+ eltociear
@@ -801,17 +808,10 @@ Thanks goes to these wonderful people ✨ - - jamesfryer + + fl42
- jamesfryer -
- - - - eltociear -
- eltociear + fl42
@@ -944,10 +944,10 @@ Thanks goes to these wonderful people ✨ - - 0xflotus + + nilshoell
- 0xflotus + nilshoell
@@ -1388,6 +1388,13 @@ Thanks goes to these wonderful people ✨ mchamplain + + + 0xflotus +
+ 0xflotus +
+ auchri @@ -1415,15 +1422,15 @@ Thanks goes to these wonderful people ✨
damianmoore
- + + espitall
espitall
- - + dkarski @@ -1458,15 +1465,15 @@ Thanks goes to these wonderful people ✨
mazzz1y
- + + aydodo
aydodo
- - + vedtam @@ -1501,15 +1508,15 @@ Thanks goes to these wonderful people ✨
ErikEngerd
- + + huncode
huncode
- - + felixn @@ -1544,15 +1551,15 @@ Thanks goes to these wonderful people ✨
20th
- + + 2b
2b
- - + askz @@ -1587,15 +1594,15 @@ Thanks goes to these wonderful people ✨
alexanderneu
- + + ch3sh1r
ch3sh1r
- - + eglia @@ -1630,15 +1637,15 @@ Thanks goes to these wonderful people ✨
MrFreezeex
- + + arunvc
arunvc
- - + astrocket @@ -1673,15 +1680,22 @@ Thanks goes to these wonderful people ✨
crash7
- + + fkefer
fkefer
- - + + + + KCrawley +
+ KCrawley +
+ khuedoan @@ -1709,7 +1723,8 @@ Thanks goes to these wonderful people ✨
linhandev
- + + luke- @@ -1723,8 +1738,7 @@ Thanks goes to these wonderful people ✨
LucidityCrash
- - + MadsRC @@ -1752,7 +1766,8 @@ Thanks goes to these wonderful people ✨
dragetd
- + + michaeljensen @@ -1766,8 +1781,7 @@ Thanks goes to these wonderful people ✨
exhuma
- - + milas @@ -1795,7 +1809,8 @@ Thanks goes to these wonderful people ✨
mpldr
- + + naveensrinivasan @@ -1809,8 +1824,7 @@ Thanks goes to these wonderful people ✨
neuralp
- - + radicand @@ -1818,13 +1832,6 @@ Thanks goes to these wonderful people ✨ radicand - - - nilshoell -
- nilshoell -
- frugan-dev @@ -1845,15 +1852,15 @@ Thanks goes to these wonderful people ✨
glandais
- + + GiovanH
GiovanH
- - + harryyoud @@ -1888,15 +1895,15 @@ Thanks goes to these wonderful people ✨
jcalfee
- + + mivek
mivek
- - + init-js @@ -1931,15 +1938,15 @@ Thanks goes to these wonderful people ✨
jmccl
- + + jurekbarth
jurekbarth
- - + JOduMonT @@ -1962,10 +1969,10 @@ Thanks goes to these wonderful people ✨ - - KCrawley + + thechubbypanda
- KCrawley + thechubbypanda
From 3a142f97264eff31e3d72a3ce7420ccdb8e3aa4d Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Sun, 14 Jan 2024 23:14:03 +0100 Subject: [PATCH 33/34] tests: small adjustments (#3772) --- CHANGELOG.md | 2 +- test/files/emails/amavis/spam.txt | 6 ------ test/helper/sending.bash | 15 +++++++++++---- .../parallel/set1/spam_virus/rspamd_full.bats | 18 ++++++++---------- .../set1/spam_virus/spam_junk_folder.bats | 2 +- .../tests/parallel/set3/mta/smtp_delivery.bats | 2 +- 6 files changed, 22 insertions(+), 23 deletions(-) delete mode 100644 test/files/emails/amavis/spam.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index df9c6987..0ade3c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ All notable changes to this project will be documented in this file. The format ### Updates - **Tests**: - - Refactored mail sending ([#3747](https://github.com/docker-mailserver/docker-mailserver/pull/3747)): + - Refactored mail sending ([#3747](https://github.com/docker-mailserver/docker-mailserver/pull/3747) & [#3772](https://github.com/docker-mailserver/docker-mailserver/pull/3772)): - This change is a follow-up to [#3732](https://github.com/docker-mailserver/docker-mailserver/pull/3732) from DMS v13.2. - `swaks` version is now the latest from Github releases instead of the Debian package. - `_nc_wrapper`, `_send_mail` and related helpers expect the `.txt` filepath extension again. diff --git a/test/files/emails/amavis/spam.txt b/test/files/emails/amavis/spam.txt deleted file mode 100644 index e8d26138..00000000 --- a/test/files/emails/amavis/spam.txt +++ /dev/null @@ -1,6 +0,0 @@ -From: Docker Mail Server -To: Existing Local User -Date: Sat, 22 May 2010 07:43:25 -0400 -Subject: Test Message amavis/spam.txt -This is a test mail. -XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X diff --git a/test/helper/sending.bash b/test/helper/sending.bash index 529e36aa..e18dc1ac 100644 --- a/test/helper/sending.bash +++ b/test/helper/sending.bash @@ -141,14 +141,12 @@ function _send_email_and_get_id() { # Get rid of ${1} so only the arguments for swaks remain shift 1 - local QUEUE_ID # The unique ID Postfix (and other services) use may be different in length - # on different systems (e.g. amd64 (11) vs aarch64 (10)). Hence, we use a - # range to safely capture it. + # on different systems. Hence, we use a range to safely capture it. local QUEUE_ID_REGEX='[A-Z0-9]{9,12}' _wait_for_empty_mail_queue_in_container - local OUTPUT=$(_send_email "${@}" --header "Message-Id: ${MID}") + _send_email "${@}" --header "Message-Id: ${MID}" _wait_for_empty_mail_queue_in_container # We store Postfix's queue ID first @@ -164,3 +162,12 @@ function _send_email_and_get_id() { run echo "${ID_ENV_VAR_REF}" assert_line --regexp "^${QUEUE_ID_REGEX}\|${MID}$" } + +# Send a spam e-mail by utilizing GTUBE. +# +# Extra arguments given to this function will be supplied by `_send_email_and_get_id` directly. +function _send_spam() { + _send_email_and_get_id MAIL_ID_SPAM "${@}" \ + --from 'spam@external.tld' \ + --body 'XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X' +} diff --git a/test/tests/parallel/set1/spam_virus/rspamd_full.bats b/test/tests/parallel/set1/spam_virus/rspamd_full.bats index b83ac353..f66e9231 100644 --- a/test/tests/parallel/set1/spam_virus/rspamd_full.bats +++ b/test/tests/parallel/set1/spam_virus/rspamd_full.bats @@ -43,20 +43,18 @@ function setup_file() { _wait_for_service postfix _wait_for_smtp_port_in_container - # ref: https://rspamd.com/doc/gtube_patterns.html - local GTUBE_SUFFIX='*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X' - # We will send 4 emails: - # 1. The first one should pass just fine + # 1. The first one should pass just fine _send_email_and_get_id MAIL_ID_PASS - # 2. The second one should be rejected (GTUBE pattern) - _send_email_and_get_id MAIL_ID_REJECT --expect-rejection --body "XJS${GTUBE_SUFFIX}" - # 3. The third one should be rejected due to a virus (ClamAV EICAR pattern) + # 2. The second one should be rejected (Rspamd-specific GTUBE pattern for rejection) + _send_spam --expect-rejection + # 3. The third one should be rejected due to a virus (ClamAV EICAR pattern) # shellcheck disable=SC2016 _send_email_and_get_id MAIL_ID_VIRUS --expect-rejection \ --body 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' - # 4. The fourth one will receive an added header (GTUBE pattern) - _send_email_and_get_id MAIL_ID_HEADER --body "YJS${GTUBE_SUFFIX}" + # 4. The fourth one will receive an added header (Rspamd-specific GTUBE pattern for adding a spam header) + # ref: https://rspamd.com/doc/gtube_patterns.html + _send_email_and_get_id MAIL_ID_HEADER --body "YJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X" _run_in_container cat /var/log/mail.log assert_success @@ -122,7 +120,7 @@ function teardown_file() { _default_teardown ; } _service_log_should_contain_string 'rspamd' 'S \(reject\)' _service_log_should_contain_string 'rspamd' 'reject "Gtube pattern"' - _print_mail_log_for_id "${MAIL_ID_REJECT}" + _print_mail_log_for_id "${MAIL_ID_SPAM}" assert_output --partial 'milter-reject' assert_output --partial '5.7.1 Gtube pattern' diff --git a/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats b/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats index 2c3a522d..15ec4fe1 100644 --- a/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats +++ b/test/tests/parallel/set1/spam_virus/spam_junk_folder.bats @@ -95,7 +95,7 @@ function teardown() { _default_teardown ; } function _should_send_spam_message() { _wait_for_smtp_port_in_container _wait_for_tcp_port_in_container 10024 # port 10024 is for Amavis - _send_email --from 'spam@external.tld' --data 'amavis/spam.txt' + _send_spam } function _should_be_received_by_amavis() { diff --git a/test/tests/parallel/set3/mta/smtp_delivery.bats b/test/tests/parallel/set3/mta/smtp_delivery.bats index 329f36b2..e851d94e 100644 --- a/test/tests/parallel/set3/mta/smtp_delivery.bats +++ b/test/tests/parallel/set3/mta/smtp_delivery.bats @@ -81,7 +81,7 @@ function setup_file() { _send_email --to bounce-always@localhost.localdomain _send_email --to alias2@localhost.localdomain # Required for 'rejects spam': - _send_email --from 'spam@external.tld' --data 'amavis/spam.txt' + _send_spam # Required for 'delivers mail to existing account': _send_email --header 'Subject: Test Message existing-user1' From 1449629479ebf9dbd1e63fc78b0d7b9c1b6cd2ff Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:23:23 +1300 Subject: [PATCH 34/34] fix: Revert quoting `SA_SPAM_SUBJECT` in `mailserver.env` (#3767) In Docker Compose `.env` files are parsed properly when values are wrapped with quotes. Trailing white-space is also discarded, like it would be with shell variables. This is not the case with `docker run` or other CRI like `podman` (_including it's compose equivalent support_). Those will parse the quotes to be included in a literal string value. Trailing white-space is also retained. Hence a default with a trailing space is not compatible across CRI. This change documents the default with additional context on how to include a trailing white-space with a custom value for the users CRI choice. It additionally clearly communicates the opt-out value for this feature. --- mailserver.env | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mailserver.env b/mailserver.env index 49bc2cca..9b085c9f 100644 --- a/mailserver.env +++ b/mailserver.env @@ -401,7 +401,10 @@ SA_TAG2=6.31 SA_KILL=10.0 # add tag to subject if spam detected -SA_SPAM_SUBJECT='***SPAM*** ' +# The value `undef` opts-out of this feature. The value shown below is the default. +# NOTE: By default spam is delivered to a junk folder, reducing the value of adding a subject prefix. +# NOTE: If not using Docker Compose, other CRI may require the single quotes removed. +#SA_SPAM_SUBJECT='***SPAM*** ' # ----------------------------------------------- # --- Fetchmail Section -------------------------