From 3b4f44e8377598eaa03b88c907dbb9e2f70332ac Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 30 May 2022 12:53:30 +1200 Subject: [PATCH] tests(fix): Adjust for local testing conditions (#2606) * tests(fix): Increase some timeouts Running tests locally via a VM these tests would fail sometimes due to the time from being queued and Amavis actually processing being roughly around 30 seconds. There should be no harm in raising this to 60 seconds, other than delaying a failure case which will ripple through other time sensitive tests. It's better to pass when functionality is actually correct but just needs a bit longer to complete. * tests(fix): Don't setup an invalid hostname During container startup `helpers/dns.sh` would panic with `hostname -f` failing. Dropping `--domainname` for this container is fine and does not affect the point of it's test. --- It's unclear why this does not occur in CI. Possibly changes within the docker daemon since as CI runs docker on Ubuntu 20.04? (2020). For clarity, this may be equivalent to setting a hostname of `domain.com.domain.com`, or `--hostname` value truncated the NIS domain (`--domainname`) of the same value. IIRC, it would still fail with both options using different values if `--hostname` was multi-label. I believe I've documented how non-deterministic these options can be across different environments. `--hostname` should be preferred. There doesn't seem to be any reason to actually need `--domainname` (which is NIS domain name, unrelated to the DNS domain name). We still need to properly investigate reworking our ENV support that `dns.sh` manages. --- Containers were also not removing themselves after failures either (missing teardown). Which would cause problems when running tests again. * chore: Normalize white-space Sets a consistent indent size of 2 spaces. Previously this varied a fair bit, sometimes with tabs or mixed tabs and spaces. Some formatting with blank lines. Easier to review with white-space in diff ignored. Some minor edits besides blank lines, but no change in functionality. * fix: `setup.sh` target container under test Some of the `setup.sh` commands did not specify the container which was problematic if another `docker-mailserver` container was running, causing test failures. This probably doesn't help with `test/no_container.bats`, but at least prevents `test/tests.bats` failing at this point. --- test/default_relay_host.bats | 20 +- test/dovecot_inet_protocol.bats | 28 +- test/helper-functions.bats | 22 +- test/mail_changedetector.bats | 25 +- test/mail_disabled_clamav_spamassassin.bats | 20 +- test/mail_dnsbl.bats | 24 +- test/mail_fail2ban.bats | 37 +- test/mail_fetchmail.bats | 22 +- test/mail_fetchmail_parallel.bats | 24 +- test/mail_hostname.bats | 53 ++- test/mail_lmtp_ip.bats | 15 +- test/mail_pop3.bats | 13 +- test/mail_postfix_inet.bats | 34 +- test/mail_postscreen.bats | 33 +- test/mail_privacy.bats | 3 + test/mail_quotas_disabled.bats | 38 +- test/mail_smtponly.bats | 24 +- test/mail_spam_bounced.bats | 3 +- test/mail_spam_junk_folder.bats | 46 +-- test/mail_special_use_folders.bats | 28 +- test/mail_ssl_manual.bats | 148 ++++---- test/mail_time.bats | 2 +- test/mail_tls_dhparams.bats | 95 +++-- test/mail_undef_spam_subject.bats | 70 ++-- test/mail_with_imap.bats | 13 +- test/mail_with_ldap.bats | 71 ++-- test/mail_with_mdbox.bats | 28 +- test/mail_with_postgrey.bats | 36 +- ...ail_with_postgrey_disabled_by_default.bats | 20 +- test/mail_with_relays.bats | 37 +- test/mail_with_sdbox.bats | 30 +- test/open_dkim.bats | 125 ++++--- test/permit_docker.bats | 92 ++--- test/security_tls_cipherlists.bats | 315 ++++++++-------- test/sedfile.bats | 4 +- test/test_helper.bats | 348 +++++++++--------- test/test_helper/common.bash | 163 ++++---- test/tests.bats | 55 +-- 38 files changed, 1166 insertions(+), 998 deletions(-) diff --git a/test/default_relay_host.bats b/test/default_relay_host.bats index be8008c2..62976757 100644 --- a/test/default_relay_host.bats +++ b/test/default_relay_host.bats @@ -1,15 +1,17 @@ load 'test_helper/common' function setup() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container relay-hosts) - docker run -d --name mail_with_default_relay \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e DEFAULT_RELAY_HOST=default.relay.host.invalid:25 \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container relay-hosts) + + docker run -d --name mail_with_default_relay \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e DEFAULT_RELAY_HOST=default.relay.host.invalid:25 \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -h mail.my-domain.com -t "${NAME}" + wait_for_finished_setup_in_container mail_with_default_relay } diff --git a/test/dovecot_inet_protocol.bats b/test/dovecot_inet_protocol.bats index dfb63c8b..ab31db4a 100644 --- a/test/dovecot_inet_protocol.bats +++ b/test/dovecot_inet_protocol.bats @@ -9,23 +9,23 @@ function setup_file() { IPV4="mail_dovecot_ipv4" IPV6="mail_dovecot_ipv6" - docker run --rm -d --name "${ALL}" \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -e DOVECOT_INET_PROTOCOLS= \ - -h mail.my-domain.com \ - -t "${NAME}" + docker run --rm -d --name "${ALL}" \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -e DOVECOT_INET_PROTOCOLS= \ + -h mail.my-domain.com \ + -t "${NAME}" docker run --rm -d --name "${IPV4}" \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -e DOVECOT_INET_PROTOCOLS=ipv4 \ - -h mail.my-domain.com \ - -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -e DOVECOT_INET_PROTOCOLS=ipv4 \ + -h mail.my-domain.com \ + -t "${NAME}" - docker run --rm -d --name "${IPV6}" \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -e DOVECOT_INET_PROTOCOLS=ipv6 \ - -h mail.my-domain.com \ - -t "${NAME}" + docker run --rm -d --name "${IPV6}" \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -e DOVECOT_INET_PROTOCOLS=ipv6 \ + -h mail.my-domain.com \ + -t "${NAME}" } @test 'checking dovecot IP configuration' { diff --git a/test/helper-functions.bats b/test/helper-functions.bats index 2145a1d7..2c10c9e1 100644 --- a/test/helper-functions.bats +++ b/test/helper-functions.bats @@ -1,15 +1,17 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_helper_functions \ - --cap-add=NET_ADMIN \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_FETCHMAIL=1 \ - -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_helper_functions + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_helper_functions \ + --cap-add=NET_ADMIN \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_FETCHMAIL=1 \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail_helper_functions } function teardown_file() { @@ -19,8 +21,10 @@ function teardown_file() { @test "check helper functions (network.sh): _sanitize_ipv4_to_subnet_cidr" { run docker exec mail_helper_functions bash -c "source /usr/local/bin/helpers/index.sh; _sanitize_ipv4_to_subnet_cidr 255.255.255.255/0" assert_output "0.0.0.0/0" + run docker exec mail_helper_functions bash -c "source /usr/local/bin/helpers/index.sh; _sanitize_ipv4_to_subnet_cidr 192.168.255.14/20" assert_output "192.168.240.0/20" + run docker exec mail_helper_functions bash -c "source /usr/local/bin/helpers/index.sh; _sanitize_ipv4_to_subnet_cidr 192.168.255.14/32" assert_output "192.168.255.14/32" } diff --git a/test/mail_changedetector.bats b/test/mail_changedetector.bats index 7e6f73dc..3a3be488 100644 --- a/test/mail_changedetector.bats +++ b/test/mail_changedetector.bats @@ -9,17 +9,18 @@ function setup_file() { PRIVATE_CONFIG=$(duplicate_config_for_container . mail_changedetector_one) docker run -d --name mail_changedetector_one \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e LOG_LEVEL=trace \ - -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_changedetector_one + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e LOG_LEVEL=trace \ + -h mail.my-domain.com -t "${NAME}" docker run -d --name mail_changedetector_two \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e LOG_LEVEL=trace \ - -h mail.my-domain.com -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e LOG_LEVEL=trace \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail_changedetector_one wait_for_finished_setup_in_container mail_changedetector_two } @@ -36,11 +37,13 @@ function teardown_file() { @test "checking changedetector: can detect changes & between two containers using same config" { echo "" >> "$(private_config_path mail_changedetector_one)/postfix-accounts.cf" sleep 25 + run docker exec mail_changedetector_one /bin/bash -c "supervisorctl tail -3000 changedetector" assert_output --partial "postfix: stopped" assert_output --partial "postfix: started" assert_output --partial "Change detected" assert_output --partial "Removed lock" + run docker exec mail_changedetector_two /bin/bash -c "supervisorctl tail -3000 changedetector" assert_output --partial "postfix: stopped" assert_output --partial "postfix: started" @@ -54,10 +57,12 @@ function teardown_file() { echo "" >> "$(private_config_path mail_changedetector_one)/postfix-accounts.cf" run docker exec mail_changedetector_two /bin/bash -c "supervisorctl start changedetector" sleep 15 + run docker exec mail_changedetector_one /bin/bash -c "supervisorctl tail changedetector" assert_output --partial "another execution of 'check-for-changes.sh' is happening" run docker exec mail_changedetector_two /bin/bash -c "supervisorctl tail changedetector" assert_output --partial "another execution of 'check-for-changes.sh' is happening" + # Ensure starting a new check-for-changes.sh instance (restarting here) doesn't delete the lock docker exec mail_changedetector_two /bin/bash -c "rm -f /var/log/supervisor/changedetector.log" run docker exec mail_changedetector_two /bin/bash -c "supervisorctl restart changedetector" @@ -72,9 +77,11 @@ function teardown_file() { docker exec mail_changedetector_one /bin/bash -c "touch /tmp/docker-mailserver/check-for-changes.sh.lock" echo "" >> "$(private_config_path mail_changedetector_one)/postfix-accounts.cf" sleep 15 + run docker exec mail_changedetector_one /bin/bash -c "supervisorctl tail changedetector" assert_output --partial "another execution of 'check-for-changes.sh' is happening" sleep 65 + run docker exec mail_changedetector_one /bin/bash -c "supervisorctl tail -3000 changedetector" assert_output --partial "removing stale lock file" } diff --git a/test/mail_disabled_clamav_spamassassin.bats b/test/mail_disabled_clamav_spamassassin.bats index 79190462..13d3e73e 100644 --- a/test/mail_disabled_clamav_spamassassin.bats +++ b/test/mail_disabled_clamav_spamassassin.bats @@ -1,15 +1,17 @@ load 'test_helper/common' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run --rm -d --name mail_disabled_clamav_spamassassin \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_SPAMASSASSIN=0 \ - -e AMAVIS_LOGLEVEL=2 \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run --rm -d --name mail_disabled_clamav_spamassassin \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_CLAMAV=0 \ + -e ENABLE_SPAMASSASSIN=0 \ + -e AMAVIS_LOGLEVEL=2 \ + -h mail.my-domain.com -t "${NAME}" + # TODO: find a better way to know when we have waited long enough # for ClamAV to should have come up, if it were enabled wait_for_smtp_port_in_container mail_disabled_clamav_spamassassin diff --git a/test/mail_dnsbl.bats b/test/mail_dnsbl.bats index c1043154..9612f79e 100644 --- a/test/mail_dnsbl.bats +++ b/test/mail_dnsbl.bats @@ -7,20 +7,20 @@ function setup_file() { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . "${CONTAINER}") - docker run --rm -d --name "${CONTAINER}" \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -e ENABLE_DNSBL=1 \ - -h mail.my-domain.com \ - -t "${NAME}" + docker run --rm -d --name "${CONTAINER}" \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -e ENABLE_DNSBL=1 \ + -h mail.my-domain.com \ + -t "${NAME}" - docker run --rm -d --name "${CONTAINER2}" \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -e ENABLE_DNSBL=0 \ - -h mail.my-domain.com \ - -t "${NAME}" + docker run --rm -d --name "${CONTAINER2}" \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -e ENABLE_DNSBL=0 \ + -h mail.my-domain.com \ + -t "${NAME}" - wait_for_smtp_port_in_container "${CONTAINER}" - wait_for_smtp_port_in_container "${CONTAINER2}" + wait_for_smtp_port_in_container "${CONTAINER}" + wait_for_smtp_port_in_container "${CONTAINER2}" } # ENABLE_DNSBL=1 diff --git a/test/mail_fail2ban.bats b/test/mail_fail2ban.bats index fa5ce29f..71735c5d 100644 --- a/test/mail_fail2ban.bats +++ b/test/mail_fail2ban.bats @@ -1,29 +1,28 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run --rm -d --name mail_fail2ban \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_FAIL2BAN=1 \ - -e POSTSCREEN_ACTION=ignore \ - --cap-add=NET_ADMIN \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + docker run --rm -d --name mail_fail2ban \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_FAIL2BAN=1 \ + -e POSTSCREEN_ACTION=ignore \ + --cap-add=NET_ADMIN \ + -h mail.my-domain.com -t "${NAME}" - # Create a container which will send wrong authentications and should get banned - docker run --name fail-auth-mailer \ - -e MAIL_FAIL2BAN_IP="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mail_fail2ban)" \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test \ - -d "${NAME}" \ - tail -f /var/log/faillog - - wait_for_finished_setup_in_container mail_fail2ban + # Create a container which will send wrong authentications and should get banned + docker run --name fail-auth-mailer \ + -e MAIL_FAIL2BAN_IP="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mail_fail2ban)" \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test \ + -d "${NAME}" \ + tail -f /var/log/faillog + wait_for_finished_setup_in_container mail_fail2ban } function teardown_file() { - docker rm -f mail_fail2ban fail-auth-mailer + docker rm -f mail_fail2ban fail-auth-mailer } # @@ -102,7 +101,6 @@ function teardown_file() { } @test "checking fail2ban: unban ip works" { - FAIL_AUTH_MAILER_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' fail-auth-mailer) docker exec mail_fail2ban fail2ban-client set postfix-sasl unbanip "${FAIL_AUTH_MAILER_IP}" @@ -131,7 +129,6 @@ function teardown_file() { } @test "checking setup.sh: setup.sh fail2ban" { - run docker exec mail_fail2ban /bin/sh -c "fail2ban-client set dovecot banip 192.0.66.4" run docker exec mail_fail2ban /bin/sh -c "fail2ban-client set dovecot banip 192.0.66.5" diff --git a/test/mail_fetchmail.bats b/test/mail_fetchmail.bats index 07f2fb07..2eac5534 100644 --- a/test/mail_fetchmail.bats +++ b/test/mail_fetchmail.bats @@ -1,19 +1,21 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_fetchmail \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_FETCHMAIL=1 \ - --cap-add=NET_ADMIN \ - -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_fetchmail + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_fetchmail \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_FETCHMAIL=1 \ + --cap-add=NET_ADMIN \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail_fetchmail } function teardown_file() { - docker rm -f mail_fetchmail + docker rm -f mail_fetchmail } # diff --git a/test/mail_fetchmail_parallel.bats b/test/mail_fetchmail_parallel.bats index a1d87d86..68f98c52 100644 --- a/test/mail_fetchmail_parallel.bats +++ b/test/mail_fetchmail_parallel.bats @@ -1,20 +1,22 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_fetchmail_parallel \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_FETCHMAIL=1 \ - -e FETCHMAIL_PARALLEL=1 \ - --cap-add=NET_ADMIN \ - -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_fetchmail_parallel + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_fetchmail_parallel \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_FETCHMAIL=1 \ + -e FETCHMAIL_PARALLEL=1 \ + --cap-add=NET_ADMIN \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail_fetchmail_parallel } function teardown_file() { - docker rm -f mail_fetchmail_parallel + docker rm -f mail_fetchmail_parallel } # diff --git a/test/mail_hostname.bats b/test/mail_hostname.bats index caa5b08f..c32c5260 100644 --- a/test/mail_hostname.bats +++ b/test/mail_hostname.bats @@ -3,25 +3,25 @@ load 'test_helper/common' function setup_file() { local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_override_hostname) - docker run --rm -d --name mail_override_hostname \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e PERMIT_DOCKER=network \ - -e ENABLE_SRS=1 \ - -e OVERRIDE_HOSTNAME=mail.my-domain.com \ - -h unknown.domain.tld \ - -t "${NAME}" + docker run --rm -d --name mail_override_hostname \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e PERMIT_DOCKER=network \ + -e ENABLE_SRS=1 \ + -e OVERRIDE_HOSTNAME=mail.my-domain.com \ + -h unknown.domain.tld \ + -t "${NAME}" PRIVATE_CONFIG_TWO=$(duplicate_config_for_container . mail_non_subdomain_hostname) - docker run --rm -d --name mail_non_subdomain_hostname \ - -v "${PRIVATE_CONFIG_TWO}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e PERMIT_DOCKER=network \ - -e ENABLE_SRS=1 \ - --hostname domain.com \ - --domainname domain.com \ - -t "${NAME}" + docker run --rm -d --name mail_non_subdomain_hostname \ + -v "${PRIVATE_CONFIG_TWO}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e PERMIT_DOCKER=network \ + -e ENABLE_SRS=1 \ + --hostname domain.com \ + -t "${NAME}" PRIVATE_CONFIG_THREE=$(duplicate_config_for_container . mail_srs_domainname) docker run --rm -d --name mail_srs_domainname \ @@ -45,11 +45,8 @@ function setup_file() { -t "${NAME}" wait_for_smtp_port_in_container mail_override_hostname - wait_for_smtp_port_in_container mail_non_subdomain_hostname - wait_for_smtp_port_in_container mail_srs_domainname - wait_for_smtp_port_in_container mail_domainname # postfix virtual transport lmtp @@ -57,6 +54,10 @@ function setup_file() { docker exec mail_non_subdomain_hostname /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt" } +function teardown_file() { + docker rm -f mail_override_hostname mail_non_subdomain_hostname mail_srs_domainname mail_domainname +} + @test "checking SRS: SRS_DOMAINNAME is used correctly" { repeat_until_success_or_timeout 15 docker exec mail_srs_domainname grep "SRS_DOMAIN=srs.my-domain.com" /etc/default/postsrsd } @@ -73,16 +74,22 @@ function setup_file() { @test "checking configuration: hostname/domainname override: check overriden hostname is applied to all configs" { run docker exec mail_override_hostname /bin/bash -c "cat /etc/mailname | grep my-domain.com" assert_success + run docker exec mail_override_hostname /bin/bash -c "postconf -n | grep mydomain | grep my-domain.com" assert_success + run docker exec mail_override_hostname /bin/bash -c "postconf -n | grep myhostname | grep mail.my-domain.com" assert_success + run docker exec mail_override_hostname /bin/bash -c "doveconf | grep hostname | grep mail.my-domain.com" assert_success + run docker exec mail_override_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep AuthservID | grep mail.my-domain.com" assert_success + run docker exec mail_override_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep TrustedAuthservIDs | grep mail.my-domain.com" assert_success + run docker exec mail_override_hostname /bin/bash -c "cat /etc/amavis/conf.d/05-node_id | grep myhostname | grep mail.my-domain.com" assert_success } @@ -95,6 +102,7 @@ function setup_file() { @test "checking configuration: hostname/domainname override: check headers of received mail" { run docker exec mail_override_hostname /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/new | wc -l | grep 1" assert_success + run docker exec mail_override_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep mail.my-domain.com" assert_success @@ -125,16 +133,22 @@ function setup_file() { @test "checking configuration: non-subdomain: check overriden hostname is applied to all configs" { run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/mailname | grep domain.com" assert_success + run docker exec mail_non_subdomain_hostname /bin/bash -c "postconf -n | grep mydomain | grep domain.com" assert_success + run docker exec mail_non_subdomain_hostname /bin/bash -c "postconf -n | grep myhostname | grep domain.com" assert_success + run docker exec mail_non_subdomain_hostname /bin/bash -c "doveconf | grep hostname | grep domain.com" assert_success + run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep AuthservID | grep domain.com" assert_success + run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep TrustedAuthservIDs | grep domain.com" assert_success + run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/amavis/conf.d/05-node_id | grep myhostname | grep domain.com" assert_success } @@ -147,6 +161,7 @@ function setup_file() { @test "checking configuration: non-subdomain: check headers of received mail" { run docker exec mail_non_subdomain_hostname /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/new | wc -l | grep 1" assert_success + run docker exec mail_non_subdomain_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep domain.com" assert_success } diff --git a/test/mail_lmtp_ip.bats b/test/mail_lmtp_ip.bats index 341bd553..91981d12 100644 --- a/test/mail_lmtp_ip.bats +++ b/test/mail_lmtp_ip.bats @@ -1,10 +1,11 @@ load 'test_helper/common' setup_file() { - local PRIVATE_CONFIG PRIVATE_ETC - PRIVATE_CONFIG=$(duplicate_config_for_container .) - PRIVATE_ETC=$(duplicate_config_for_container dovecot-lmtp/ mail_lmtp_ip_dovecot-lmtp) - docker run -d --name mail_lmtp_ip \ + local PRIVATE_CONFIG PRIVATE_ETC + PRIVATE_CONFIG=$(duplicate_config_for_container .) + PRIVATE_ETC=$(duplicate_config_for_container dovecot-lmtp/ mail_lmtp_ip_dovecot-lmtp) + + docker run -d --name mail_lmtp_ip \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ -v "${PRIVATE_ETC}":/etc/dovecot \ -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ @@ -12,12 +13,12 @@ setup_file() { -e POSTFIX_DAGENT=lmtp:127.0.0.1:24 \ -e PERMIT_DOCKER=container \ -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_lmtp_ip + + wait_for_finished_setup_in_container mail_lmtp_ip } - teardown_file() { - docker rm -f mail_lmtp_ip + docker rm -f mail_lmtp_ip } # diff --git a/test/mail_pop3.bats b/test/mail_pop3.bats index fb397658..d4873d47 100644 --- a/test/mail_pop3.bats +++ b/test/mail_pop3.bats @@ -1,16 +1,17 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_pop3 \ + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_pop3 \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ -e ENABLE_POP3=1 \ -e PERMIT_DOCKER=container \ -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_pop3 + wait_for_finished_setup_in_container mail_pop3 } function teardown_file() { @@ -43,10 +44,13 @@ function teardown_file() { @test "checking spamassassin: docker env variables are set correctly (default)" { run docker exec mail_pop3 /bin/sh -c "grep '\$sa_tag_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= 2.0'" assert_success + run docker exec mail_pop3 /bin/sh -c "grep '\$sa_tag2_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= 6.31'" assert_success + run docker exec mail_pop3 /bin/sh -c "grep '\$sa_kill_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= 6.31'" assert_success + run docker exec mail_pop3 /bin/sh -c "grep '\$sa_spam_subject_tag' /etc/amavis/conf.d/20-debian_defaults | grep '= .\*\*\*SPAM\*\*\* .'" assert_success } @@ -58,6 +62,7 @@ function teardown_file() { @test "checking system: /var/log/mail/mail.log is error free" { run docker exec mail_pop3 grep 'non-null host address bits in' /var/log/mail/mail.log assert_failure + run docker exec mail_pop3 grep ': error:' /var/log/mail/mail.log assert_failure } diff --git a/test/mail_postfix_inet.bats b/test/mail_postfix_inet.bats index 3f90b735..24dd463f 100644 --- a/test/mail_postfix_inet.bats +++ b/test/mail_postfix_inet.bats @@ -7,10 +7,11 @@ load 'test_helper/common' @test "checking postfix: inet default" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . ) + docker run -d --name mail_postfix_inet_default \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -h mail.my-domain.com -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -h mail.my-domain.com -t "${NAME}" teardown() { docker rm -f mail_postfix_inet_default; } @@ -24,11 +25,12 @@ load 'test_helper/common' @test "checking postfix: inet all" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . ) + docker run -d --name mail_postfix_inet_all \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e POSTFIX_INET_PROTOCOLS=all \ - -h mail.my-domain.com -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e POSTFIX_INET_PROTOCOLS=all \ + -h mail.my-domain.com -t "${NAME}" teardown() { docker rm -f mail_postfix_inet_all; } @@ -42,11 +44,12 @@ load 'test_helper/common' @test "checking postfix: inet ipv4" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . ) + docker run -d --name mail_postfix_inet_ipv4 \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e POSTFIX_INET_PROTOCOLS=ipv4 \ - -h mail.my-domain.com -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e POSTFIX_INET_PROTOCOLS=ipv4 \ + -h mail.my-domain.com -t "${NAME}" teardown() { docker rm -f mail_postfix_inet_ipv4; } @@ -60,11 +63,12 @@ load 'test_helper/common' @test "checking postfix: inet ipv6" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . ) + docker run -d --name mail_postfix_inet_ipv6 \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e POSTFIX_INET_PROTOCOLS=ipv6 \ - -h mail.my-domain.com -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e POSTFIX_INET_PROTOCOLS=ipv6 \ + -h mail.my-domain.com -t "${NAME}" teardown() { docker rm -f mail_postfix_inet_ipv6; } diff --git a/test/mail_postscreen.bats b/test/mail_postscreen.bats index 166f4d38..d1e3b6e1 100644 --- a/test/mail_postscreen.bats +++ b/test/mail_postscreen.bats @@ -1,30 +1,31 @@ load 'test_helper/common' setup() { - # Getting mail container IP - MAIL_POSTSCREEN_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mail_postscreen) + # Getting mail container IP + MAIL_POSTSCREEN_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mail_postscreen) } setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_postscreen \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e POSTSCREEN_ACTION=enforce \ - --cap-add=NET_ADMIN \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run --name mail_postscreen_sender \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -d "${NAME}" \ - tail -f /var/log/faillog + docker run -d --name mail_postscreen \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e POSTSCREEN_ACTION=enforce \ + --cap-add=NET_ADMIN \ + -h mail.my-domain.com -t "${NAME}" - wait_for_smtp_port_in_container mail_postscreen + docker run --name mail_postscreen_sender \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -d "${NAME}" \ + tail -f /var/log/faillog + + wait_for_smtp_port_in_container mail_postscreen } teardown_file() { - docker rm -f mail_postscreen mail_postscreen_sender + docker rm -f mail_postscreen mail_postscreen_sender } @test "checking postscreen: talk too fast" { diff --git a/test/mail_privacy.bats b/test/mail_privacy.bats index a09a3f32..ba489b04 100644 --- a/test/mail_privacy.bats +++ b/test/mail_privacy.bats @@ -3,6 +3,7 @@ load 'test_helper/common' function setup_file() { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container .) + docker run -d --name mail_privacy \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ @@ -29,9 +30,11 @@ function teardown_file() { # shellcheck disable=SC2016 repeat_until_success_or_timeout 120 docker exec mail_privacy /bin/bash -c '[[ $(ls /var/mail/localhost.localdomain/user1/new | wc -l) -eq 1 ]]' docker logs mail_privacy + run docker exec mail_privacy /bin/sh -c "ls /var/mail/localhost.localdomain/user1/new | wc -l" assert_success assert_output 1 + run docker exec mail_privacy /bin/sh -c 'grep -rE "^User-Agent:" /var/mail/localhost.localdomain/user1/new | wc -l' assert_success assert_output 0 diff --git a/test/mail_quotas_disabled.bats b/test/mail_quotas_disabled.bats index 27577178..3afac83b 100644 --- a/test/mail_quotas_disabled.bats +++ b/test/mail_quotas_disabled.bats @@ -6,30 +6,34 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_no_quotas \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_QUOTAS=0 \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - wait_for_finished_setup_in_container mail_no_quotas + docker run -d --name mail_no_quotas \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_QUOTAS=0 \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail_no_quotas } function teardown_file() { - docker rm -f mail_no_quotas + docker rm -f mail_no_quotas } @test "checking dovecot: (ENABLE_QUOTAS=0) quota plugin is disabled" { - run docker exec mail_no_quotas /bin/sh -c "grep '\$mail_plugins quota' /etc/dovecot/conf.d/10-mail.conf" - assert_failure - run docker exec mail_no_quotas /bin/sh -c "grep '\$mail_plugins imap_quota' /etc/dovecot/conf.d/20-imap.conf" - assert_failure - run docker exec mail_no_quotas ls /etc/dovecot/conf.d/90-quota.conf - assert_failure - run docker exec mail_no_quotas ls /etc/dovecot/conf.d/90-quota.conf.disab - assert_success + run docker exec mail_no_quotas /bin/sh -c "grep '\$mail_plugins quota' /etc/dovecot/conf.d/10-mail.conf" + assert_failure + + run docker exec mail_no_quotas /bin/sh -c "grep '\$mail_plugins imap_quota' /etc/dovecot/conf.d/20-imap.conf" + assert_failure + + run docker exec mail_no_quotas ls /etc/dovecot/conf.d/90-quota.conf + assert_failure + + run docker exec mail_no_quotas ls /etc/dovecot/conf.d/90-quota.conf.disab + assert_success } @test "checking postfix: (ENABLE_QUOTAS=0) dovecot quota absent in postconf" { diff --git a/test/mail_smtponly.bats b/test/mail_smtponly.bats index 083a4245..85bcf493 100644 --- a/test/mail_smtponly.bats +++ b/test/mail_smtponly.bats @@ -1,21 +1,22 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run --rm -d --name mail_smtponly \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e SMTP_ONLY=1 \ - -e PERMIT_DOCKER=network \ - -e OVERRIDE_HOSTNAME=mail.my-domain.com \ - -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - wait_for_finished_setup_in_container mail_smtponly + docker run --rm -d --name mail_smtponly \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e SMTP_ONLY=1 \ + -e PERMIT_DOCKER=network \ + -e OVERRIDE_HOSTNAME=mail.my-domain.com \ + -t "${NAME}" + + wait_for_finished_setup_in_container mail_smtponly } function teardown_file() { - docker rm -f mail_smtponly + docker rm -f mail_smtponly } # @@ -65,6 +66,7 @@ function teardown_file() { @test "checking PERMIT_DOCKER=network: opendmarc/opendkim config" { run docker exec mail_smtponly /bin/sh -c "cat /etc/opendmarc/ignore.hosts | grep '172.16.0.0/12'" assert_success + run docker exec mail_smtponly /bin/sh -c "cat /etc/opendkim/TrustedHosts | grep '172.16.0.0/12'" assert_success } diff --git a/test/mail_spam_bounced.bats b/test/mail_spam_bounced.bats index 7de19585..4848087e 100644 --- a/test/mail_spam_bounced.bats +++ b/test/mail_spam_bounced.bats @@ -43,6 +43,7 @@ function _should_bounce_spam() { run docker exec "${TEST_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" assert_success - run repeat_until_success_or_timeout 20 sh -c "docker logs ${TEST_NAME} | grep 'Blocked SPAM {NoBounceInbound,Quarantined}'" + # message will be added to a queue with varying delay until amavis receives it + run repeat_until_success_or_timeout 60 sh -c "docker logs ${TEST_NAME} | grep 'Blocked SPAM {NoBounceInbound,Quarantined}'" assert_success } diff --git a/test/mail_spam_junk_folder.bats b/test/mail_spam_junk_folder.bats index 23d64c8e..8dcf454f 100644 --- a/test/mail_spam_junk_folder.bats +++ b/test/mail_spam_junk_folder.bats @@ -5,17 +5,18 @@ load 'test_helper/common' # When SPAMASSASSIN_SPAM_TO_INBOX=1, spam messages must be delivered and eventually (MOVE_SPAM_TO_JUNK=1) moved to the Junk folder. @test "checking amavis: spam message is delivered and moved to the Junk folder (MOVE_SPAM_TO_JUNK=1)" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_spam_moved_junk) - docker run -d --name mail_spam_moved_junk \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_SPAMASSASSIN=1 \ - -e MOVE_SPAM_TO_JUNK=1 \ - -e PERMIT_DOCKER=container \ - -e SA_SPAM_SUBJECT="SPAM: " \ - -e SPAMASSASSIN_SPAM_TO_INBOX=1 \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_spam_moved_junk) + + docker run -d --name mail_spam_moved_junk \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_SPAMASSASSIN=1 \ + -e MOVE_SPAM_TO_JUNK=1 \ + -e PERMIT_DOCKER=container \ + -e SA_SPAM_SUBJECT="SPAM: " \ + -e SPAMASSASSIN_SPAM_TO_INBOX=1 \ + -h mail.my-domain.com -t "${NAME}" teardown() { docker rm -f mail_spam_moved_junk; } @@ -25,7 +26,8 @@ load 'test_helper/common' run docker exec mail_spam_moved_junk /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" assert_success - run repeat_until_success_or_timeout 20 sh -c "docker logs mail_spam_moved_junk | grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}'" + # message will be added to a queue with varying delay until amavis receives it + run repeat_until_success_or_timeout 60 sh -c "docker logs mail_spam_moved_junk | grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}'" assert_success # spam moved to Junk folder @@ -36,15 +38,16 @@ load 'test_helper/common' @test "checking amavis: spam message is delivered to INBOX (MOVE_SPAM_TO_JUNK=0)" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . mail_spam_moved_new) + docker run -d --name mail_spam_moved_new \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_SPAMASSASSIN=1 \ - -e MOVE_SPAM_TO_JUNK=0 \ - -e PERMIT_DOCKER=container \ - -e SA_SPAM_SUBJECT="SPAM: " \ - -e SPAMASSASSIN_SPAM_TO_INBOX=1 \ - -h mail.my-domain.com -t "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_SPAMASSASSIN=1 \ + -e MOVE_SPAM_TO_JUNK=0 \ + -e PERMIT_DOCKER=container \ + -e SA_SPAM_SUBJECT="SPAM: " \ + -e SPAMASSASSIN_SPAM_TO_INBOX=1 \ + -h mail.my-domain.com -t "${NAME}" teardown() { docker rm -f mail_spam_moved_new; } @@ -54,7 +57,8 @@ load 'test_helper/common' run docker exec mail_spam_moved_new /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" assert_success - run repeat_until_success_or_timeout 20 sh -c "docker logs mail_spam_moved_new | grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}'" + # message will be added to a queue with varying delay until amavis receives it + run repeat_until_success_or_timeout 60 sh -c "docker logs mail_spam_moved_new | grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}'" assert_success # spam moved to INBOX diff --git a/test/mail_special_use_folders.bats b/test/mail_special_use_folders.bats index 7c141c71..fb181964 100644 --- a/test/mail_special_use_folders.bats +++ b/test/mail_special_use_folders.bats @@ -1,22 +1,24 @@ load 'test_helper/common' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_special_use_folders \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e SASL_PASSWD="external-domain.com username:password" \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_SPAMASSASSIN=0 \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -h mail.my-domain.com -t "${NAME}" - wait_for_smtp_port_in_container mail_special_use_folders + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_special_use_folders \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e SASL_PASSWD="external-domain.com username:password" \ + -e ENABLE_CLAMAV=0 \ + -e ENABLE_SPAMASSASSIN=0 \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_smtp_port_in_container mail_special_use_folders } teardown_file() { - docker rm -f mail_special_use_folders + docker rm -f mail_special_use_folders } @test "checking normal delivery" { diff --git a/test/mail_ssl_manual.bats b/test/mail_ssl_manual.bats index 287734cf..52f07913 100644 --- a/test/mail_ssl_manual.bats +++ b/test/mail_ssl_manual.bats @@ -2,108 +2,110 @@ load 'test_helper/common' function setup_file() { - # Internal copies made by `start-mailserver.sh`: - export PRIMARY_KEY='/etc/dms/tls/key' - export PRIMARY_CERT='/etc/dms/tls/cert' - export FALLBACK_KEY='/etc/dms/tls/fallback_key' - export FALLBACK_CERT='/etc/dms/tls/fallback_cert' + # Internal copies made by `start-mailserver.sh`: + export PRIMARY_KEY='/etc/dms/tls/key' + export PRIMARY_CERT='/etc/dms/tls/cert' + export FALLBACK_KEY='/etc/dms/tls/fallback_key' + export FALLBACK_CERT='/etc/dms/tls/fallback_cert' - # Volume mounted certs: - export SSL_KEY_PATH='/config/ssl/key.ecdsa.pem' - export SSL_CERT_PATH='/config/ssl/cert.ecdsa.pem' - export SSL_ALT_KEY_PATH='/config/ssl/key.rsa.pem' - export SSL_ALT_CERT_PATH='/config/ssl/cert.rsa.pem' + # Volume mounted certs: + export SSL_KEY_PATH='/config/ssl/key.ecdsa.pem' + export SSL_CERT_PATH='/config/ssl/cert.ecdsa.pem' + export SSL_ALT_KEY_PATH='/config/ssl/key.rsa.pem' + export SSL_ALT_CERT_PATH='/config/ssl/cert.rsa.pem' - local PRIVATE_CONFIG - export DOMAIN_SSL_MANUAL='example.test' - PRIVATE_CONFIG=$(duplicate_config_for_container .) + local PRIVATE_CONFIG + export DOMAIN_SSL_MANUAL='example.test' + PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_manual_ssl \ - --volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \ - --volume "$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/:/config/ssl/:ro" \ - --env LOG_LEVEL='trace' \ - --env SSL_TYPE='manual' \ - --env TLS_LEVEL='modern' \ - --env SSL_KEY_PATH="${SSL_KEY_PATH}" \ - --env SSL_CERT_PATH="${SSL_CERT_PATH}" \ - --env SSL_ALT_KEY_PATH="${SSL_ALT_KEY_PATH}" \ - --env SSL_ALT_CERT_PATH="${SSL_ALT_CERT_PATH}" \ - --hostname "mail.${DOMAIN_SSL_MANUAL}" \ - --tty \ - "${NAME}" # Image name - wait_for_finished_setup_in_container mail_manual_ssl + docker run -d --name mail_manual_ssl \ + --volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \ + --volume "$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/:/config/ssl/:ro" \ + --env LOG_LEVEL='trace' \ + --env SSL_TYPE='manual' \ + --env TLS_LEVEL='modern' \ + --env SSL_KEY_PATH="${SSL_KEY_PATH}" \ + --env SSL_CERT_PATH="${SSL_CERT_PATH}" \ + --env SSL_ALT_KEY_PATH="${SSL_ALT_KEY_PATH}" \ + --env SSL_ALT_CERT_PATH="${SSL_ALT_CERT_PATH}" \ + --hostname "mail.${DOMAIN_SSL_MANUAL}" \ + --tty \ + "${NAME}" # Image name + + wait_for_finished_setup_in_container mail_manual_ssl } function teardown_file() { - docker rm -f mail_manual_ssl + docker rm -f mail_manual_ssl } @test "checking ssl: ENV vars provided are valid files" { - assert docker exec mail_manual_ssl [ -f "${SSL_CERT_PATH}" ] - assert docker exec mail_manual_ssl [ -f "${SSL_KEY_PATH}" ] - assert docker exec mail_manual_ssl [ -f "${SSL_ALT_CERT_PATH}" ] - assert docker exec mail_manual_ssl [ -f "${SSL_ALT_KEY_PATH}" ] + assert docker exec mail_manual_ssl [ -f "${SSL_CERT_PATH}" ] + assert docker exec mail_manual_ssl [ -f "${SSL_KEY_PATH}" ] + assert docker exec mail_manual_ssl [ -f "${SSL_ALT_CERT_PATH}" ] + assert docker exec mail_manual_ssl [ -f "${SSL_ALT_KEY_PATH}" ] } @test "checking ssl: manual configuration is correct" { - local DOVECOT_CONFIG_SSL='/etc/dovecot/conf.d/10-ssl.conf' + local DOVECOT_CONFIG_SSL='/etc/dovecot/conf.d/10-ssl.conf' - run docker exec mail_manual_ssl grep '^smtpd_tls_chain_files =' '/etc/postfix/main.cf' - assert_success - assert_output "smtpd_tls_chain_files = ${PRIMARY_KEY} ${PRIMARY_CERT} ${FALLBACK_KEY} ${FALLBACK_CERT}" + run docker exec mail_manual_ssl grep '^smtpd_tls_chain_files =' '/etc/postfix/main.cf' + assert_success + assert_output "smtpd_tls_chain_files = ${PRIMARY_KEY} ${PRIMARY_CERT} ${FALLBACK_KEY} ${FALLBACK_CERT}" - run docker exec mail_manual_ssl grep '^ssl_key =' "${DOVECOT_CONFIG_SSL}" - assert_success - assert_output "ssl_key = <${PRIMARY_KEY}" + run docker exec mail_manual_ssl grep '^ssl_key =' "${DOVECOT_CONFIG_SSL}" + assert_success + assert_output "ssl_key = <${PRIMARY_KEY}" - run docker exec mail_manual_ssl grep '^ssl_cert =' "${DOVECOT_CONFIG_SSL}" - assert_success - assert_output "ssl_cert = <${PRIMARY_CERT}" + run docker exec mail_manual_ssl grep '^ssl_cert =' "${DOVECOT_CONFIG_SSL}" + assert_success + assert_output "ssl_cert = <${PRIMARY_CERT}" - run docker exec mail_manual_ssl grep '^ssl_alt_key =' "${DOVECOT_CONFIG_SSL}" - assert_success - assert_output "ssl_alt_key = <${FALLBACK_KEY}" + run docker exec mail_manual_ssl grep '^ssl_alt_key =' "${DOVECOT_CONFIG_SSL}" + assert_success + assert_output "ssl_alt_key = <${FALLBACK_KEY}" - run docker exec mail_manual_ssl grep '^ssl_alt_cert =' "${DOVECOT_CONFIG_SSL}" - assert_success - assert_output "ssl_alt_cert = <${FALLBACK_CERT}" + run docker exec mail_manual_ssl grep '^ssl_alt_cert =' "${DOVECOT_CONFIG_SSL}" + assert_success + assert_output "ssl_alt_cert = <${FALLBACK_CERT}" } @test "checking ssl: manual configuration copied files correctly " { - run docker exec mail_manual_ssl cmp -s "${PRIMARY_KEY}" "${SSL_KEY_PATH}" - assert_success - run docker exec mail_manual_ssl cmp -s "${PRIMARY_CERT}" "${SSL_CERT_PATH}" - assert_success + run docker exec mail_manual_ssl cmp -s "${PRIMARY_KEY}" "${SSL_KEY_PATH}" + assert_success + run docker exec mail_manual_ssl cmp -s "${PRIMARY_CERT}" "${SSL_CERT_PATH}" + assert_success - # Fallback cert - run docker exec mail_manual_ssl cmp -s "${FALLBACK_KEY}" "${SSL_ALT_KEY_PATH}" - assert_success - run docker exec mail_manual_ssl cmp -s "${FALLBACK_CERT}" "${SSL_ALT_CERT_PATH}" - assert_success + # Fallback cert + run docker exec mail_manual_ssl cmp -s "${FALLBACK_KEY}" "${SSL_ALT_KEY_PATH}" + assert_success + run docker exec mail_manual_ssl cmp -s "${FALLBACK_CERT}" "${SSL_ALT_CERT_PATH}" + assert_success } @test "checking ssl: manual cert works correctly" { - wait_for_tcp_port_in_container 587 mail_manual_ssl - local TEST_COMMAND=(timeout 1 openssl s_client -connect mail.example.test:587 -starttls smtp) - local RESULT + wait_for_tcp_port_in_container 587 mail_manual_ssl - # Should fail as a chain of trust is required to verify successfully: - RESULT=$(docker exec mail_manual_ssl "${TEST_COMMAND[@]}" | grep 'Verification error:') - assert_equal "${RESULT}" 'Verification error: unable to verify the first certificate' + local TEST_COMMAND=(timeout 1 openssl s_client -connect mail.example.test:587 -starttls smtp) + local RESULT - # Provide the Root CA cert for successful verification: - local CA_CERT='/config/ssl/ca-cert.ecdsa.pem' - assert docker exec mail_manual_ssl [ -f "${CA_CERT}" ] - RESULT=$(docker exec mail_manual_ssl "${TEST_COMMAND[@]}" -CAfile "${CA_CERT}" | grep 'Verification: OK') - assert_equal "${RESULT}" 'Verification: OK' + # Should fail as a chain of trust is required to verify successfully: + RESULT=$(docker exec mail_manual_ssl "${TEST_COMMAND[@]}" | grep 'Verification error:') + assert_equal "${RESULT}" 'Verification error: unable to verify the first certificate' + + # Provide the Root CA cert for successful verification: + local CA_CERT='/config/ssl/ca-cert.ecdsa.pem' + assert docker exec mail_manual_ssl [ -f "${CA_CERT}" ] + RESULT=$(docker exec mail_manual_ssl "${TEST_COMMAND[@]}" -CAfile "${CA_CERT}" | grep 'Verification: OK') + assert_equal "${RESULT}" 'Verification: OK' } @test "checking ssl: manual cert changes are picked up by check-for-changes" { - printf '%s' 'someThingsChangedHere' \ - >>"$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/key.ecdsa.pem" + printf '%s' 'someThingsChangedHere' \ + >>"$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/key.ecdsa.pem" - run timeout 15 docker exec mail_manual_ssl bash -c "tail -F /var/log/supervisor/changedetector.log | sed '/Manual certificates have changed/ q'" - assert_success + run timeout 15 docker exec mail_manual_ssl 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/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/key.ecdsa.pem" + sed -i '/someThingsChangedHere/d' "$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/key.ecdsa.pem" } diff --git a/test/mail_time.bats b/test/mail_time.bats index 52407a59..9e7f22a9 100644 --- a/test/mail_time.bats +++ b/test/mail_time.bats @@ -15,7 +15,7 @@ setup_file() { } teardown_file() { - docker rm -f mail_time + docker rm -f mail_time } @test "checking time: setting the time with TZ works correctly" { diff --git a/test/mail_tls_dhparams.bats b/test/mail_tls_dhparams.bats index 05e5f608..9edd0e16 100644 --- a/test/mail_tls_dhparams.bats +++ b/test/mail_tls_dhparams.bats @@ -14,7 +14,7 @@ load 'test_helper/common' # - A warning is raised about usage of potentially insecure parameters. function teardown() { - docker rm -f mail_dhparams + docker rm -f mail_dhparams } function setup_file() { @@ -33,7 +33,6 @@ function setup_file() { export DH_CUSTOM_PARAMS export DH_CUSTOM_CHECKSUM - DH_DEFAULT_PARAMS="$(pwd)/target/shared/ffdhe4096.pem" DH_DEFAULT_CHECKSUM=$(sha512sum "${DH_DEFAULT_PARAMS}" | awk '{print $1}') @@ -46,83 +45,83 @@ function setup_file() { # } @test "testing tls: DH Parameters - Verify integrity of Default (ffdhe4096)" { - # Reference used (22/04/2020): - # https://english.ncsc.nl/publications/publications/2019/juni/01/it-security-guidelines-for-transport-layer-security-tls + # Reference used (22/04/2020): + # https://english.ncsc.nl/publications/publications/2019/juni/01/it-security-guidelines-for-transport-layer-security-tls - run echo "${DH_DEFAULT_CHECKSUM}" - refute_output '' # checksum must not be empty + run echo "${DH_DEFAULT_CHECKSUM}" + refute_output '' # checksum must not be empty - # Verify the FFDHE params file has not been modified (equivalent to `target/shared/ffdhe4096.pem.sha512sum`): - local DH_MOZILLA_CHECKSUM - DH_MOZILLA_CHECKSUM=$(curl https://ssl-config.mozilla.org/ffdhe4096.txt -s | sha512sum | awk '{print $1}') - assert_equal "${DH_DEFAULT_CHECKSUM}" "${DH_MOZILLA_CHECKSUM}" + # Verify the FFDHE params file has not been modified (equivalent to `target/shared/ffdhe4096.pem.sha512sum`): + local DH_MOZILLA_CHECKSUM + DH_MOZILLA_CHECKSUM=$(curl https://ssl-config.mozilla.org/ffdhe4096.txt -s | sha512sum | awk '{print $1}') + assert_equal "${DH_DEFAULT_CHECKSUM}" "${DH_MOZILLA_CHECKSUM}" } @test "testing tls: DH Parameters - Default [ONE_DIR=0]" { - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_default_0) - DMS_ONE_DIR=0 + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_default_0) + DMS_ONE_DIR=0 - common_container_setup - should_have_valid_checksum "${DH_DEFAULT_CHECKSUM}" + common_container_setup + should_have_valid_checksum "${DH_DEFAULT_CHECKSUM}" } @test "testing tls: DH Parameters - Default [ONE_DIR=1]" { - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_default_1) + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_default_1) - common_container_setup - should_have_valid_checksum "${DH_DEFAULT_CHECKSUM}" + common_container_setup + should_have_valid_checksum "${DH_DEFAULT_CHECKSUM}" } @test "testing tls: DH Parameters - Custom [ONE_DIR=0]" { - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_custom_0) - # shellcheck disable=SC2030 - DMS_ONE_DIR=0 + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_custom_0) + # shellcheck disable=SC2030 + DMS_ONE_DIR=0 - cp "${DH_CUSTOM_PARAMS}" "${PRIVATE_CONFIG}/dhparams.pem" + cp "${DH_CUSTOM_PARAMS}" "${PRIVATE_CONFIG}/dhparams.pem" - common_container_setup - should_have_valid_checksum "${DH_CUSTOM_CHECKSUM}" - should_emit_warning + common_container_setup + should_have_valid_checksum "${DH_CUSTOM_CHECKSUM}" + should_emit_warning } @test "testing tls: DH Parameters - Custom [ONE_DIR=1]" { - # shellcheck disable=SC2030 - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_custom_1) + # shellcheck disable=SC2030 + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dhparams_custom_1) - cp "${DH_CUSTOM_PARAMS}" "${PRIVATE_CONFIG}/dhparams.pem" + cp "${DH_CUSTOM_PARAMS}" "${PRIVATE_CONFIG}/dhparams.pem" - common_container_setup - should_have_valid_checksum "${DH_CUSTOM_CHECKSUM}" - should_emit_warning + common_container_setup + should_have_valid_checksum "${DH_CUSTOM_CHECKSUM}" + should_emit_warning } function common_container_setup() { - # shellcheck disable=SC2031 - docker run -d --name mail_dhparams \ - -v "${PRIVATE_CONFIG}:/tmp/docker-mailserver" \ - -v "$(pwd)/test/test-files:/tmp/docker-mailserver-test:ro" \ - -e ONE_DIR="${DMS_ONE_DIR}" \ - -h mail.my-domain.com \ - --tty \ - "${NAME}" + # shellcheck disable=SC2031 + docker run -d --name mail_dhparams \ + -v "${PRIVATE_CONFIG}:/tmp/docker-mailserver" \ + -v "$(pwd)/test/test-files:/tmp/docker-mailserver-test:ro" \ + -e ONE_DIR="${DMS_ONE_DIR}" \ + -h mail.my-domain.com \ + --tty \ + "${NAME}" - wait_for_finished_setup_in_container mail_dhparams + wait_for_finished_setup_in_container mail_dhparams } # Ensures the docker image services (Postfix and Dovecot) have the intended DH files function should_have_valid_checksum() { - local DH_CHECKSUM=$1 + local DH_CHECKSUM=$1 - local DH_CHECKSUM_DOVECOT - DH_CHECKSUM_DOVECOT=$(docker exec mail_dhparams sha512sum /etc/dovecot/dh.pem | awk '{print $1}') - assert_equal "${DH_CHECKSUM_DOVECOT}" "${DH_CHECKSUM}" + local DH_CHECKSUM_DOVECOT + DH_CHECKSUM_DOVECOT=$(docker exec mail_dhparams sha512sum /etc/dovecot/dh.pem | awk '{print $1}') + assert_equal "${DH_CHECKSUM_DOVECOT}" "${DH_CHECKSUM}" - local DH_CHECKSUM_POSTFIX - DH_CHECKSUM_POSTFIX=$(docker exec mail_dhparams sha512sum /etc/postfix/dhparams.pem | awk '{print $1}') - assert_equal "${DH_CHECKSUM_POSTFIX}" "${DH_CHECKSUM}" + local DH_CHECKSUM_POSTFIX + DH_CHECKSUM_POSTFIX=$(docker exec mail_dhparams sha512sum /etc/postfix/dhparams.pem | awk '{print $1}') + assert_equal "${DH_CHECKSUM_POSTFIX}" "${DH_CHECKSUM}" } function should_emit_warning() { - run sh -c "docker logs mail_dhparams | grep 'Using self-generated dhparams is considered insecure.'" - assert_success + run sh -c "docker logs mail_dhparams | grep 'Using self-generated dhparams is considered insecure.'" + assert_success } diff --git a/test/mail_undef_spam_subject.bats b/test/mail_undef_spam_subject.bats index 4f44ebfd..b6ee6868 100644 --- a/test/mail_undef_spam_subject.bats +++ b/test/mail_undef_spam_subject.bats @@ -1,53 +1,59 @@ load 'test_helper/common' function setup() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_undef_spam_subject \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_SPAMASSASSIN=1 \ - -e SA_SPAM_SUBJECT="undef" \ - -h mail.my-domain.com -t "${NAME}" + local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_undef_spam_subject_2) - CONTAINER=$(docker run -d \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -v "$(pwd)/test/onedir":/var/mail-state \ - -e ENABLE_CLAMAV=1 \ - -e SPOOF_PROTECTION=1 \ - -e ENABLE_SPAMASSASSIN=1 \ - -e REPORT_RECIPIENT=user1@localhost.localdomain \ - -e REPORT_SENDER=report1@mail.my-domain.com \ - -e SA_TAG=-5.0 \ - -e SA_TAG2=2.0 \ - -e SA_KILL=3.0 \ - -e SA_SPAM_SUBJECT="SPAM: " \ - -e VIRUSMAILS_DELETE_DELAY=7 \ - -e ENABLE_SRS=1 \ - -e SASL_PASSWD="external-domain.com username:password" \ - -e ENABLE_MANAGESIEVE=1 \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -h mail.my-domain.com -t "${NAME}") - wait_for_finished_setup_in_container mail_undef_spam_subject - wait_for_finished_setup_in_container "${CONTAINER}" + PRIVATE_CONFIG=$(duplicate_config_for_container .) + docker run -d --name mail_undef_spam_subject \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_SPAMASSASSIN=1 \ + -e SA_SPAM_SUBJECT="undef" \ + -h mail.my-domain.com -t "${NAME}" + + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_undef_spam_subject_2) + CONTAINER=$(docker run -d \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -v "$(pwd)/test/onedir":/var/mail-state \ + -e ENABLE_CLAMAV=1 \ + -e SPOOF_PROTECTION=1 \ + -e ENABLE_SPAMASSASSIN=1 \ + -e REPORT_RECIPIENT=user1@localhost.localdomain \ + -e REPORT_SENDER=report1@mail.my-domain.com \ + -e SA_TAG=-5.0 \ + -e SA_TAG2=2.0 \ + -e SA_KILL=3.0 \ + -e SA_SPAM_SUBJECT="SPAM: " \ + -e VIRUSMAILS_DELETE_DELAY=7 \ + -e ENABLE_SRS=1 \ + -e SASL_PASSWD="external-domain.com username:password" \ + -e ENABLE_MANAGESIEVE=1 \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -h mail.my-domain.com -t "${NAME}") + + wait_for_finished_setup_in_container mail_undef_spam_subject + wait_for_finished_setup_in_container "${CONTAINER}" } function teardown() { - docker rm -f mail_undef_spam_subject "${CONTAINER}" + docker rm -f mail_undef_spam_subject "${CONTAINER}" } @test "checking spamassassin: docker env variables are set correctly (custom)" { run docker exec "${CONTAINER}" /bin/sh -c "grep '\$sa_tag_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= -5.0'" assert_success + run docker exec "${CONTAINER}" /bin/sh -c "grep '\$sa_tag2_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= 2.0'" assert_success + run docker exec "${CONTAINER}" /bin/sh -c "grep '\$sa_kill_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= 3.0'" assert_success + run docker exec "${CONTAINER}" /bin/sh -c "grep '\$sa_spam_subject_tag' /etc/amavis/conf.d/20-debian_defaults | grep '= .SPAM: .'" assert_success + run docker exec mail_undef_spam_subject /bin/sh -c "grep '\$sa_spam_subject_tag' /etc/amavis/conf.d/20-debian_defaults | grep '= undef'" assert_success } diff --git a/test/mail_with_imap.bats b/test/mail_with_imap.bats index 601efc14..ffb609b7 100644 --- a/test/mail_with_imap.bats +++ b/test/mail_with_imap.bats @@ -1,10 +1,10 @@ - load 'test_helper/common' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_with_imap \ + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_with_imap \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ -e ENABLE_SASLAUTHD=1 \ @@ -13,11 +13,12 @@ setup_file() { -e SASLAUTHD_MECHANISMS=rimap \ -e PERMIT_DOCKER=container \ -h mail.my-domain.com -t "${NAME}" - wait_for_smtp_port_in_container mail_with_imap + + wait_for_smtp_port_in_container mail_with_imap } teardown_file() { - docker rm -f mail_with_imap + docker rm -f mail_with_imap } # diff --git a/test/mail_with_ldap.bats b/test/mail_with_ldap.bats index 137ecf17..6e404235 100644 --- a/test/mail_with_ldap.bats +++ b/test/mail_with_ldap.bats @@ -101,41 +101,52 @@ function teardown_file() { } @test "checking postfix: ldap custom config files copied" { - run docker exec mail_with_ldap /bin/sh -c "grep '# Testconfig for ldap integration' /etc/postfix/ldap-users.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep '# Testconfig for ldap integration' /etc/postfix/ldap-groups.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep '# Testconfig for ldap integration' /etc/postfix/ldap-aliases.cf" - assert_success + run docker exec mail_with_ldap /bin/sh -c "grep '# Testconfig for ldap integration' /etc/postfix/ldap-users.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep '# Testconfig for ldap integration' /etc/postfix/ldap-groups.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep '# Testconfig for ldap integration' /etc/postfix/ldap-aliases.cf" + assert_success } @test "checking postfix: ldap config overwrites success" { - run docker exec mail_with_ldap /bin/sh -c "grep 'server_host = ldap' /etc/postfix/ldap-users.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'start_tls = no' /etc/postfix/ldap-users.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'search_base = ou=people,dc=localhost,dc=localdomain' /etc/postfix/ldap-users.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'bind_dn = cn=admin,dc=localhost,dc=localdomain' /etc/postfix/ldap-users.cf" - assert_success + run docker exec mail_with_ldap /bin/sh -c "grep 'server_host = ldap' /etc/postfix/ldap-users.cf" + assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'server_host = ldap' /etc/postfix/ldap-groups.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'start_tls = no' /etc/postfix/ldap-groups.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'search_base = ou=people,dc=localhost,dc=localdomain' /etc/postfix/ldap-groups.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'bind_dn = cn=admin,dc=localhost,dc=localdomain' /etc/postfix/ldap-groups.cf" - assert_success + run docker exec mail_with_ldap /bin/sh -c "grep 'start_tls = no' /etc/postfix/ldap-users.cf" + assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'server_host = ldap' /etc/postfix/ldap-aliases.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'start_tls = no' /etc/postfix/ldap-aliases.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'search_base = ou=people,dc=localhost,dc=localdomain' /etc/postfix/ldap-aliases.cf" - assert_success - run docker exec mail_with_ldap /bin/sh -c "grep 'bind_dn = cn=admin,dc=localhost,dc=localdomain' /etc/postfix/ldap-aliases.cf" - assert_success + run docker exec mail_with_ldap /bin/sh -c "grep 'search_base = ou=people,dc=localhost,dc=localdomain' /etc/postfix/ldap-users.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'bind_dn = cn=admin,dc=localhost,dc=localdomain' /etc/postfix/ldap-users.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'server_host = ldap' /etc/postfix/ldap-groups.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'start_tls = no' /etc/postfix/ldap-groups.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'search_base = ou=people,dc=localhost,dc=localdomain' /etc/postfix/ldap-groups.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'bind_dn = cn=admin,dc=localhost,dc=localdomain' /etc/postfix/ldap-groups.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'server_host = ldap' /etc/postfix/ldap-aliases.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'start_tls = no' /etc/postfix/ldap-aliases.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'search_base = ou=people,dc=localhost,dc=localdomain' /etc/postfix/ldap-aliases.cf" + assert_success + + run docker exec mail_with_ldap /bin/sh -c "grep 'bind_dn = cn=admin,dc=localhost,dc=localdomain' /etc/postfix/ldap-aliases.cf" + assert_success } # dovecot diff --git a/test/mail_with_mdbox.bats b/test/mail_with_mdbox.bats index a607f35a..09c8fbbe 100644 --- a/test/mail_with_mdbox.bats +++ b/test/mail_with_mdbox.bats @@ -1,19 +1,21 @@ load 'test_helper/common' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_with_mdbox_format \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e SASL_PASSWD="external-domain.com username:password" \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_SPAMASSASSIN=0 \ - -e DOVECOT_MAILBOX_FORMAT=mdbox \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -h mail.my-domain.com -t "${NAME}" - wait_for_smtp_port_in_container mail_with_mdbox_format + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_with_mdbox_format \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e SASL_PASSWD="external-domain.com username:password" \ + -e ENABLE_CLAMAV=0 \ + -e ENABLE_SPAMASSASSIN=0 \ + -e DOVECOT_MAILBOX_FORMAT=mdbox \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_smtp_port_in_container mail_with_mdbox_format } teardown_file() { diff --git a/test/mail_with_postgrey.bats b/test/mail_with_postgrey.bats index 660a508a..36dd2711 100644 --- a/test/mail_with_postgrey.bats +++ b/test/mail_with_postgrey.bats @@ -1,25 +1,27 @@ load 'test_helper/common' function setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_with_postgrey \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_DNSBL=1 \ - -e ENABLE_POSTGREY=1 \ - -e PERMIT_DOCKER=container \ - -e POSTGREY_AUTO_WHITELIST_CLIENTS=5 \ - -e POSTGREY_DELAY=15 \ - -e POSTGREY_MAX_AGE=35 \ - -e POSTGREY_TEXT="Delayed by Postgrey" \ - -h mail.my-domain.com -t "${NAME}" - # using postfix availability as start indicator, this might be insufficient for postgrey - wait_for_smtp_port_in_container mail_with_postgrey + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_with_postgrey \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_DNSBL=1 \ + -e ENABLE_POSTGREY=1 \ + -e PERMIT_DOCKER=container \ + -e POSTGREY_AUTO_WHITELIST_CLIENTS=5 \ + -e POSTGREY_DELAY=15 \ + -e POSTGREY_MAX_AGE=35 \ + -e POSTGREY_TEXT="Delayed by Postgrey" \ + -h mail.my-domain.com -t "${NAME}" + + # using postfix availability as start indicator, this might be insufficient for postgrey + wait_for_smtp_port_in_container mail_with_postgrey } function teardown_file() { - docker rm -f mail_with_postgrey + docker rm -f mail_with_postgrey } @test "checking postgrey: /etc/postfix/main.cf correctly edited" { @@ -32,6 +34,7 @@ function teardown_file() { run docker exec mail_with_postgrey /bin/bash -c "grep '^POSTGREY_OPTS=\"--inet=127.0.0.1:10023 --delay=15 --max-age=35 --auto-whitelist-clients=5\"$' /etc/default/postgrey | wc -l" assert_success assert_output 1 + run docker exec mail_with_postgrey /bin/bash -c "grep '^POSTGREY_TEXT=\"Delayed by Postgrey\"$' /etc/default/postgrey | wc -l" assert_success assert_output 1 @@ -61,6 +64,7 @@ function teardown_file() { sleep 20 #wait 20 seconds so that postgrey would accept the message run docker exec mail_with_postgrey /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/postgrey.txt" sleep 8 + run docker exec mail_with_postgrey /bin/sh -c "grep -i 'action=pass, reason=triplet found.*user@external\.tld' /var/log/mail/mail.log | wc -l" assert_success assert_output 1 diff --git a/test/mail_with_postgrey_disabled_by_default.bats b/test/mail_with_postgrey_disabled_by_default.bats index 23c5e6da..2ebbdc30 100644 --- a/test/mail_with_postgrey_disabled_by_default.bats +++ b/test/mail_with_postgrey_disabled_by_default.bats @@ -1,18 +1,20 @@ load 'test_helper/common' function setup() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - CONTAINER=$(docker run -d \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -h mail.my-domain.com -t "${NAME}") - # using postfix availability as start indicator, this might be insufficient for postgrey - wait_for_smtp_port_in_container "${CONTAINER}" + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + CONTAINER=$(docker run -d \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -h mail.my-domain.com -t "${NAME}") + + # using postfix availability as start indicator, this might be insufficient for postgrey + wait_for_smtp_port_in_container "${CONTAINER}" } function teardown() { - docker rm -f "${CONTAINER}" + docker rm -f "${CONTAINER}" } @test "checking process: postgrey (disabled in default configuration)" { diff --git a/test/mail_with_relays.bats b/test/mail_with_relays.bats index a1a6eccb..44279849 100644 --- a/test/mail_with_relays.bats +++ b/test/mail_with_relays.bats @@ -1,27 +1,28 @@ load 'test_helper/common' function setup_file() { - # We use a temporary config directory since we'll be dynamically editing - # it with setup.sh. - tmp_confdir=$(mktemp -d /tmp/docker-mailserver-config-relay-hosts-XXXXX) - cp -a test/config/relay-hosts/* "${tmp_confdir}/" + # We use a temporary config directory since we'll be dynamically editing + # it with setup.sh. + tmp_confdir=$(mktemp -d /tmp/docker-mailserver-config-relay-hosts-XXXXX) + cp -a test/config/relay-hosts/* "${tmp_confdir}/" - docker run -d --name mail_with_relays \ - -v "${tmp_confdir}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e RELAY_HOST=default.relay.com \ - -e RELAY_PORT=2525 \ - -e RELAY_USER=smtp_user \ - -e RELAY_PASSWORD=smtp_password \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -h mail.my-domain.com -t "${NAME}" - wait_for_finished_setup_in_container mail_with_relays + docker run -d --name mail_with_relays \ + -v "${tmp_confdir}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e RELAY_HOST=default.relay.com \ + -e RELAY_PORT=2525 \ + -e RELAY_USER=smtp_user \ + -e RELAY_PASSWORD=smtp_password \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail_with_relays } function teardown_file() { - docker rm -f mail_with_relays - rm -rf "${tmp_confdir}" + docker rm -f mail_with_relays + rm -rf "${tmp_confdir}" } @test "checking relay hosts: default mapping is added from env vars" { @@ -37,6 +38,7 @@ function teardown_file() { @test "checking relay hosts: default mapping is added from env vars for new user entry" { run docker exec mail_with_relays grep -e domainzero.tld /etc/postfix/relayhost_map assert_output '' + run ./setup.sh -c mail_with_relays email add user0@domainzero.tld password123 run_until_success_or_timeout 10 docker exec mail_with_relays grep -e domainzero.tld /etc/postfix/relayhost_map assert_output -e '^@domainzero.tld[[:space:]]+\[default.relay.com\]:2525$' @@ -45,6 +47,7 @@ function teardown_file() { @test "checking relay hosts: default mapping is added from env vars for new virtual user entry" { run docker exec mail_with_relays grep -e domain2.tld /etc/postfix/relayhost_map assert_output '' + run ./setup.sh -c mail_with_relays alias add user2@domain2.tld user2@domaintwo.tld run_until_success_or_timeout 10 docker exec mail_with_relays grep -e domain2.tld /etc/postfix/relayhost_map assert_output -e '^@domain2.tld[[:space:]]+\[default.relay.com\]:2525$' diff --git a/test/mail_with_sdbox.bats b/test/mail_with_sdbox.bats index 6167daae..f60c4190 100644 --- a/test/mail_with_sdbox.bats +++ b/test/mail_with_sdbox.bats @@ -1,23 +1,25 @@ load 'test_helper/common' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - docker run -d --name mail_with_sdbox_format \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -e SASL_PASSWD="external-domain.com username:password" \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_SPAMASSASSIN=0 \ - -e DOVECOT_MAILBOX_FORMAT=sdbox \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -h mail.my-domain.com -t "${NAME}" - wait_for_smtp_port_in_container mail_with_sdbox_format + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) + + docker run -d --name mail_with_sdbox_format \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e SASL_PASSWD="external-domain.com username:password" \ + -e ENABLE_CLAMAV=0 \ + -e ENABLE_SPAMASSASSIN=0 \ + -e DOVECOT_MAILBOX_FORMAT=sdbox \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_smtp_port_in_container mail_with_sdbox_format } teardown_file() { - docker rm -f mail_with_sdbox_format + docker rm -f mail_with_sdbox_format } @test "checking dovecot mailbox format: sdbox file created" { diff --git a/test/open_dkim.bats b/test/open_dkim.bats index e4fc3616..fd1a673d 100644 --- a/test/open_dkim.bats +++ b/test/open_dkim.bats @@ -9,18 +9,18 @@ TEST_FILE='checking OpenDKIM: ' # WHY IS THIS CONTAINER EVEN CREATED WHEN MOST TESTS DO NOT USE IT? function setup_file { - local PRIVATE_CONFIG + local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . "${CONTAINER_NAME}") docker run -d \ --name "${CONTAINER_NAME}" \ - --cap-add=SYS_PTRACE \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "${PWD}/test/test-files":/tmp/docker-mailserver-test:ro \ - -e DEFAULT_RELAY_HOST=default.relay.host.invalid:25 \ - -e PERMIT_DOCKER=host \ - -e LOG_LEVEL='trace' \ - -h mail.my-domain.com \ + --cap-add=SYS_PTRACE \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "${PWD}/test/test-files":/tmp/docker-mailserver-test:ro \ + -e DEFAULT_RELAY_HOST=default.relay.host.invalid:25 \ + -e PERMIT_DOCKER=host \ + -e LOG_LEVEL='trace' \ + -h mail.my-domain.com \ -t "${IMAGE_NAME}" wait_for_finished_setup_in_container "${CONTAINER_NAME}" @@ -92,20 +92,21 @@ function teardown_file # TODO Needs complete re-write @test "${TEST_FILE}generator creates key size 4096" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_key_size_4096) + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_key_size_4096) - rm -rf "${PRIVATE_CONFIG}/key4096" - mkdir -p "${PRIVATE_CONFIG}/config/key4096" + rm -rf "${PRIVATE_CONFIG}/key4096" + mkdir -p "${PRIVATE_CONFIG}/config/key4096" - run docker run --rm \ - -e LOG_LEVEL='trace' \ - -v "${PRIVATE_CONFIG}/key2048/":/tmp/docker-mailserver/ \ - -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 4096 | wc -l' - assert_success - assert_output 6 + run docker run --rm \ + -e LOG_LEVEL='trace' \ + -v "${PRIVATE_CONFIG}/key2048/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 4096 | wc -l' + + assert_success + assert_output 6 run docker run --rm \ -v "${PRIVATE_CONFIG}/key2048/opendkim":/etc/opendkim \ @@ -121,20 +122,21 @@ function teardown_file # TODO Needs complete re-write @test "${TEST_FILE}generator creates key size 2048" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_key_size_2048) + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_key_size_2048) - rm -rf "${PRIVATE_CONFIG}/key2048" - mkdir -p "${PRIVATE_CONFIG}/config/key2048" + rm -rf "${PRIVATE_CONFIG}/key2048" + mkdir -p "${PRIVATE_CONFIG}/config/key2048" - run docker run --rm \ - -e LOG_LEVEL='trace' \ - -v "${PRIVATE_CONFIG}/key2048/":/tmp/docker-mailserver/ \ - -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 2048 | wc -l' - assert_success - assert_output 6 + run docker run --rm \ + -e LOG_LEVEL='trace' \ + -v "${PRIVATE_CONFIG}/key2048/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 2048 | wc -l' + + assert_success + assert_output 6 run docker run --rm \ -v "${PRIVATE_CONFIG}/key2048/opendkim":/etc/opendkim \ @@ -150,20 +152,21 @@ function teardown_file # TODO Needs complete re-write @test "${TEST_FILE}generator creates key size 1024" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail_key_size_1024) + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container . mail_key_size_1024) - rm -rf "${PRIVATE_CONFIG}/key1024" - mkdir -p "${PRIVATE_CONFIG}/key1024" + rm -rf "${PRIVATE_CONFIG}/key1024" + mkdir -p "${PRIVATE_CONFIG}/key1024" - run docker run --rm \ - -e LOG_LEVEL='trace' \ - -v "${PRIVATE_CONFIG}/key1024/":/tmp/docker-mailserver/ \ - -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 1024 | wc -l' - assert_success - assert_output 6 + run docker run --rm \ + -e LOG_LEVEL='trace' \ + -v "${PRIVATE_CONFIG}/key1024/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 1024 | wc -l' + + assert_success + assert_output 6 run docker run --rm \ -v "${PRIVATE_CONFIG}/key1024/opendkim":/etc/opendkim \ @@ -177,14 +180,17 @@ function teardown_file @test "${TEST_FILE}generator creates keys, tables and TrustedHosts" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . mail_dkim_generator_creates_keys_tables_TrustedHosts) + rm -rf "${PRIVATE_CONFIG}/empty" mkdir -p "${PRIVATE_CONFIG}/empty" + run docker run --rm \ -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/empty/":/tmp/docker-mailserver/ \ -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ "${IMAGE_NAME}" /bin/bash -c 'open-dkim | wc -l' + assert_success assert_output 6 @@ -192,6 +198,7 @@ function teardown_file run docker run --rm \ -v "${PRIVATE_CONFIG}/empty/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + assert_success assert_output 2 @@ -199,6 +206,7 @@ function teardown_file run docker run --rm \ -v "${PRIVATE_CONFIG}/empty/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' + assert_success assert_output 2 @@ -206,6 +214,7 @@ function teardown_file run docker run --rm \ -v "${PRIVATE_CONFIG}/empty/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" + assert_success assert_output 4 } @@ -213,13 +222,16 @@ function teardown_file @test "${TEST_FILE}generator creates keys, tables and TrustedHosts without postfix-accounts.cf" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . ) + rm -rf "${PRIVATE_CONFIG}/without-accounts" mkdir -p "${PRIVATE_CONFIG}/without-accounts" + run docker run --rm \ -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-accounts/":/tmp/docker-mailserver/ \ -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ "${IMAGE_NAME}" /bin/bash -c 'open-dkim | wc -l' + assert_success assert_output 5 @@ -228,6 +240,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-accounts/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + assert_success assert_output 2 @@ -237,11 +250,13 @@ function teardown_file # "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' # assert_success # [ "${output}" -eq 0 ] + # check presence of tables and TrustedHosts run docker run --rm \ -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-accounts/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" + assert_success assert_output 4 } @@ -249,13 +264,16 @@ function teardown_file @test "${TEST_FILE}generator creates keys, tables and TrustedHosts without postfix-virtual.cf" { local PRIVATE_CONFIG PRIVATE_CONFIG=$(duplicate_config_for_container . "${BATS_TEST_NAME}") + rm -rf "${PRIVATE_CONFIG}/without-virtual" mkdir -p "${PRIVATE_CONFIG}/without-virtual" + run docker run --rm \ -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-virtual/":/tmp/docker-mailserver/ \ -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ "${IMAGE_NAME}" /bin/bash -c 'open-dkim | wc -l' + assert_success assert_output 5 @@ -264,6 +282,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-virtual/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + assert_success assert_output 2 @@ -272,6 +291,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-virtual/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' + assert_success assert_output 2 @@ -280,6 +300,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/without-virtual/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" + assert_success assert_output 4 } @@ -294,6 +315,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/":/tmp/docker-mailserver/ \ "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 2048 domain domain1.tld | wc -l' + assert_success assert_output 4 @@ -302,6 +324,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/":/tmp/docker-mailserver/ \ "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 2048 domain "domain2.tld,domain3.tld" | wc -l' + assert_success assert_output 2 @@ -310,6 +333,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/":/tmp/docker-mailserver/ \ "${IMAGE_NAME}" /bin/bash -c 'open-dkim keysize 2048 domain "domain3.tld,domain4.tld" | wc -l' + assert_success assert_output 1 @@ -318,6 +342,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/domain1.tld/ | wc -l' + assert_success assert_output 2 @@ -326,6 +351,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/domain2.tld | wc -l' + assert_success assert_output 2 @@ -334,6 +360,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/domain3.tld | wc -l' + assert_success assert_output 2 @@ -342,6 +369,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c 'ls -1 /etc/opendkim/keys/domain4.tld | wc -l' + assert_success assert_output 2 @@ -350,6 +378,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys' | wc -l" + assert_success assert_output 4 @@ -359,6 +388,7 @@ function teardown_file -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c \ "egrep 'domain1.tld|domain2.tld|domain3.tld|domain4.tld' /etc/opendkim/KeyTable | wc -l" + assert_success assert_output 4 @@ -368,6 +398,7 @@ function teardown_file -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ "${IMAGE_NAME}" /bin/bash -c \ "egrep 'domain1.tld|domain2.tld|domain3.tld|domain4.tld' /etc/opendkim/SigningTable | wc -l" + assert_success assert_output 4 } @@ -382,6 +413,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-selector/":/tmp/docker-mailserver/ \ "${IMAGE_NAME:?}" /bin/sh -c "open-dkim keysize 2048 domain 'domain1.tld' selector mailer| wc -l" + assert_success assert_output 4 @@ -390,6 +422,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-selector/opendkim":/etc/opendkim \ "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/domain1.tld/ | wc -l' + assert_success assert_output 2 @@ -398,6 +431,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-selector/opendkim":/etc/opendkim \ "${IMAGE_NAME:?}" /bin/sh -c "ls -1 /etc/opendkim/keys/domain1.tld | grep -E 'mailer.private|mailer.txt' | wc -l" + assert_success assert_output 2 @@ -406,6 +440,7 @@ function teardown_file -e LOG_LEVEL='trace' \ -v "${PRIVATE_CONFIG}/with-selector/opendkim":/etc/opendkim \ "${IMAGE_NAME:?}" /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys' | wc -l" + assert_success assert_output 4 @@ -415,6 +450,7 @@ function teardown_file -v "${PRIVATE_CONFIG}/with-selector/opendkim":/etc/opendkim \ "${IMAGE_NAME:?}" /bin/sh -c \ "grep 'domain1.tld' /etc/opendkim/KeyTable | wc -l" + assert_success assert_output 1 @@ -424,6 +460,7 @@ function teardown_file -v "${PRIVATE_CONFIG}/with-selector/opendkim":/etc/opendkim \ "${IMAGE_NAME:?}" /bin/sh -c \ "grep 'domain1.tld' /etc/opendkim/SigningTable | wc -l" + assert_success assert_output 1 } diff --git a/test/permit_docker.bats b/test/permit_docker.bats index 5ba4d9fb..12254207 100644 --- a/test/permit_docker.bats +++ b/test/permit_docker.bats @@ -2,55 +2,57 @@ load 'test_helper/common' NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME=non-default-docker-mail-network setup_file() { - docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" - docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" - # use two networks (default ("bridge") and our custom network) to recreate problematic test case where PERMIT_DOCKER=host would not help - # currently we cannot use --network in `docker run` multiple times, it will just use the last one - # instead we need to use create, network connect and start (see https://success.docker.com/article/multiple-docker-networks) - local PRIVATE_CONFIG - 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 \ - -e SMTP_ONLY=1 \ - -e PERMIT_DOCKER=connected-networks \ - -e OVERRIDE_HOSTNAME=mail.my-domain.com \ - --network "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" \ - -t "${NAME}" - docker network connect "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" mail_smtponly_second_network - docker start mail_smtponly_second_network - 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 \ - -e SMTP_ONLY=1 \ - -e PERMIT_DOCKER=connected-networks \ - -e OVERRIDE_HOSTNAME=mail.my-domain.com \ - --network "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" \ - -t "${NAME}" + docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" + docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" - # wait until postfix is up - wait_for_smtp_port_in_container mail_smtponly_second_network + # use two networks (default ("bridge") and our custom network) to recreate problematic test case where PERMIT_DOCKER=host would not help + # currently we cannot use --network in `docker run` multiple times, it will just use the last one + # instead we need to use create, network connect and start (see https://success.docker.com/article/multiple-docker-networks) + local PRIVATE_CONFIG - # 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 \ - -e SMTP_ONLY=1 \ - -e PERMIT_DOCKER=none \ - -e OVERRIDE_HOSTNAME=mail.my-domain.com \ - -t "${NAME}" + 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 \ + -e SMTP_ONLY=1 \ + -e PERMIT_DOCKER=connected-networks \ + -e OVERRIDE_HOSTNAME=mail.my-domain.com \ + --network "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" \ + -t "${NAME}" - # wait until postfix is up - wait_for_smtp_port_in_container mail_smtponly_force_authentication + docker network connect "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" mail_smtponly_second_network + docker start mail_smtponly_second_network + + 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 \ + -e SMTP_ONLY=1 \ + -e PERMIT_DOCKER=connected-networks \ + -e OVERRIDE_HOSTNAME=mail.my-domain.com \ + --network "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" \ + -t "${NAME}" + + # wait until postfix is up + wait_for_smtp_port_in_container mail_smtponly_second_network + + # 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 \ + -e SMTP_ONLY=1 \ + -e PERMIT_DOCKER=none \ + -e OVERRIDE_HOSTNAME=mail.my-domain.com \ + -t "${NAME}" + + # wait until postfix is up + wait_for_smtp_port_in_container mail_smtponly_force_authentication } teardown_file() { - docker logs mail_smtponly_second_network - docker rm -f mail_smtponly_second_network \ - mail_smtponly_second_network_sender \ - mail_smtponly_force_authentication - docker network rm "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" + docker logs mail_smtponly_second_network + docker rm -f mail_smtponly_second_network mail_smtponly_second_network_sender mail_smtponly_force_authentication + docker network rm "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2" } @test "checking PERMIT_DOCKER: connected-networks" { @@ -62,8 +64,10 @@ teardown_file() { run docker exec mail_smtponly_second_network /bin/sh -c "postconf -e smtp_host_lookup=no" assert_success + run docker exec mail_smtponly_second_network /bin/sh -c "/etc/init.d/postfix reload" assert_success + # 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" assert_output --partial "250 2.0.0 Ok: queued as " @@ -74,8 +78,10 @@ teardown_file() { @test "checking PERMIT_DOCKER: none" { run docker exec mail_smtponly_force_authentication /bin/sh -c "postconf -e smtp_host_lookup=no" assert_success + run docker exec mail_smtponly_force_authentication /bin/sh -c "/etc/init.d/postfix reload" assert_success + # 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" assert_output --partial "550 5.5.1 Protocol error" diff --git a/test/security_tls_cipherlists.bats b/test/security_tls_cipherlists.bats index 5c2b3b45..a60e1f14 100644 --- a/test/security_tls_cipherlists.bats +++ b/test/security_tls_cipherlists.bats @@ -4,170 +4,176 @@ load 'test_helper/common' # `${NAME}` defaults to `mailserver-testing:ci` function teardown() { - docker rm -f tls_test_cipherlists + docker rm -f tls_test_cipherlists } function setup_file() { - export DOMAIN="example.test" - export NETWORK="test-network" + export DOMAIN="example.test" + export NETWORK="test-network" - # Shared config for TLS testing (read-only) - export TLS_CONFIG_VOLUME - TLS_CONFIG_VOLUME="$(pwd)/test/test-files/ssl/${DOMAIN}/:/config/ssl/:ro" - # `${BATS_TMPDIR}` maps to `/tmp` - export TLS_RESULTS_DIR="${BATS_TMPDIR}/results" + # Shared config for TLS testing (read-only) + export TLS_CONFIG_VOLUME + TLS_CONFIG_VOLUME="$(pwd)/test/test-files/ssl/${DOMAIN}/:/config/ssl/:ro" + # `${BATS_TMPDIR}` maps to `/tmp` + export TLS_RESULTS_DIR="${BATS_TMPDIR}/results" - # NOTE: If the network already exists, test will fail to start. - docker network create "${NETWORK}" + # NOTE: If the network already exists, test will fail to start. + docker network create "${NETWORK}" - # Copies all of `./test/config/` to specific directory for testing - # `${PRIVATE_CONFIG}` becomes `$(pwd)/test/duplicate_configs/` - export PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) + # Copies all of `./test/config/` to specific directory for testing + # `${PRIVATE_CONFIG}` becomes `$(pwd)/test/duplicate_configs/` + export PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - # Pull `testssl.sh` image in advance to avoid it interfering with the `run` captured output. - # Only interferes (potential test failure) with `assert_output` not `assert_success`? - docker pull drwetter/testssl.sh:3.1dev + # Pull `testssl.sh` image in advance to avoid it interfering with the `run` captured output. + # Only interferes (potential test failure) with `assert_output` not `assert_success`? + docker pull drwetter/testssl.sh:3.1dev } function teardown_file() { - docker network rm "${NETWORK}" + docker network rm "${NETWORK}" } @test "checking tls: cipher list - rsa intermediate" { - check_ports 'rsa' 'intermediate' + check_ports 'rsa' 'intermediate' } @test "checking tls: cipher list - rsa modern" { - check_ports 'rsa' 'modern' + check_ports 'rsa' 'modern' } @test "checking tls: cipher list - ecdsa intermediate" { - check_ports 'ecdsa' 'intermediate' + check_ports 'ecdsa' 'intermediate' } @test "checking tls: cipher list - ecdsa modern" { - check_ports 'ecdsa' 'modern' + check_ports 'ecdsa' 'modern' } # Only ECDSA with RSA fallback is tested. # There isn't a situation where RSA with ECDSA fallback would make sense. @test "checking tls: cipher list - ecdsa intermediate, with rsa fallback" { - check_ports 'ecdsa' 'intermediate' 'rsa' + check_ports 'ecdsa' 'intermediate' 'rsa' } @test "checking tls: cipher list - ecdsa modern, with rsa fallback" { - check_ports 'ecdsa' 'modern' 'rsa' + check_ports 'ecdsa' 'modern' 'rsa' } function check_ports() { - local KEY_TYPE=$1 - local TLS_LEVEL=$2 - local ALT_KEY_TYPE=$3 # Optional parameter + local KEY_TYPE=$1 + local TLS_LEVEL=$2 + local ALT_KEY_TYPE=$3 # Optional parameter - local KEY_TYPE_LABEL="${KEY_TYPE}" - # This is just to add a `_` delimiter between the two key types for readability - if [[ -n ${ALT_KEY_TYPE} ]] - then - KEY_TYPE_LABEL="${KEY_TYPE}_${ALT_KEY_TYPE}" - fi - local RESULTS_PATH="${KEY_TYPE_LABEL}/${TLS_LEVEL}" + local KEY_TYPE_LABEL="${KEY_TYPE}" + # This is just to add a `_` delimiter between the two key types for readability + if [[ -n ${ALT_KEY_TYPE} ]] + then + KEY_TYPE_LABEL="${KEY_TYPE}_${ALT_KEY_TYPE}" + fi + local RESULTS_PATH="${KEY_TYPE_LABEL}/${TLS_LEVEL}" - collect_cipherlist_data + collect_cipherlist_data - # SMTP: Opportunistic STARTTLS Explicit(25) - # Needs to test against cipher lists specific to Port 25 ('_p25' parameter) - check_cipherlists "${RESULTS_PATH}/port_25.json" '_p25' - # SMTP Submission: Mandatory STARTTLS Explicit(587) and Implicit(465) TLS - check_cipherlists "${RESULTS_PATH}/port_587.json" - check_cipherlists "${RESULTS_PATH}/port_465.json" - # IMAP: Mandatory STARTTLS Explicit(143) and Implicit(993) TLS - check_cipherlists "${RESULTS_PATH}/port_143.json" - check_cipherlists "${RESULTS_PATH}/port_993.json" - # POP3: Mandatory STARTTLS Explicit(110) and Implicit(995) - check_cipherlists "${RESULTS_PATH}/port_110.json" - check_cipherlists "${RESULTS_PATH}/port_995.json" + # SMTP: Opportunistic STARTTLS Explicit(25) + # Needs to test against cipher lists specific to Port 25 ('_p25' parameter) + check_cipherlists "${RESULTS_PATH}/port_25.json" '_p25' + + # SMTP Submission: Mandatory STARTTLS Explicit(587) and Implicit(465) TLS + check_cipherlists "${RESULTS_PATH}/port_587.json" + check_cipherlists "${RESULTS_PATH}/port_465.json" + + # IMAP: Mandatory STARTTLS Explicit(143) and Implicit(993) TLS + check_cipherlists "${RESULTS_PATH}/port_143.json" + check_cipherlists "${RESULTS_PATH}/port_993.json" + + # POP3: Mandatory STARTTLS Explicit(110) and Implicit(995) + check_cipherlists "${RESULTS_PATH}/port_110.json" + check_cipherlists "${RESULTS_PATH}/port_995.json" } function collect_cipherlist_data() { - local ALT_CERT=() - local ALT_KEY=() + local ALT_CERT=() + local ALT_KEY=() - if [[ -n ${ALT_KEY_TYPE} ]] - then - ALT_CERT=(--env SSL_ALT_CERT_PATH="/config/ssl/cert.${ALT_KEY_TYPE}.pem") - ALT_KEY=(--env SSL_ALT_KEY_PATH="/config/ssl/key.${ALT_KEY_TYPE}.pem") - fi + if [[ -n ${ALT_KEY_TYPE} ]] + then + ALT_CERT=(--env SSL_ALT_CERT_PATH="/config/ssl/cert.${ALT_KEY_TYPE}.pem") + ALT_KEY=(--env SSL_ALT_KEY_PATH="/config/ssl/key.${ALT_KEY_TYPE}.pem") + fi - run docker run -d --name tls_test_cipherlists \ - --volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \ - --volume "${TLS_CONFIG_VOLUME}" \ - --env ENABLE_POP3=1 \ - --env SSL_TYPE="manual" \ - --env SSL_CERT_PATH="/config/ssl/cert.${KEY_TYPE}.pem" \ - --env SSL_KEY_PATH="/config/ssl/key.${KEY_TYPE}.pem" \ - "${ALT_CERT[@]}" \ - "${ALT_KEY[@]}" \ - --env TLS_LEVEL="${TLS_LEVEL}" \ - --network "${NETWORK}" \ - --network-alias "${DOMAIN}" \ - --hostname "mail.${DOMAIN}" \ - --tty \ - "${NAME}" # Image name - assert_success + run docker run -d --name tls_test_cipherlists \ + --volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \ + --volume "${TLS_CONFIG_VOLUME}" \ + --env ENABLE_POP3=1 \ + --env SSL_TYPE="manual" \ + --env SSL_CERT_PATH="/config/ssl/cert.${KEY_TYPE}.pem" \ + --env SSL_KEY_PATH="/config/ssl/key.${KEY_TYPE}.pem" \ + "${ALT_CERT[@]}" \ + "${ALT_KEY[@]}" \ + --env TLS_LEVEL="${TLS_LEVEL}" \ + --network "${NETWORK}" \ + --network-alias "${DOMAIN}" \ + --hostname "mail.${DOMAIN}" \ + --tty \ + "${NAME}" # Image name - wait_for_tcp_port_in_container 25 tls_test_cipherlists - # NOTE: An rDNS query for the container IP will resolve to `..` + assert_success - # Make directory with test user ownership. Avoids Docker creating with root ownership. - # TODO: Can switch to filename prefix for JSON output when this is resolved: https://github.com/drwetter/testssl.sh/issues/1845 - mkdir -p "${TLS_RESULTS_DIR}/${RESULTS_PATH}" + wait_for_tcp_port_in_container 25 tls_test_cipherlists + # NOTE: An rDNS query for the container IP will resolve to `..` - # For non-CI test runs, instead of removing prior test files after this test suite completes, - # they're retained and overwritten by future test runs instead. Useful for inspection. - # `--preference` reduces the test scope to the cipher suites reported as supported by the server. Completes in ~35% of the time. - local TESTSSL_CMD=(--quiet --file "/config/ssl/testssl.txt" --mode parallel --overwrite --preference) - # NOTE: Batch testing ports via `--file` doesn't properly bubble up failure. - # If the failure for a test is misleading consider testing a single port with: - # local TESTSSL_CMD=(--quiet --jsonfile-pretty "${RESULTS_PATH}/port_${PORT}.json" --starttls smtp "${DOMAIN}:${PORT}") - # TODO: Can use `jq` to check for failure when this is resolved: https://github.com/drwetter/testssl.sh/issues/1844 + # Make directory with test user ownership. Avoids Docker creating with root ownership. + # TODO: Can switch to filename prefix for JSON output when this is resolved: https://github.com/drwetter/testssl.sh/issues/1845 + mkdir -p "${TLS_RESULTS_DIR}/${RESULTS_PATH}" - # `--user ":"` is a workaround: Avoids `permission denied` write errors for json output, uses `id` to match user uid & gid. - run docker run --rm \ - --user "$(id -u):$(id -g)" \ - --network "${NETWORK}" \ - --volume "${TLS_CONFIG_VOLUME}" \ - --volume "${TLS_RESULTS_DIR}/${RESULTS_PATH}/:/output" \ - --workdir "/output" \ - drwetter/testssl.sh:3.1dev "${TESTSSL_CMD[@]}" - assert_success + # For non-CI test runs, instead of removing prior test files after this test suite completes, + # they're retained and overwritten by future test runs instead. Useful for inspection. + # `--preference` reduces the test scope to the cipher suites reported as supported by the server. Completes in ~35% of the time. + local TESTSSL_CMD=(--quiet --file "/config/ssl/testssl.txt" --mode parallel --overwrite --preference) + # NOTE: Batch testing ports via `--file` doesn't properly bubble up failure. + # If the failure for a test is misleading consider testing a single port with: + # local TESTSSL_CMD=(--quiet --jsonfile-pretty "${RESULTS_PATH}/port_${PORT}.json" --starttls smtp "${DOMAIN}:${PORT}") + # TODO: Can use `jq` to check for failure when this is resolved: https://github.com/drwetter/testssl.sh/issues/1844 + + # `--user ":"` is a workaround: Avoids `permission denied` write errors for json output, uses `id` to match user uid & gid. + run docker run --rm \ + --user "$(id -u):$(id -g)" \ + --network "${NETWORK}" \ + --volume "${TLS_CONFIG_VOLUME}" \ + --volume "${TLS_RESULTS_DIR}/${RESULTS_PATH}/:/output" \ + --workdir "/output" \ + drwetter/testssl.sh:3.1dev "${TESTSSL_CMD[@]}" + + assert_success } # Use `jq` to extract a specific cipher list from the target`testssl.sh` results json output file function compare_cipherlist() { - local TARGET_CIPHERLIST=$1 - local RESULTS_FILE=$2 - local EXPECTED_CIPHERLIST=$3 + local TARGET_CIPHERLIST=$1 + local RESULTS_FILE=$2 + local EXPECTED_CIPHERLIST=$3 - run jq '.scanResult[0].serverPreferences[] | select(.id=="'"${TARGET_CIPHERLIST}"'") | .finding' "${TLS_RESULTS_DIR}/${RESULTS_FILE}" - assert_success - assert_output "${EXPECTED_CIPHERLIST}" + run jq '.scanResult[0].serverPreferences[] | select(.id=="'"${TARGET_CIPHERLIST}"'") | .finding' "${TLS_RESULTS_DIR}/${RESULTS_FILE}" + assert_success + assert_output "${EXPECTED_CIPHERLIST}" } # Compares the expected cipher lists against logged test results from `testssl.sh` function check_cipherlists() { - local RESULTS_FILE=$1 - local p25=$2 # optional suffix + local RESULTS_FILE=$1 + local p25=$2 # optional suffix - # TLS_LEVEL `modern` doesn't have TLS v1.0 or v1.1 cipher suites. Sets TLS v1.2 as minimum. - if [[ ${TLS_LEVEL} == "intermediate" ]] - then - compare_cipherlist "cipherorder_TLSv1" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1${p25}")" - compare_cipherlist "cipherorder_TLSv1_1" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1_1${p25}")" - fi - compare_cipherlist "cipherorder_TLSv1_2" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1_2${p25}")" - compare_cipherlist "cipherorder_TLSv1_3" "${RESULTS_FILE}" "$(get_cipherlist 'TLSv1_3')" + # TLS_LEVEL `modern` doesn't have TLS v1.0 or v1.1 cipher suites. Sets TLS v1.2 as minimum. + if [[ ${TLS_LEVEL} == "intermediate" ]] + then + compare_cipherlist "cipherorder_TLSv1" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1${p25}")" + compare_cipherlist "cipherorder_TLSv1_1" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1_1${p25}")" + fi + + compare_cipherlist "cipherorder_TLSv1_2" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1_2${p25}")" + compare_cipherlist "cipherorder_TLSv1_3" "${RESULTS_FILE}" "$(get_cipherlist 'TLSv1_3')" } # Expected cipher lists. Should match `TLS_LEVEL` cipher lists set in `start-mailserver.sh`. @@ -175,64 +181,63 @@ function check_cipherlists() { # NOTE: If a test fails, look at the `check_ports` params, then update the corresponding associative key's value # with the `actual` error value (assuming an update needs to be made, and not a valid security issue to look into). function get_cipherlist() { - local TLS_VERSION=$1 + local TLS_VERSION=$1 - if [[ ${TLS_VERSION} == "TLSv1_3" ]] - then - # TLS v1.3 cipher suites are not user defineable and not unique to the available certificate(s). - # They do not support server enforced order either. - echo '"TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256"' - else + if [[ ${TLS_VERSION} == "TLSv1_3" ]] + then + # TLS v1.3 cipher suites are not user defineable and not unique to the available certificate(s). + # They do not support server enforced order either. + echo '"TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256"' + else + # Associative array for easy querying of required cipher list + declare -A CIPHER_LIST - # Associative array for easy querying of required cipher list - declare -A CIPHER_LIST + # `intermediate` cipher lists TLS v1.0 and v1.1 cipher suites should be the same: + CIPHER_LIST["rsa_intermediate_TLSv1"]='"ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA"' + CIPHER_LIST["rsa_intermediate_TLSv1_1"]=${CIPHER_LIST["rsa_intermediate_TLSv1"]} + CIPHER_LIST["rsa_intermediate_TLSv1_2"]='"ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA"' + # `modern` cipher lists shouldn't have TLS v1.0 or v1.1 cipher suites: + CIPHER_LIST["rsa_modern_TLSv1_2"]='"ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384"' - # `intermediate` cipher lists TLS v1.0 and v1.1 cipher suites should be the same: - CIPHER_LIST["rsa_intermediate_TLSv1"]='"ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA"' - CIPHER_LIST["rsa_intermediate_TLSv1_1"]=${CIPHER_LIST["rsa_intermediate_TLSv1"]} - CIPHER_LIST["rsa_intermediate_TLSv1_2"]='"ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA"' - # `modern` cipher lists shouldn't have TLS v1.0 or v1.1 cipher suites: - CIPHER_LIST["rsa_modern_TLSv1_2"]='"ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384"' + # ECDSA: + CIPHER_LIST["ecdsa_intermediate_TLSv1"]='"ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA"' + CIPHER_LIST["ecdsa_intermediate_TLSv1_1"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1"]} + CIPHER_LIST["ecdsa_intermediate_TLSv1_2"]='"ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA"' + CIPHER_LIST["ecdsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305"' - # ECDSA: - CIPHER_LIST["ecdsa_intermediate_TLSv1"]='"ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA"' - CIPHER_LIST["ecdsa_intermediate_TLSv1_1"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1"]} - CIPHER_LIST["ecdsa_intermediate_TLSv1_2"]='"ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA"' - CIPHER_LIST["ecdsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305"' - - # ECDSA + RSA fallback, dual cert support: - CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1"]='"ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA"' - CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_1"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1"]} - CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2"]='"ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA"' - CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384"' + # ECDSA + RSA fallback, dual cert support: + CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1"]='"ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA"' + CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_1"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1"]} + CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2"]='"ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA"' + CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384"' - # Port 25 - # TLSv1 and TLSv1_1 share the same cipher suites as other ports have. But the server order differs: - CIPHER_LIST["rsa_intermediate_TLSv1_p25"]='"ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA"' - CIPHER_LIST["rsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["rsa_intermediate_TLSv1_p25"]} - # TLSv1_2 has different server order and also includes ARIA, CCM, DHE+CHACHA20-POLY1305 cipher suites: - CIPHER_LIST["rsa_intermediate_TLSv1_2_p25"]='"ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES256-CCM8 DHE-RSA-AES256-CCM ECDHE-ARIA256-GCM-SHA384 DHE-RSA-ARIA256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ARIA256-GCM-SHA384 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-CCM8 DHE-RSA-AES128-CCM ECDHE-ARIA128-GCM-SHA256 DHE-RSA-ARIA128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA ARIA128-GCM-SHA256"' - # Port 25 is unaffected by `TLS_LEVEL` profiles (other than min TLS version), it has the same TLS v1.2 cipher list under both: - CIPHER_LIST["rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["rsa_intermediate_TLSv1_2_p25"]} + # Port 25 + # TLSv1 and TLSv1_1 share the same cipher suites as other ports have. But the server order differs: + CIPHER_LIST["rsa_intermediate_TLSv1_p25"]='"ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA"' + CIPHER_LIST["rsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["rsa_intermediate_TLSv1_p25"]} + # TLSv1_2 has different server order and also includes ARIA, CCM, DHE+CHACHA20-POLY1305 cipher suites: + CIPHER_LIST["rsa_intermediate_TLSv1_2_p25"]='"ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES256-CCM8 DHE-RSA-AES256-CCM ECDHE-ARIA256-GCM-SHA384 DHE-RSA-ARIA256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ARIA256-GCM-SHA384 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-CCM8 DHE-RSA-AES128-CCM ECDHE-ARIA128-GCM-SHA256 DHE-RSA-ARIA128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA ARIA128-GCM-SHA256"' + # Port 25 is unaffected by `TLS_LEVEL` profiles (other than min TLS version), it has the same TLS v1.2 cipher list under both: + CIPHER_LIST["rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["rsa_intermediate_TLSv1_2_p25"]} - # ECDSA (Port 25): - CIPHER_LIST["ecdsa_intermediate_TLSv1_p25"]='"ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA"' - CIPHER_LIST["ecdsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_p25"]} + # ECDSA (Port 25): + CIPHER_LIST["ecdsa_intermediate_TLSv1_p25"]='"ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA"' + CIPHER_LIST["ecdsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_p25"]} - CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]='"ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-CCM8 ECDHE-ECDSA-AES256-CCM ECDHE-ECDSA-ARIA256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA"' - CIPHER_LIST["ecdsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]} + CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]='"ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-CCM8 ECDHE-ECDSA-AES256-CCM ECDHE-ECDSA-ARIA256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA"' + CIPHER_LIST["ecdsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]} - # ECDSA + RSA fallback, dual cert support (Port 25): - CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_p25"]='"ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA"' - CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_p25"]} + # ECDSA + RSA fallback, dual cert support (Port 25): + CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_p25"]='"ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA"' + CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_p25"]} - CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]='"ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-CCM8 ECDHE-ECDSA-AES256-CCM DHE-RSA-AES256-CCM8 DHE-RSA-AES256-CCM ECDHE-ECDSA-ARIA256-GCM-SHA384 ECDHE-ARIA256-GCM-SHA384 DHE-RSA-ARIA256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ARIA256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM DHE-RSA-AES128-CCM8 DHE-RSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ARIA128-GCM-SHA256 DHE-RSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA ARIA128-GCM-SHA256"' - CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]} + CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]='"ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-CCM8 ECDHE-ECDSA-AES256-CCM DHE-RSA-AES256-CCM8 DHE-RSA-AES256-CCM ECDHE-ECDSA-ARIA256-GCM-SHA384 ECDHE-ARIA256-GCM-SHA384 DHE-RSA-ARIA256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ARIA256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM DHE-RSA-AES128-CCM8 DHE-RSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ARIA128-GCM-SHA256 DHE-RSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA ARIA128-GCM-SHA256"' + CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]} - local TARGET_QUERY="${KEY_TYPE_LABEL}_${TLS_LEVEL}_${TLS_VERSION}" - echo "${CIPHER_LIST[${TARGET_QUERY}]}" - fi + local TARGET_QUERY="${KEY_TYPE_LABEL}_${TLS_LEVEL}_${TLS_VERSION}" + echo "${CIPHER_LIST[${TARGET_QUERY}]}" + fi } diff --git a/test/sedfile.bats b/test/sedfile.bats index 1880a17f..c19c2fc1 100644 --- a/test/sedfile.bats +++ b/test/sedfile.bats @@ -9,8 +9,8 @@ function setup_file() { PRIVATE_CONFIG="$(duplicate_config_for_container . )" docker run -d --name "${CONTAINER}" \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -h mail.my-domain.com "${NAME}" + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com "${NAME}" wait_for_finished_setup_in_container "${CONTAINER}" } diff --git a/test/test_helper.bats b/test/test_helper.bats index 57d54967..5d785fc9 100644 --- a/test/test_helper.bats +++ b/test/test_helper.bats @@ -3,255 +3,267 @@ load 'test_helper/bats-assert/load' load 'test_helper/common' @test "repeat_until_success_or_timeout returns instantly on success" { - SECONDS=0 - repeat_until_success_or_timeout 1 true - [[ ${SECONDS} -le 1 ]] + SECONDS=0 + repeat_until_success_or_timeout 1 true + [[ ${SECONDS} -le 1 ]] } @test "repeat_until_success_or_timeout waits for timeout on persistent failure" { - SECONDS=0 - run repeat_until_success_or_timeout 2 false - [[ ${SECONDS} -ge 2 ]] - assert_failure - assert_output --partial "Timed out on command" + SECONDS=0 + run repeat_until_success_or_timeout 2 false + [[ ${SECONDS} -ge 2 ]] + assert_failure + assert_output --partial "Timed out on command" } @test "repeat_until_success_or_timeout aborts immediately on fatal failure" { - SECONDS=0 - run repeat_until_success_or_timeout --fatal-test false 2 false - [[ ${SECONDS} -le 1 ]] - assert_failure - assert_output --partial "early aborting" + SECONDS=0 + run repeat_until_success_or_timeout --fatal-test false 2 false + [[ ${SECONDS} -le 1 ]] + assert_failure + assert_output --partial "early aborting" } @test "repeat_until_success_or_timeout expects integer timeout" { - run repeat_until_success_or_timeout 1 true - assert_success + run repeat_until_success_or_timeout 1 true + assert_success - run repeat_until_success_or_timeout timeout true - assert_failure + run repeat_until_success_or_timeout timeout true + assert_failure - run repeat_until_success_or_timeout --fatal-test true timeout true - assert_failure + run repeat_until_success_or_timeout --fatal-test true timeout true + assert_failure } @test "run_until_success_or_timeout returns instantly on success" { - SECONDS=0 - run_until_success_or_timeout 2 true - [[ ${SECONDS} -le 1 ]] - assert_success + SECONDS=0 + run_until_success_or_timeout 2 true + [[ ${SECONDS} -le 1 ]] + assert_success } @test "run_until_success_or_timeout waits for timeout on persistent failure" { - SECONDS=0 - ! run_until_success_or_timeout 2 false - [[ ${SECONDS} -ge 2 ]] - assert_failure + SECONDS=0 + ! run_until_success_or_timeout 2 false + [[ ${SECONDS} -ge 2 ]] + assert_failure } @test "repeat_in_container_until_success_or_timeout fails immediately for non-running container" { - SECONDS=0 - ! repeat_in_container_until_success_or_timeout 10 name-of-non-existing-container true - [[ ${SECONDS} -le 1 ]] + SECONDS=0 + ! repeat_in_container_until_success_or_timeout 10 name-of-non-existing-container true + [[ ${SECONDS} -le 1 ]] } @test "repeat_in_container_until_success_or_timeout run command in container" { - local CONTAINER_NAME - CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) - SECONDS=0 - ! repeat_in_container_until_success_or_timeout 10 "${CONTAINER_NAME}" sh -c "echo '${CONTAINER_NAME}' > /tmp/marker" - [[ ${SECONDS} -le 1 ]] - run docker exec "${CONTAINER_NAME}" cat /tmp/marker - assert_output "${CONTAINER_NAME}" + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) + SECONDS=0 + ! repeat_in_container_until_success_or_timeout 10 "${CONTAINER_NAME}" sh -c "echo '${CONTAINER_NAME}' > /tmp/marker" + [[ ${SECONDS} -le 1 ]] + run docker exec "${CONTAINER_NAME}" cat /tmp/marker + assert_output "${CONTAINER_NAME}" } @test "container_is_running" { - local CONTAINER_NAME - CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) - container_is_running "${CONTAINER_NAME}" - docker rm -f "${CONTAINER_NAME}" - ! container_is_running "${CONTAINER_NAME}" + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) + container_is_running "${CONTAINER_NAME}" + docker rm -f "${CONTAINER_NAME}" + ! container_is_running "${CONTAINER_NAME}" } @test "wait_for_smtp_port_in_container aborts wait after timeout" { - local CONTAINER_NAME - CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) - SECONDS=0 - TEST_TIMEOUT_IN_SECONDS=2 run wait_for_smtp_port_in_container "${CONTAINER_NAME}" - [[ ${SECONDS} -ge 2 ]] - assert_failure - assert_output --partial "Timed out on command" + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) + SECONDS=0 + TEST_TIMEOUT_IN_SECONDS=2 run wait_for_smtp_port_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -ge 2 ]] + assert_failure + assert_output --partial "Timed out on command" } +# NOTE: Test requires external network access available @test "wait_for_smtp_port_in_container returns immediately when port found" { - local CONTAINER_NAME - CONTAINER_NAME=$(docker run --rm -d alpine sh -c "sleep 10") + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sh -c "sleep 10") - docker exec "${CONTAINER_NAME}" apk add netcat-openbsd - docker exec "${CONTAINER_NAME}" nc -l 25 & + docker exec "${CONTAINER_NAME}" apk add netcat-openbsd + docker exec "${CONTAINER_NAME}" nc -l 25 & - SECONDS=0 - TEST_TIMEOUT_IN_SECONDS=5 run wait_for_smtp_port_in_container "${CONTAINER_NAME}" - [[ ${SECONDS} -lt 5 ]] - assert_success + SECONDS=0 + TEST_TIMEOUT_IN_SECONDS=5 run wait_for_smtp_port_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -lt 5 ]] + assert_success } @test "wait_for_finished_setup_in_container" { - # variable not local to make visible to teardown - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - CONTAINER_NAME=$(docker run -d --rm \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -h mail.my-domain.com \ - -t "${NAME}") - teardown() { docker rm -f "${CONTAINER_NAME}"; } + # variable not local to make visible to teardown + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - # the setup should not be finished immediately after starting - ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_finished_setup_in_container "${CONTAINER_NAME}" + CONTAINER_NAME=$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}") - # but it will finish eventually - SECONDS=1 - wait_for_finished_setup_in_container "${CONTAINER_NAME}" - [[ ${SECONDS} -gt 0 ]] + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + # the setup should not be finished immediately after starting + ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_finished_setup_in_container "${CONTAINER_NAME}" + + # but it will finish eventually + SECONDS=1 + + wait_for_finished_setup_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -gt 0 ]] } @test "duplicate_config_for_container" { - local path - path=$(duplicate_config_for_container duplicate_config_test) + local path + path=$(duplicate_config_for_container duplicate_config_test) - run cat "${path}/marker" - assert_line "This marker file is there to identify the correct config being copied" + run cat "${path}/marker" + assert_line "This marker file is there to identify the correct config being copied" - run duplicate_config_for_container non-existant-source-folder "${BATS_TEST_NAME}2" - assert_failure + run duplicate_config_for_container non-existant-source-folder "${BATS_TEST_NAME}2" + assert_failure } @test "container_has_service_running/wait_for_service" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - # variable not local to make visible to teardown - CONTAINER_NAME=$(docker run -d --rm \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -h mail.my-domain.com \ - -t "${NAME}") - teardown() { docker rm -f "${CONTAINER_NAME}"; } + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - # pick a service that was not started - ! container_has_service_running "${CONTAINER_NAME}" clamav + # variable not local to make visible to teardown + CONTAINER_NAME=$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}") - # wait for a service that should be started - wait_for_service "${CONTAINER_NAME}" postfix + teardown() { docker rm -f "${CONTAINER_NAME}"; } - # shut down the service - docker exec "${CONTAINER_NAME}" supervisorctl stop postfix + # pick a service that was not started + ! container_has_service_running "${CONTAINER_NAME}" clamav - # now it should be off - SECONDS=0 - TEST_TIMEOUT_IN_SECONDS=5 run wait_for_service "${CONTAINER_NAME}" postfix - [[ ${SECONDS} -ge 5 ]] - assert_failure + # wait for a service that should be started + wait_for_service "${CONTAINER_NAME}" postfix + + # shut down the service + docker exec "${CONTAINER_NAME}" supervisorctl stop postfix + + # now it should be off + SECONDS=0 + TEST_TIMEOUT_IN_SECONDS=5 run wait_for_service "${CONTAINER_NAME}" postfix + [[ ${SECONDS} -ge 5 ]] + assert_failure } @test "wait_for_changes_to_be_detected_in_container fails when timeout is reached" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - # variable not local to make visible to teardown - CONTAINER_NAME=$(docker run -d --rm \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -h mail.my-domain.com \ - -t "${NAME}") - teardown() { docker rm -f "${CONTAINER_NAME}"; } + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - # wait for the initial checksum detection to complete - repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /tmp/docker-mailserver-config-chksum + # variable not local to make visible to teardown + CONTAINER_NAME=$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}") - # there should be no changes in the beginning - TEST_TIMEOUT_IN_SECONDS=0 wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" + teardown() { docker rm -f "${CONTAINER_NAME}"; } - # trigger some change - docker exec "${CONTAINER_NAME}" /bin/sh -c "addmailuser auser3@mail.my-domain.com mypassword" + # wait for the initial checksum detection to complete + repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /tmp/docker-mailserver-config-chksum - # that should be picked up as not yet detected - ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" + # there should be no changes in the beginning + TEST_TIMEOUT_IN_SECONDS=0 wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" + + # trigger some change + docker exec "${CONTAINER_NAME}" /bin/sh -c "addmailuser auser3@mail.my-domain.com mypassword" + + # that should be picked up as not yet detected + ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" } @test "wait_for_changes_to_be_detected_in_container succeeds within timeout" { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - # variable not local to make visible to teardown - CONTAINER_NAME=$(docker run -d --rm \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -h mail.my-domain.com \ - -t "${NAME}") - teardown() { docker rm -f "${CONTAINER_NAME}"; } + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - # wait for the initial checksum detection to complete - repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /tmp/docker-mailserver-config-chksum + # variable not local to make visible to teardown + CONTAINER_NAME=$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}") - # trigger some change - docker exec "${CONTAINER_NAME}" /bin/sh -c "addmailuser auser3@mail.my-domain.com mypassword" + teardown() { docker rm -f "${CONTAINER_NAME}"; } - # that should eventually be detected - SECONDS=0 - wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" - [[ ${SECONDS} -gt 0 ]] + # wait for the initial checksum detection to complete + repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /tmp/docker-mailserver-config-chksum + + # trigger some change + docker exec "${CONTAINER_NAME}" /bin/sh -c "addmailuser auser3@mail.my-domain.com mypassword" + + # that should eventually be detected + SECONDS=0 + wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -gt 0 ]] } # TODO investigate why this test fails @test "wait_for_empty_mail_queue_in_container fails when timeout reached" { - skip 'disabled as it fails randomly: https://github.com/docker-mailserver/docker-mailserver/pull/2177' + skip 'disabled as it fails randomly: https://github.com/docker-mailserver/docker-mailserver/pull/2177' - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - # variable not local to make visible to teardown - # 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 \ - -e ENABLE_CLAMAV=1 \ - -h mail.my-domain.com \ - -t "${NAME}") + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - teardown() { docker rm -f "${CONTAINER_NAME}"; } + # variable not local to make visible to teardown + # 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 \ + -e ENABLE_CLAMAV=1 \ + -h mail.my-domain.com \ + -t "${NAME}") - wait_for_smtp_port_in_container "${CONTAINER_NAME}" || docker logs "${CONTAINER_NAME}" + teardown() { docker rm -f "${CONTAINER_NAME}"; } - SECONDS=0 - # no mails -> should return immediately - TEST_TIMEOUT_IN_SECONDS=5 wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" - [[ ${SECONDS} -lt 5 ]] + 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" + SECONDS=0 + # no mails -> should return immediately + TEST_TIMEOUT_IN_SECONDS=5 wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -lt 5 ]] - # that should still be stuck in the queue - ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_empty_mail_queue_in_container "${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" + + # that should still be stuck in the queue + ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" } # TODO investigate why this test fails @test "wait_for_empty_mail_queue_in_container succeeds within timeout" { - skip 'disabled as it fails randomly: https://github.com/docker-mailserver/docker-mailserver/pull/2177' + skip 'disabled as it fails randomly: https://github.com/docker-mailserver/docker-mailserver/pull/2177' - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container .) - # variable not local to make visible to teardown - # 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 \ - -e ENABLE_CLAMAV=1 \ - -h mail.my-domain.com \ - -t "${NAME}") + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container .) - teardown() { docker rm -f "${CONTAINER_NAME}"; } + # variable not local to make visible to teardown + # 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 \ + -e ENABLE_CLAMAV=1 \ + -h mail.my-domain.com \ + -t "${NAME}") - wait_for_smtp_port_in_container "${CONTAINER_NAME}" || docker logs "${CONTAINER_NAME}" + teardown() { docker rm -f "${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" + wait_for_smtp_port_in_container "${CONTAINER_NAME}" || docker logs "${CONTAINER_NAME}" - # give it some time to clear the queue - SECONDS=0 - wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" - [[ ${SECONDS} -gt 0 ]] + # 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" + + # give it some time to clear the queue + SECONDS=0 + wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -gt 0 ]] } diff --git a/test/test_helper/common.bash b/test/test_helper/common.bash index 514a387f..8a1cf7f2 100644 --- a/test/test_helper/common.bash +++ b/test/test_helper/common.bash @@ -13,76 +13,86 @@ NUMBER_OF_LOG_LINES=${NUMBER_OF_LOG_LINES-10} # @param --fatal-test additional test whose failure aborts immediately # @param ... test to run function repeat_until_success_or_timeout { - local FATAL_FAILURE_TEST_COMMAND - if [[ "${1}" == "--fatal-test" ]]; then - FATAL_FAILURE_TEST_COMMAND="${2}" - shift 2 + local FATAL_FAILURE_TEST_COMMAND + + if [[ "${1}" == "--fatal-test" ]]; then + FATAL_FAILURE_TEST_COMMAND="${2}" + shift 2 + fi + + if ! [[ "${1}" =~ ^[0-9]+$ ]]; then + echo "First parameter for timeout must be an integer, recieved \"${1}\"" + return 1 + fi + + local TIMEOUT=${1} + local STARTTIME=${SECONDS} + shift 1 + + until "${@}" + do + if [[ -n ${FATAL_FAILURE_TEST_COMMAND} ]] && ! eval "${FATAL_FAILURE_TEST_COMMAND}"; then + echo "\`${FATAL_FAILURE_TEST_COMMAND}\` failed, early aborting repeat_until_success of \`${*}\`" >&2 + return 1 fi - if ! [[ "${1}" =~ ^[0-9]+$ ]]; then - echo "First parameter for timeout must be an integer, recieved \"${1}\"" - return 1 + + sleep 1 + + if [[ $(( SECONDS - STARTTIME )) -gt ${TIMEOUT} ]]; then + echo "Timed out on command: ${*}" >&2 + return 1 fi - local TIMEOUT=${1} - local STARTTIME=${SECONDS} - shift 1 - until "${@}" - do - if [[ -n ${FATAL_FAILURE_TEST_COMMAND} ]] && ! eval "${FATAL_FAILURE_TEST_COMMAND}"; then - echo "\`${FATAL_FAILURE_TEST_COMMAND}\` failed, early aborting repeat_until_success of \`${*}\`" >&2 - return 1 - fi - sleep 1 - if [[ $(( SECONDS - STARTTIME )) -gt ${TIMEOUT} ]]; then - echo "Timed out on command: ${*}" >&2 - return 1 - fi - done + done } # like repeat_until_success_or_timeout but with wrapping the command to run into `run` for later bats consumption # @param ${1} timeout # @param ... test command to run function run_until_success_or_timeout { - if ! [[ ${1} =~ ^[0-9]+$ ]]; then - echo "First parameter for timeout must be an integer, recieved \"${1}\"" - return 1 + if ! [[ ${1} =~ ^[0-9]+$ ]]; then + echo "First parameter for timeout must be an integer, recieved \"${1}\"" + return 1 + fi + + local TIMEOUT=${1} + local STARTTIME=${SECONDS} + shift 1 + + until run "${@}" && [[ $status -eq 0 ]] + do + sleep 1 + + if (( SECONDS - STARTTIME > TIMEOUT )); then + echo "Timed out on command: ${*}" >&2 + return 1 fi - local TIMEOUT=${1} - local STARTTIME=${SECONDS} - shift 1 - until run "${@}" && [[ $status -eq 0 ]] - do - sleep 1 - if (( SECONDS - STARTTIME > TIMEOUT )); then - echo "Timed out on command: ${*}" >&2 - return 1 - fi - done + done } # @param ${1} timeout # @param ${2} container name # @param ... test command for container function repeat_in_container_until_success_or_timeout() { - local TIMEOUT="${1}" - local CONTAINER_NAME="${2}" - shift 2 - repeat_until_success_or_timeout --fatal-test "container_is_running ${CONTAINER_NAME}" "${TIMEOUT}" docker exec "${CONTAINER_NAME}" "${@}" + local TIMEOUT="${1}" + local CONTAINER_NAME="${2}" + shift 2 + + repeat_until_success_or_timeout --fatal-test "container_is_running ${CONTAINER_NAME}" "${TIMEOUT}" docker exec "${CONTAINER_NAME}" "${@}" } function container_is_running() { - [[ "$(docker inspect -f '{{.State.Running}}' "${1}")" == "true" ]] + [[ "$(docker inspect -f '{{.State.Running}}' "${1}")" == "true" ]] } # @param ${1} port # @param ${2} container name function wait_for_tcp_port_in_container() { - repeat_until_success_or_timeout --fatal-test "container_is_running ${2}" "${TEST_TIMEOUT_IN_SECONDS}" docker exec "${2}" /bin/sh -c "nc -z 0.0.0.0 ${1}" + repeat_until_success_or_timeout --fatal-test "container_is_running ${2}" "${TEST_TIMEOUT_IN_SECONDS}" docker exec "${2}" /bin/sh -c "nc -z 0.0.0.0 ${1}" } # @param ${1} name of the postfix container function wait_for_smtp_port_in_container() { - wait_for_tcp_port_in_container 25 "${1}" + wait_for_tcp_port_in_container 25 "${1}" } # @param ${1} name of the postfix container @@ -94,6 +104,7 @@ function wait_for_smtp_port_in_container_to_respond() { echo "Unable to receive a valid response from 'nc localhost 25' within 20 seconds" return 1 fi + sleep 1 ((COUNT+=1)) done @@ -101,67 +112,73 @@ function wait_for_smtp_port_in_container_to_respond() { # @param ${1} name of the postfix container function wait_for_amavis_port_in_container() { - wait_for_tcp_port_in_container 10024 "${1}" + wait_for_tcp_port_in_container 10024 "${1}" } # TODO: Should also fail early on "docker logs ${1} | egrep '^[ FATAL ]'"? # @param ${1} name of the postfix container function wait_for_finished_setup_in_container() { - local STATUS=0 - repeat_until_success_or_timeout --fatal-test "container_is_running ${1}" "${TEST_TIMEOUT_IN_SECONDS}" sh -c "docker logs ${1} | grep 'is up and running'" || STATUS=1 - if [[ ${STATUS} -eq 1 ]]; then - echo "Last ${NUMBER_OF_LOG_LINES} lines of container \`${1}\`'s log" - docker logs "${1}" | tail -n "${NUMBER_OF_LOG_LINES}" - fi - return ${STATUS} + local STATUS=0 + repeat_until_success_or_timeout --fatal-test "container_is_running ${1}" "${TEST_TIMEOUT_IN_SECONDS}" sh -c "docker logs ${1} | grep 'is up and running'" || STATUS=1 + + if [[ ${STATUS} -eq 1 ]]; then + echo "Last ${NUMBER_OF_LOG_LINES} lines of container \`${1}\`'s log" + docker logs "${1}" | tail -n "${NUMBER_OF_LOG_LINES}" + fi + + return ${STATUS} } SETUP_FILE_MARKER="${BATS_TMPDIR}/$(basename "${BATS_TEST_FILENAME}").setup_file" # get the private config path for the given container or test file, if no container name was given function private_config_path() { - echo "${PWD}/test/duplicate_configs/${1:-$(basename "${BATS_TEST_FILENAME}")}" + echo "${PWD}/test/duplicate_configs/${1:-$(basename "${BATS_TEST_FILENAME}")}" } # @param ${1} relative source in test/config folder # @param ${2} (optional) container name, defaults to ${BATS_TEST_FILENAME} # @return path to the folder where the config is duplicated function duplicate_config_for_container() { - local OUTPUT_FOLDER - OUTPUT_FOLDER=$(private_config_path "${2}") || return $? - rm -rf "${OUTPUT_FOLDER:?}/" || return $? # cleanup - mkdir -p "${OUTPUT_FOLDER}" || return $? - cp -r "${PWD}/test/config/${1:?}/." "${OUTPUT_FOLDER}" || return $? - echo "${OUTPUT_FOLDER}" + local OUTPUT_FOLDER + OUTPUT_FOLDER=$(private_config_path "${2}") || return $? + + rm -rf "${OUTPUT_FOLDER:?}/" || return $? # cleanup + mkdir -p "${OUTPUT_FOLDER}" || return $? + cp -r "${PWD}/test/config/${1:?}/." "${OUTPUT_FOLDER}" || return $? + + echo "${OUTPUT_FOLDER}" } function container_has_service_running() { - local CONTAINER_NAME="${1}" - local SERVICE_NAME="${2}" - docker exec "${CONTAINER_NAME}" /usr/bin/supervisorctl status "${SERVICE_NAME}" | grep RUNNING >/dev/null + local CONTAINER_NAME="${1}" + local SERVICE_NAME="${2}" + + docker exec "${CONTAINER_NAME}" /usr/bin/supervisorctl status "${SERVICE_NAME}" | grep RUNNING >/dev/null } function wait_for_service() { - local CONTAINER_NAME="${1}" - local SERVICE_NAME="${2}" - repeat_until_success_or_timeout --fatal-test "container_is_running ${CONTAINER_NAME}" "${TEST_TIMEOUT_IN_SECONDS}" \ - container_has_service_running "${CONTAINER_NAME}" "${SERVICE_NAME}" + local CONTAINER_NAME="${1}" + local SERVICE_NAME="${2}" + + repeat_until_success_or_timeout --fatal-test "container_is_running ${CONTAINER_NAME}" "${TEST_TIMEOUT_IN_SECONDS}" \ + container_has_service_running "${CONTAINER_NAME}" "${SERVICE_NAME}" } function wait_for_changes_to_be_detected_in_container() { - local CONTAINER_NAME="${1}" - local TIMEOUT=${TEST_TIMEOUT_IN_SECONDS} + local CONTAINER_NAME="${1}" + local TIMEOUT=${TEST_TIMEOUT_IN_SECONDS} - # shellcheck disable=SC2016 - repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c 'source /usr/local/bin/helpers/index.sh; cmp --silent -- <(_monitored_files_checksums) "${CHKSUM_FILE}" >/dev/null' + # shellcheck disable=SC2016 + repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c 'source /usr/local/bin/helpers/index.sh; cmp --silent -- <(_monitored_files_checksums) "${CHKSUM_FILE}" >/dev/null' } function wait_for_empty_mail_queue_in_container() { - local CONTAINER_NAME="${1}" - local TIMEOUT=${TEST_TIMEOUT_IN_SECONDS} + local CONTAINER_NAME="${1}" + local TIMEOUT=${TEST_TIMEOUT_IN_SECONDS} - # shellcheck disable=SC2016 - repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c '[[ $(mailq) == *"Mail queue is empty"* ]]' + # shellcheck disable=SC2016 + repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c '[[ $(mailq) == *"Mail queue is empty"* ]]' } # Common defaults appropriate for most tests, override vars in each test when necessary. diff --git a/test/tests.bats b/test/tests.bats index 0766e2a3..453ae85f 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -9,6 +9,7 @@ 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" + docker run --rm -d --name mail \ -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ @@ -1033,60 +1034,60 @@ EOF # alias @test "checking setup.sh: setup.sh alias list" { - run ./setup.sh alias list + run ./setup.sh -c mail alias list assert_success assert_output --partial "alias1@localhost.localdomain user1@localhost.localdomain" assert_output --partial "@localdomain2.com user1@localhost.localdomain" } @test "checking setup.sh: setup.sh alias add" { - ./setup.sh alias add alias@example.com target1@forward.com - ./setup.sh alias add alias@example.com target2@forward.com - ./setup.sh alias add alias2@example.org target3@forward.com + ./setup.sh -c mail alias add alias@example.com target1@forward.com + ./setup.sh -c mail alias add alias@example.com target2@forward.com + ./setup.sh -c mail alias add alias2@example.org target3@forward.com sleep 5 run grep "alias@example.com target1@forward.com,target2@forward.com" "$(private_config_path mail)/postfix-virtual.cf" assert_success } @test "checking setup.sh: setup.sh alias del" { - ./setup.sh alias del alias@example.com target1@forward.com + ./setup.sh -c mail alias del alias@example.com target1@forward.com run grep "target1@forward.com" "$(private_config_path mail)/postfix-virtual.cf" assert_failure run grep "target2@forward.com" "$(private_config_path mail)/postfix-virtual.cf" assert_output "alias@example.com target2@forward.com" - ./setup.sh alias del alias@example.org target2@forward.com + ./setup.sh -c mail alias del alias@example.org target2@forward.com run grep "alias@example.org" "$(private_config_path mail)/postfix-virtual.cf" assert_failure run grep "alias2@example.org" "$(private_config_path mail)/postfix-virtual.cf" assert_success - ./setup.sh alias del alias2@example.org target3@forward.com + ./setup.sh -c mail alias del alias2@example.org target3@forward.com run grep "alias2@example.org" "$(private_config_path mail)/postfix-virtual.cf" assert_failure } # quota @test "checking setup.sh: setup.sh setquota" { - run ./setup.sh email add quota_user@example.com test_password - run ./setup.sh email add quota_user2@example.com test_password + run ./setup.sh -c mail email add quota_user@example.com test_password + run ./setup.sh -c mail email add quota_user2@example.com test_password - run ./setup.sh quota set quota_user@example.com 12M + run ./setup.sh -c mail quota set quota_user@example.com 12M assert_success - run ./setup.sh quota set 51M quota_user@example.com + run ./setup.sh -c mail quota set 51M quota_user@example.com assert_failure - run ./setup.sh quota set unknown@domain.com 150M + run ./setup.sh -c mail quota set unknown@domain.com 150M assert_failure - run ./setup.sh quota set quota_user2 51M + run ./setup.sh -c mail quota set quota_user2 51M assert_failure run /bin/sh -c 'cat ./test/duplicate_configs/mail/dovecot-quotas.cf | grep -E "^quota_user@example.com\:12M\$" | wc -l | grep 1' assert_success - run ./setup.sh quota set quota_user@example.com 26M + run ./setup.sh -c mail quota set quota_user@example.com 26M assert_success run /bin/sh -c 'cat ./test/duplicate_configs/mail/dovecot-quotas.cf | grep -E "^quota_user@example.com\:26M\$" | wc -l | grep 1' assert_success @@ -1096,20 +1097,20 @@ EOF } @test "checking setup.sh: setup.sh delquota" { - run ./setup.sh email add quota_user@example.com test_password - run ./setup.sh email add quota_user2@example.com test_password + run ./setup.sh -c mail email add quota_user@example.com test_password + run ./setup.sh -c mail email add quota_user2@example.com test_password - run ./setup.sh quota set quota_user@example.com 12M + run ./setup.sh -c mail quota set quota_user@example.com 12M assert_success run /bin/sh -c 'cat ./test/duplicate_configs/mail/dovecot-quotas.cf | grep -E "^quota_user@example.com\:12M\$" | wc -l | grep 1' assert_success - run ./setup.sh quota del unknown@domain.com + run ./setup.sh -c mail quota del unknown@domain.com assert_failure run /bin/sh -c 'cat ./test/duplicate_configs/mail/dovecot-quotas.cf | grep -E "^quota_user@example.com\:12M\$" | wc -l | grep 1' assert_success - run ./setup.sh quota del quota_user@example.com + run ./setup.sh -c mail quota del quota_user@example.com assert_success run grep "quota_user@example.com" ./test/duplicate_configs/mail/dovecot-quotas.cf assert_failure @@ -1135,10 +1136,10 @@ EOF } @test "checking setup.sh: setup.sh relay add-domain" { - ./setup.sh relay add-domain example1.org smtp.relay1.com 2525 - ./setup.sh relay add-domain example2.org smtp.relay2.com - ./setup.sh relay add-domain example3.org smtp.relay3.com 2525 - ./setup.sh relay add-domain example3.org smtp.relay.com 587 + ./setup.sh -c mail relay add-domain example1.org smtp.relay1.com 2525 + ./setup.sh -c mail relay add-domain example2.org smtp.relay2.com + ./setup.sh -c mail relay add-domain example3.org smtp.relay3.com 2525 + ./setup.sh -c mail relay add-domain example3.org smtp.relay.com 587 # check adding run /bin/sh -c "cat $(private_config_path mail)/postfix-relaymap.cf | grep -e \"^@example1.org\s\+\[smtp.relay1.com\]:2525\" | wc -l | grep 1" @@ -1152,9 +1153,9 @@ EOF } @test "checking setup.sh: setup.sh relay add-auth" { - ./setup.sh relay add-auth example.org smtp_user smtp_pass - ./setup.sh relay add-auth example2.org smtp_user2 smtp_pass2 - ./setup.sh relay add-auth example2.org smtp_user2 smtp_pass_new + ./setup.sh -c mail relay add-auth example.org smtp_user smtp_pass + ./setup.sh -c mail relay add-auth example2.org smtp_user2 smtp_pass2 + ./setup.sh -c mail relay add-auth example2.org smtp_user2 smtp_pass_new # test adding run /bin/sh -c "cat $(private_config_path mail)/postfix-sasl-password.cf | grep -e \"^@example.org\s\+smtp_user:smtp_pass\" | wc -l | grep 1" @@ -1165,7 +1166,7 @@ EOF } @test "checking setup.sh: setup.sh relay exclude-domain" { - ./setup.sh relay exclude-domain example.org + ./setup.sh -c mail relay exclude-domain example.org run /bin/sh -c "cat $(private_config_path mail)/postfix-relaymap.cf | grep -e \"^@example.org\s*$\" | wc -l | grep 1" assert_success