diff --git a/test/config/before.dovecot.sieve b/test/config/dovecot-sieve/before.dovecot.sieve similarity index 74% rename from test/config/before.dovecot.sieve rename to test/config/dovecot-sieve/before.dovecot.sieve index 188412ba..eed64237 100644 --- a/test/config/before.dovecot.sieve +++ b/test/config/dovecot-sieve/before.dovecot.sieve @@ -1,6 +1,5 @@ require ["fileinto", "copy"]; if address :contains ["From"] "spam@spam.com" { - fileinto :copy "INBOX"; + fileinto :copy "INBOX"; } - diff --git a/test/config/dovecot-sieve/dovecot.sieve b/test/config/dovecot-sieve/dovecot.sieve new file mode 100644 index 00000000..412db33f --- /dev/null +++ b/test/config/dovecot-sieve/dovecot.sieve @@ -0,0 +1,7 @@ +require ["fileinto"]; + +if address :contains ["From"] "spam@spam.com" { + fileinto "INBOX.spam"; +} else { + keep; +} diff --git a/test/config/sieve-pipe/pipe_to_tmp b/test/config/dovecot-sieve/sieve-pipe/pipe_to_tmp similarity index 100% rename from test/config/sieve-pipe/pipe_to_tmp rename to test/config/dovecot-sieve/sieve-pipe/pipe_to_tmp diff --git a/test/config/user2@otherdomain.tld.dovecot.sieve b/test/config/dovecot-sieve/user2@otherdomain.tld.dovecot.sieve similarity index 100% rename from test/config/user2@otherdomain.tld.dovecot.sieve rename to test/config/dovecot-sieve/user2@otherdomain.tld.dovecot.sieve diff --git a/test/config/dovecot.cf b/test/config/override-configs/dovecot.cf similarity index 57% rename from test/config/dovecot.cf rename to test/config/override-configs/dovecot.cf index 47e76619..35279f28 100644 --- a/test/config/dovecot.cf +++ b/test/config/override-configs/dovecot.cf @@ -1,2 +1 @@ mail_max_userip_connections = 69 -recipient_delimiter = ~ diff --git a/test/config/postfix-main.cf b/test/config/override-configs/postfix-main.cf similarity index 78% rename from test/config/postfix-main.cf rename to test/config/override-configs/postfix-main.cf index 38f0bb26..77c2d787 100644 --- a/test/config/postfix-main.cf +++ b/test/config/override-configs/postfix-main.cf @@ -1,4 +1,3 @@ -recipient_delimiter = ~ max_idle = 600s # this is a comment # this is also a comment diff --git a/test/config/postfix-master.cf b/test/config/override-configs/postfix-master.cf similarity index 100% rename from test/config/postfix-master.cf rename to test/config/override-configs/postfix-master.cf diff --git a/test/config/sieve/dovecot.sieve b/test/config/sieve/dovecot.sieve deleted file mode 100644 index cce44e99..00000000 --- a/test/config/sieve/dovecot.sieve +++ /dev/null @@ -1,8 +0,0 @@ -require ["fileinto", "reject"]; - -if address :contains ["From"] "spam@spam.com" { - fileinto "INBOX.spam"; -} else { - keep; -} - diff --git a/test/config/smtp-delivery/dovecot.cf b/test/config/smtp-delivery/dovecot.cf new file mode 100644 index 00000000..b26e77ea --- /dev/null +++ b/test/config/smtp-delivery/dovecot.cf @@ -0,0 +1 @@ +recipient_delimiter = ~ diff --git a/test/config/smtp-delivery/postfix-main.cf b/test/config/smtp-delivery/postfix-main.cf new file mode 100644 index 00000000..b26e77ea --- /dev/null +++ b/test/config/smtp-delivery/postfix-main.cf @@ -0,0 +1 @@ +recipient_delimiter = ~ diff --git a/test/helper/common.bash b/test/helper/common.bash index 26c66137..bed96c38 100644 --- a/test/helper/common.bash +++ b/test/helper/common.bash @@ -240,3 +240,8 @@ function wait_for_empty_mail_queue_in_container() { # shellcheck disable=SC2016 repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c '[[ $(mailq) == *"Mail queue is empty"* ]]' } + +# `lines` is a special BATS variable updated via `run`: +function _should_output_number_of_lines() { + assert_equal "${#lines[@]}" $1 +} diff --git a/test/tests/parallel/set1/spam_virus/amavis.bats b/test/tests/parallel/set1/spam_virus/amavis.bats new file mode 100644 index 00000000..d2e23a2c --- /dev/null +++ b/test/tests/parallel/set1/spam_virus/amavis.bats @@ -0,0 +1,25 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +TEST_NAME_PREFIX='Amavis:' +CONTAINER_NAME='dms-test_amavis' + +function setup_file() { + init_with_defaults + + local CUSTOM_SETUP_ARGUMENTS=( + --env ENABLE_AMAVIS=1 + --env AMAVIS_LOGLEVEL=2 + --env ENABLE_SPAMASSASSIN=1 + ) + + common_container_setup 'CUSTOM_SETUP_ARGUMENTS' +} + +function teardown_file() { _default_teardown ; } + +@test "${TEST_NAME_PREFIX} Amavis integration should be active" { + _run_in_container grep 'ANTI-SPAM-SA' /var/log/mail/mail.log + assert_output --partial 'loaded' + refute_output --partial 'NOT loaded' +} diff --git a/test/tests/parallel/set3/config-overrides.bats b/test/tests/parallel/set3/config-overrides.bats new file mode 100644 index 00000000..2a42ed55 --- /dev/null +++ b/test/tests/parallel/set3/config-overrides.bats @@ -0,0 +1,47 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +TEST_NAME_PREFIX='Override Configs:' +CONTAINER_NAME='dms-test_config-overrides' + +function setup_file() { + init_with_defaults + + # Move override configs into main `/tmp/docker-mailserver` config location: + mv "${TEST_TMP_CONFIG}/override-configs/"* "${TEST_TMP_CONFIG}/" + + common_container_setup +} + +function teardown_file() { _default_teardown ; } + +@test "${TEST_NAME_PREFIX}: Postfix - 'postfix-main.cf' overrides applied to '/etc/postfix/main.cf'" { + _run_in_container grep -q 'max_idle = 600s' /tmp/docker-mailserver/postfix-main.cf + assert_success + + _run_in_container grep -q 'readme_directory = /tmp' /tmp/docker-mailserver/postfix-main.cf + assert_success + + _run_in_container postconf + assert_success + assert_output --partial 'max_idle = 600s' + assert_output --partial 'readme_directory = /tmp' +} + +@test "${TEST_NAME_PREFIX}: Postfix - 'postfix-master.cf' overrides applied to '/etc/postfix/master.cf'" { + _run_in_container grep -q 'submission/inet/smtpd_sasl_security_options=noanonymous' /tmp/docker-mailserver/postfix-master.cf + assert_success + + _run_in_container postconf -M + assert_success + assert_output --partial '-o smtpd_sasl_security_options=noanonymous' +} + +@test "${TEST_NAME_PREFIX}: Dovecot - 'dovecot.cf' overrides applied to '/etc/dovecot/local.conf'" { + _run_in_container grep -q 'mail_max_userip_connections = 69' /tmp/docker-mailserver/dovecot.cf + assert_success + + _run_in_container doveconf + assert_success + assert_output --partial 'mail_max_userip_connections = 69' +} diff --git a/test/tests/parallel/set3/dovecot-sieve.bats b/test/tests/parallel/set3/dovecot-sieve.bats new file mode 100644 index 00000000..e465e623 --- /dev/null +++ b/test/tests/parallel/set3/dovecot-sieve.bats @@ -0,0 +1,63 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +# Docs: +# https://docker-mailserver.github.io/docker-mailserver/edge/config/advanced/mail-sieve/ + +TEST_NAME_PREFIX='Dovecot [Sieve Support]:' +CONTAINER_NAME='dms-test_dovecot-sieve' + +function setup_file() { + init_with_defaults + + # Move sieve configs into main `/tmp/docker-mailserver` config location: + mv "${TEST_TMP_CONFIG}/dovecot-sieve/"* "${TEST_TMP_CONFIG}/" + + local CONTAINER_ARGS_ENV_CUSTOM=( + --env ENABLE_MANAGESIEVE=1 + # Required for mail delivery via nc: + --env PERMIT_DOCKER=container + # Mount into mail dir for user1 to treat as a user-sieve: + # NOTE: Cannot use ':ro', 'start-mailserver.sh' attempts to 'chown -R' /var/mail: + --volume "${TEST_TMP_CONFIG}/dovecot.sieve:/var/mail/localhost.localdomain/user1/.dovecot.sieve" + ) + common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' + + wait_for_smtp_port_in_container "${CONTAINER_NAME}" + + # Single mail sent from 'spam@spam.com' that is handled by User (relocate) and Global (copy) sieves for user1: + _run_in_container bash -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt" + # Mail for user2 triggers the sieve-pipe: + _run_in_container bash -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-pipe.txt" + + wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" +} + +function teardown_file() { _default_teardown ; } + +# dovecot-sieve/dovecot.sieve +@test "${TEST_NAME_PREFIX} User Sieve - should store mail from 'spam@spam.com' into recipient (user1) mailbox 'INBOX.spam'" { + _run_in_container bash -c 'ls -A /var/mail/localhost.localdomain/user1/.INBOX.spam/new' + assert_success + _should_output_number_of_lines 1 +} + +# dovecot-sieve/before.dovecot.sieve +@test "${TEST_NAME_PREFIX} Global Sieve - should have copied mail from 'spam@spam.com' to recipient (user1) inbox" { + _run_in_container grep 'Spambot ' -R /var/mail/localhost.localdomain/user1/new/ + assert_success +} + +# dovecot-sieve/sieve-pipe + dovecot-sieve/user2@otherdomain.tld.dovecot.sieve +@test "${TEST_NAME_PREFIX} Sieve Pipe - should pipe mail received for user2 into '/tmp/pipe-test.out'" { + _run_in_container bash -c 'ls -A /tmp/pipe-test.out' + assert_success + _should_output_number_of_lines 1 +} + +# Only test coverage for feature is to check that the service is listening on the expected port: +# https://doc.dovecot.org/admin_manual/pigeonhole_managesieve_server/ +@test "${TEST_NAME_PREFIX} ENV 'ENABLE_MANAGESIEVE' - should have enabled service on port 4190" { + _run_in_container bash -c 'nc -z 0.0.0.0 4190' + assert_success +} diff --git a/test/tests/parallel/set3/smtp-delivery.bats b/test/tests/parallel/set3/smtp-delivery.bats new file mode 100644 index 00000000..bea3127d --- /dev/null +++ b/test/tests/parallel/set3/smtp-delivery.bats @@ -0,0 +1,256 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +TEST_NAME_PREFIX='SMTP Delivery:' +CONTAINER_NAME='dms-test_smtp-delivery' + +function setup_file() { + init_with_defaults + + local CONTAINER_ARGS_ENV_CUSTOM=( + # Required not only for authentication, but delivery in these tests (via nc): + # TODO: Properly test with DNS records configured and separate container for + # handling delivery (without nc). This would remove the need for this ENV: + --env PERMIT_DOCKER=container + # NOTE: Authentication is rejected due to default POSTSCREEN_ACTION=enforce and PERMIT_DOCKER=none + # Non-issue when PERMIT_DOCKER is not the default `none` for these nc 0.0.0.0 tests: + # --env POSTSCREEN_ACTION=ignore + + # Required for test 'rejects spam': + --env ENABLE_SPAMASSASSIN=1 + --env SPAMASSASSIN_SPAM_TO_INBOX=0 + # Either SA_TAG or ENABLE_SRS=1 will pass the spamassassin X-SPAM headers test case: + --env SA_TAG=-5.0 + + # Only relevant for tests expecting to match `external.tld=`?: + # NOTE: Disabling support in tests as it doesn't seem relevant to the test, but misleading.. + # `spam@external.tld` and `user@external.tld` are delivered with with the domain-part changed to `example.test` + # https://github.com/roehling/postsrsd + # --env ENABLE_SRS=1 + # Required for ENABLE_SRS=1: + # --ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" + + # Required for tests: 'redirects mail to external aliases' + 'rejects spam': + --env ENABLE_AMAVIS=1 + + # TODO: Relocate relevant tests to the separated clamav test file: + # Originally relevant, but tests expecting ClamAV weren't properly implemented and didn't raise a failure. + # --env ENABLE_CLAMAV=1 + ) + + # Required for 'delivers mail to existing alias with recipient delimiter': + mv "${TEST_TMP_CONFIG}/smtp-delivery/postfix-main.cf" "${TEST_TMP_CONFIG}/postfix-main.cf" + mv "${TEST_TMP_CONFIG}/smtp-delivery/dovecot.cf" "${TEST_TMP_CONFIG}/dovecot.cf" + + common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' + + _run_in_container setup email add 'added@localhost.localdomain' 'mypassword' + assert_success + wait_until_change_detection_event_completes "${CONTAINER_NAME}" + + wait_for_smtp_port_in_container "${CONTAINER_NAME}" + + # 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 + # _run_in_container bash -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + + # Required for 'delivers mail to existing alias': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-external.txt' + # Required for 'delivers mail to existing alias with recipient delimiter': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-recipient-delimiter.txt' + # Required for 'delivers mail to existing catchall': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-catchall-local.txt' + # Required for 'delivers mail to regexp alias': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-local.txt' + + # Required for 'rejects mail to unknown user': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/non-existing-user.txt' + # Required for 'redirects mail to external aliases': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-external.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-local.txt' + # Required for 'rejects spam': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt' + + # Required for 'delivers mail to existing account': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user2.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user3.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-added.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user-and-cc-local-alias.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-pipe.txt' + _run_in_container bash -c 'sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt' + + wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" +} + +function teardown_file() { _default_teardown ; } + +@test "${TEST_NAME_PREFIX} should successfully authenticate with good password (plain)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} should fail to authenticate with wrong password (plain)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain-wrong.txt' + assert_output --partial 'authentication failed' + assert_success +} + +@test "${TEST_NAME_PREFIX} should successfully authenticate with good password (login)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} should fail to authenticate with wrong password (login)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt' + assert_output --partial 'authentication failed' + assert_success +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should successfully authenticate with good password (plain)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should fail to authenticate with wrong password (plain)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain-wrong.txt' + assert_success + assert_output --partial 'authentication failed' +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should successfully authenticate with good password (login)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should fail to authenticate with wrong password (login)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login-wrong.txt' + assert_success + assert_output --partial 'authentication failed' +} + +# TODO: Add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) +@test "${TEST_NAME_PREFIX} delivers mail to existing account" { + _run_in_container bash -c "grep 'postfix/lmtp' /var/log/mail/mail.log | grep 'status=sent' | grep ' Saved)' | sed 's/.* to=' + assert_output --partial '6 ' + assert_output --partial '1 , orig_to=' + assert_output --partial '1 ' + assert_output --partial '2 ' + assert_output --partial '1 ' + _should_output_number_of_lines 6 + + # NOTE: Requires ClamAV enabled and to send `amavis-virus` template: + # assert_output --partial '1 , orig_to=' + # _should_output_number_of_lines 7 +} + +@test "${TEST_NAME_PREFIX} delivers mail to existing alias" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} delivers mail to existing alias with recipient delimiter" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 + + _run_in_container grep 'to=' /var/log/mail/mail.log + assert_success + refute_output --partial 'status=bounced' +} + +@test "${TEST_NAME_PREFIX} delivers mail to existing catchall" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} delivers mail to regexp alias" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} user1 should have received 8 mails" { + _run_in_container bash -c "grep Subject /var/mail/localhost.localdomain/user1/new/* | sed 's/.*Subject: //g' | sed 's/\.txt.*//g' | sed 's/VIRUS.*/VIRUS/g' | sort" + assert_success + + assert_output --partial 'Root Test Message' + assert_output --partial 'Test Message existing-alias-external' + assert_output --partial 'Test Message existing-alias-recipient-delimiter' + assert_output --partial 'Test Message existing-catchall-local' + assert_output --partial 'Test Message existing-regexp-alias-local' + assert_output --partial 'Test Message existing-user-and-cc-local-alias' + assert_output --partial 'Test Message existing-user1' + assert_output --partial 'Test Message sieve-spam-folder' + _should_output_number_of_lines 8 + + # The virus mail has three subject lines + # NOTE: Requires ClamAV enabled and to send amavis-virus: + # assert_output --partial 'Test Message amavis-virus' # Should verify two lines expected with this content + # assert_output --partial 'VIRUS' + # _should_output_number_of_lines 11 +} + +@test "${TEST_NAME_PREFIX} rejects mail to unknown user" { + _run_in_container grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail/mail.log + assert_success + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} redirects mail to external aliases" { + _run_in_container bash -c "grep 'Passed CLEAN {RelayedInbound}' /var/log/mail/mail.log | grep -- '-> '" + assert_success + assert_output --partial ' -> ' + _should_output_number_of_lines 2 + # assert_output --partial 'external.tld=user@example.test> -> ' +} + +# TODO: Add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) +@test "${TEST_NAME_PREFIX} rejects spam" { + _run_in_container grep 'Blocked SPAM {NoBounceInbound,Quarantined}' /var/log/mail/mail.log + assert_success + assert_output --partial ' -> ' + _should_output_number_of_lines 1 + + # Amavis log line with SPAMASSASSIN_SPAM_TO_INBOX=0 + grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}' /var/log/mail/mail.log: + # Amavis log line with SPAMASSASSIN_SPAM_TO_INBOX=1 + grep 'Blocked SPAM {NoBounceInbound,Quarantined}' /var/log/mail/mail.log: + # -> + # Amavis log line with ENABLE_SRS=1 changes the domain-part to match in a log: + # -> + # assert_output --partial 'external.tld=spam@example.test> -> ' +} + +@test "${TEST_NAME_PREFIX} SA - All registered domains should receive mail with spam headers (X-Spam)" { + _run_in_container grep -ir 'X-Spam-' /var/mail/localhost.localdomain/user1/new + assert_success + + _run_in_container grep -ir 'X-Spam-' /var/mail/otherdomain.tld/user2/new + assert_success +} + +# 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 "${TEST_NAME_PREFIX} not advertising smtputf8" { + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/smtp-ehlo.txt' + assert_success + refute_output --partial 'SMTPUTF8' +} + +@test "${TEST_NAME_PREFIX} mail for root was delivered" { + _run_in_container grep -R 'Subject: Root Test Message' /var/mail/localhost.localdomain/user1/new/ + assert_success +} diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index 71c70d47..2dfd890c 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -1,77 +1,37 @@ -load "${REPOSITORY_ROOT}/test/test_helper/common" +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +CONTAINER_NAME='mail' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail) - mv "${PRIVATE_CONFIG}/user-patches/user-patches.sh" "${PRIVATE_CONFIG}/user-patches.sh" + init_with_defaults - # `LOG_LEVEL=debug` required for using `wait_until_change_detection_event_completes()` - docker run --rm -d --name mail \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -v "$(pwd)/test/onedir":/var/mail-state \ - -e AMAVIS_LOGLEVEL=2 \ - -e CLAMAV_MESSAGE_SIZE_LIMIT=30M \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_MANAGESIEVE=1 \ - -e ENABLE_QUOTAS=1 \ - -e ENABLE_SPAMASSASSIN=1 \ - -e ENABLE_SRS=1 \ - -e ENABLE_UPDATE_CHECK=0 \ - -e LOG_LEVEL='debug' \ - -e PERMIT_DOCKER=host \ - -e PFLOGSUMM_TRIGGER=logrotate \ - -e REPORT_RECIPIENT=user1@localhost.localdomain \ - -e REPORT_SENDER=report1@mail.my-domain.com \ - -e SA_KILL=3.0 \ - -e SA_SPAM_SUBJECT="SPAM: " \ - -e SA_TAG=-5.0 \ - -e SA_TAG2=2.0 \ - -e SPAMASSASSIN_SPAM_TO_INBOX=0 \ - -e SPOOF_PROTECTION=1 \ - -e SSL_TYPE='snakeoil' \ - -e VIRUSMAILS_DELETE_DELAY=7 \ - --hostname mail.my-domain.com \ - --tty \ - --ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" \ - --health-cmd "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1" \ - "${NAME}" + mv "${TEST_TMP_CONFIG}/user-patches/user-patches.sh" "${TEST_TMP_CONFIG}/user-patches.sh" - wait_for_finished_setup_in_container mail + 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 + --env REPORT_RECIPIENT=user1@localhost.localdomain + --env REPORT_SENDER=report1@mail.example.test + --env SPOOF_PROTECTION=1 + --env SSL_TYPE='snakeoil' + --ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" + --health-cmd "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1" + ) + common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' # generate accounts after container has been started docker exec mail setup email add 'added@localhost.localdomain' 'mypassword' docker exec mail setup email add 'pass@localhost.localdomain' 'may be \a `p^a.*ssword' - # setup sieve - docker cp "${PRIVATE_CONFIG}/sieve/dovecot.sieve" mail:/var/mail/localhost.localdomain/user1/.dovecot.sieve - # this relies on the checksum file being updated after all changes have been applied wait_until_change_detection_event_completes mail wait_for_service mail postfix wait_for_smtp_port_in_container mail - - # The first mail sent leverages an assert for better error output if a failure occurs: - run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" - assert_success - - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-external.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-local.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-recipient-delimiter.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user2.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user3.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-added.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user-and-cc-local-alias.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-external.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-local.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-catchall-local.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-pipe.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/non-existing-user.txt" - docker exec mail /bin/sh -c "sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt" - - wait_for_empty_mail_queue_in_container mail } teardown_file() { @@ -197,140 +157,6 @@ teardown_file() { assert_success } -# -# smtp -# - -@test "checking smtp: authentication works with good password (plain)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: authentication fails with wrong password (plain)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain-wrong.txt" - assert_output --partial 'authentication failed' - assert_success -} - -@test "checking smtp: authentication works with good password (login)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: authentication fails with wrong password (login)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt" - assert_output --partial 'authentication failed' - assert_success -} - -@test "checking smtp: added user authentication works with good password (plain)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: added user authentication fails with wrong password (plain)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain-wrong.txt | grep 'authentication failed'" - assert_success -} - -@test "checking smtp: added user authentication works with good password (login)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: added user authentication fails with wrong password (login)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login-wrong.txt | grep 'authentication failed'" - assert_success -} - -# TODO add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) -@test "checking smtp: delivers mail to existing account" { - run docker exec mail /bin/sh -c "grep 'postfix/lmtp' /var/log/mail/mail.log | grep 'status=sent' | grep ' Saved)' | sed 's/.* to= - 6 - 1 , orig_to= - 1 , orig_to= - 1 - 2 - 1 -EOF -} - -@test "checking smtp: delivers mail to existing alias" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias with recipient delimiter" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 - - run docker exec mail /bin/sh -c "grep 'to=' /var/log/mail/mail.log | grep 'status=bounced'" - assert_failure -} - -@test "checking smtp: delivers mail to existing catchall" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to regexp alias" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: user1 should have received 9 mails" { - run docker exec mail /bin/sh -c "grep Subject /var/mail/localhost.localdomain/user1/new/* | sed 's/.*Subject: //g' | sed 's/\.txt.*//g' | sed 's/VIRUS.*/VIRUS/g' | sort" - assert_success - # 9 messages, the virus mail has three subject lines - cat <<'EOF' | assert_output -Root Test Message -Test Message amavis-virus -Test Message amavis-virus -Test Message existing-alias-external -Test Message existing-alias-recipient-delimiter -Test Message existing-catchall-local -Test Message existing-regexp-alias-local -Test Message existing-user-and-cc-local-alias -Test Message existing-user1 -Test Message sieve-spam-folder -VIRUS -EOF -} - -@test "checking smtp: rejects mail to unknown user" { - run docker exec mail /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: redirects mail to external aliases" { - run docker exec mail /bin/sh -c "grep -- '-> ' /var/log/mail/mail.log* | grep RelayedInbound | wc -l" - assert_success - assert_output 2 -} - -# TODO add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) -@test "checking smtp: rejects spam" { - run docker exec mail /bin/sh -c "grep 'Blocked SPAM' /var/log/mail/mail.log | grep external.tld=spam@my-domain.com | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: not advertising smtputf8" { - # 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 - run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/smtp-ehlo.txt | grep SMTPUTF8 | wc -l" - assert_success - assert_output 0 -} - # # accounts # @@ -355,7 +181,7 @@ EOF } @test "checking accounts: user mail folder for user3" { - run docker exec mail /bin/bash -c "ls -d /var/mail/localhost.localdomain/user3/mail" + run docker exec mail /bin/bash -c "ls -d /var/mail/localhost.localdomain/user3" assert_success } @@ -381,46 +207,6 @@ EOF assert_line --index 2 "otherdomain.tld" } -@test "checking postfix: main.cf overrides" { - run docker exec mail grep -q 'max_idle = 600s' /tmp/docker-mailserver/postfix-main.cf - assert_success - run docker exec mail grep -q 'readme_directory = /tmp' /tmp/docker-mailserver/postfix-main.cf - assert_success -} - -@test "checking postfix: master.cf overrides" { - run docker exec mail grep -q 'submission/inet/smtpd_sasl_security_options=noanonymous' /tmp/docker-mailserver/postfix-master.cf - assert_success -} - -# -# dovecot -# - -@test "checking dovecot: config additions" { - run docker exec mail grep -q 'mail_max_userip_connections = 69' /tmp/docker-mailserver/dovecot.cf - assert_success - run docker exec mail /bin/sh -c "doveconf | grep 'mail_max_userip_connections = 69'" - assert_success - assert_output 'mail_max_userip_connections = 69' -} - -# -# spamassassin -# - -@test "checking spamassassin: should be listed in amavis when enabled" { - run docker exec mail /bin/sh -c "grep -i 'ANTI-SPAM-SA code' /var/log/mail/mail.log | grep 'NOT loaded'" - assert_failure -} - -@test "checking spamassassin: all registered domains should see spam headers" { - run docker exec mail /bin/sh -c "grep -ir 'X-Spam-' /var/mail/localhost.localdomain/user1/new" - assert_success - run docker exec mail /bin/sh -c "grep -ir 'X-Spam-' /var/mail/otherdomain.tld/user2/new" - assert_success -} - # # postsrsd # @@ -443,7 +229,7 @@ EOF @test "checking SRS: fallback to hostname is handled correctly" { - run docker exec mail grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd + run docker exec mail grep "SRS_DOMAIN=example.test" /etc/default/postsrsd assert_success } @@ -510,13 +296,13 @@ EOF @test "checking system: sets the server fqdn" { run docker exec mail hostname assert_success - assert_output "mail.my-domain.com" + assert_output "mail.example.test" } @test "checking system: sets the server domain name in /etc/mailname" { run docker exec mail cat /etc/mailname assert_success - assert_output "my-domain.com" + assert_output "example.test" } @test "checking system: postfix should not log to syslog" { @@ -558,33 +344,6 @@ zip EOF } - -# -# sieve -# - -@test "checking sieve: user1 should have received 1 email in folder INBOX.spam" { - run docker exec mail /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/.INBOX.spam/new | wc -l" - assert_success - assert_output 1 -} - -@test "checking manage sieve: server is ready when ENABLE_MANAGESIEVE has been set" { - run docker exec mail /bin/bash -c "nc -z 0.0.0.0 4190" - assert_success -} - -@test "checking sieve: user2 should have piped 1 email to /tmp/" { - run docker exec mail /bin/sh -c "ls -A /tmp/pipe-test.out | wc -l" - assert_success - assert_output 1 -} - -@test "checking sieve global: user1 should have gotten a copy of his spam mail" { - run docker exec mail /bin/sh -c "grep 'Spambot ' -R /var/mail/localhost.localdomain/user1/new/" - assert_success -} - # # accounts # @@ -656,7 +415,7 @@ EOF @test "checking accounts: listmailuser (quotas enabled)" { run docker exec mail /bin/sh -c "sed -i '/ENABLE_QUOTAS=0/d' /etc/dms-settings; listmailuser | head -n 1" assert_success - assert_output '* user1@localhost.localdomain ( 10K / ~ ) [0%]' + assert_output '* user1@localhost.localdomain ( 0 / ~ ) [0%]' } @test "checking accounts: no error is generated when deleting a user if /tmp/docker-mailserver/postfix-accounts.cf is missing" { @@ -914,7 +673,7 @@ EOF # postfix @test "checking dovecot: postmaster address" { - run docker exec mail /bin/sh -c "grep 'postmaster_address = postmaster@my-domain.com' /etc/dovecot/conf.d/15-lda.conf" + run docker exec mail /bin/sh -c "grep 'postmaster_address = postmaster@example.test' /etc/dovecot/conf.d/15-lda.conf" assert_success } @@ -941,10 +700,10 @@ EOF run docker exec mail grep "Subject: Postfix Summary for " /var/mail/localhost.localdomain/user1/new/ -R assert_success # check sender is the one specified in REPORT_SENDER - run docker exec mail grep "From: report1@mail.my-domain.com" /var/mail/localhost.localdomain/user1/new/ -R + run docker exec mail grep "From: report1@mail.example.test" /var/mail/localhost.localdomain/user1/new/ -R assert_success # check sender is not the default one. - run docker exec mail grep "From: mailserver-report@mail.my-domain.com" /var/mail/localhost.localdomain/user1/new/ -R + run docker exec mail grep "From: mailserver-report@mail.example.test" /var/mail/localhost.localdomain/user1/new/ -R assert_failure } @@ -971,12 +730,3 @@ EOF run docker exec mail /bin/bash -c "pkill opendmarc && sleep 10 && ps aux --forest | grep -v grep | grep '/usr/sbin/opendmarc'" assert_success } - -# -# root mail delivery -# - -@test "checking that mail for root was delivered" { - run docker exec mail grep "Subject: Root Test Message" /var/mail/localhost.localdomain/user1/new/ -R - assert_success -}