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