diff --git a/Makefile b/Makefile index 75afc05b..b2b49ce9 100644 --- a/Makefile +++ b/Makefile @@ -332,6 +332,7 @@ init_reverse: init_redis init_postgres -v "`pwd`/test/share/passwd":/tmp/passwd \ -v "`pwd`/test/share/ssl/rsa":/var/mail/ssl \ -v "`pwd`/test/share/sieve/custom.sieve":/var/mail/sieve/custom.sieve \ + -v "`pwd`/test/share/postfix/rbl_override":/etc/postfix/rbl_override \ -v "`pwd`/test/share/letsencrypt":/etc/letsencrypt \ -t $(NAME) fixtures_reverse: @@ -556,7 +557,8 @@ fixtures_default: @timeout $(WAIT_TIMEOUT) sh -c 'until [ -z "$$(docker exec mailserver_default find /var/spool/postfix/incoming /var/spool/postfix/active /var/spool/postfix/maildrop -type f 2>/dev/null)" ]; do sleep 2; done' \ || { echo "TIMEOUT: postfix queue did not drain"; docker exec mailserver_default postqueue -p; exit 1; } run_default: - ./test/bats/bin/bats test/default.bats + # Some tests start their own container, from this image + MAILSERVER_IMAGE=$(NAME) ./test/bats/bin/bats test/default.bats stop_default: -docker rm -fv \ mailserver_default || true diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index f7977685..589bf409 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -313,21 +313,25 @@ fi # # Initialize rbl_override configuration # -if [ -d "/etc/postifx/rbl_override" ]; then - echo "[ERROR] The `/etc/postfix/rbl_override` is a folder instead of a file. Please fix it." - exit 1; -fi - -if [ ! -f "/etc/postfix/rbl_override" ]; then +if [ -d "/etc/postfix/rbl_override" ]; then + echo "[ERROR] /etc/postfix/rbl_override is a folder instead of a file. Please fix it." + touch /etc/setup-error +elif [ ! -f "/etc/postfix/rbl_override" ]; then touch /etc/postfix/rbl_override echo "[INFO] Created default (empty) rbl_override file" else echo "[INFO] /etc/postfix/rbl_override file found" fi +# -o keeps root privileges. Without it postmap runs as the owner of a non-root +# file, typically a bind mount, and cannot write the .db into /etc/postfix. if [ -f "/etc/postfix/rbl_override" ]; then - postmap /etc/postfix/rbl_override - echo "[INFO] RBL Override initialized" + if postmap -o /etc/postfix/rbl_override; then + echo "[INFO] RBL Override initialized" + else + echo "[ERROR] Could not build the rbl_override map, see the postmap error above." + touch /etc/setup-error + fi fi # DOVECOT TUNING diff --git a/test/default.bats b/test/default.bats index 5056f974..c796ed04 100644 --- a/test/default.bats +++ b/test/default.bats @@ -45,6 +45,50 @@ load 'test_helper/bats-assert/load' assert_failure } +# A bind mount whose host file is missing makes Docker create a directory in its +# place, and Postfix cannot use that as a map. The timeout turns a container that +# starts anyway into a failed test rather than a hung suite. +@test "checking system: startup stops when rbl_override is a directory" { + run timeout 180 docker run --rm \ + --name mailserver_default_rbl_dir \ + --link mariadb:mariadb \ + --link redis:redis \ + -e DBPASS=testpasswd \ + -e RSPAMD_PASSWORD=testpasswd \ + -e VMAILUID=`id -u` \ + -e VMAILGID=`id -g` \ + -e DISABLE_CLAMAV=true \ + -e TESTING=true \ + --tmpfs /etc/postfix/rbl_override \ + -h mail.domain.tld \ + "${MAILSERVER_IMAGE:-mailserver2/mailserver:testing}" + docker rm -f mailserver_default_rbl_dir > /dev/null 2>&1 || true + assert_failure 1 + assert_output --partial "[ERROR] /etc/postfix/rbl_override is a folder instead of a file" +} + +# Without the map, Postfix answers RCPT from clients outside mynetworks with +# 451 4.3.5, so a failed postmap must stop startup. A directory mounted where +# the .db goes makes postmap fail. +@test "checking system: startup stops when the rbl_override map cannot be built" { + run timeout 180 docker run --rm \ + --name mailserver_default_rbl_db \ + --link mariadb:mariadb \ + --link redis:redis \ + -e DBPASS=testpasswd \ + -e RSPAMD_PASSWORD=testpasswd \ + -e VMAILUID=`id -u` \ + -e VMAILGID=`id -g` \ + -e DISABLE_CLAMAV=true \ + -e TESTING=true \ + --tmpfs /etc/postfix/rbl_override.db \ + -h mail.domain.tld \ + "${MAILSERVER_IMAGE:-mailserver2/mailserver:testing}" + docker rm -f mailserver_default_rbl_db > /dev/null 2>&1 || true + assert_failure 1 + assert_output --partial "[ERROR] Could not build the rbl_override map" +} + # # processes (default configuration) # @@ -522,6 +566,11 @@ load 'test_helper/bats-assert/load' assert_output "/tmp" } +@test "checking postfix: rbl_override map is built when none is provided" { + run docker exec mailserver_default test -f /etc/postfix/rbl_override.db + assert_success +} + @test "checking postfix: headers cleanup" { run docker exec mailserver_default /bin/sh -c "grep -i 'replace: header Received' /var/log/mail.log | wc -l" assert_success diff --git a/test/reverse.bats b/test/reverse.bats index 94c43ada..cf27b601 100644 --- a/test/reverse.bats +++ b/test/reverse.bats @@ -424,6 +424,20 @@ load 'test_helper/bats-assert/load' assert_success } +# By default postmap drops root privileges and runs as the source file's owner, +# who cannot write the .db into /etc/postfix. The fixture is bind-mounted from +# the checkout, so it belongs to the user running the suite; the first check +# makes sure that is not root, or this test could not fail. +@test "checking postfix: rbl_override map is built from a file not owned by root" { + run docker exec mailserver_reverse stat -c %u /etc/postfix/rbl_override + assert_success + refute_output "0" + + run docker exec mailserver_reverse postmap -q 192.0.2.1 hash:/etc/postfix/rbl_override + assert_success + assert_output "OK" +} + # # dovecot # diff --git a/test/share/postfix/rbl_override b/test/share/postfix/rbl_override new file mode 100644 index 00000000..b8b876e2 --- /dev/null +++ b/test/share/postfix/rbl_override @@ -0,0 +1 @@ +192.0.2.1 OK