Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ WAIT_TIMEOUT = 300
TEST_DNS ?=
DNS_FLAG = $(if $(TEST_DNS),--dns $(TEST_DNS),)

# Log which resolver the container uses and what dbl.spamhaus.org answers it,
# before any mail is sent (test/share/tests/dnsbl-probe.sh). reverse and ldap2
# inherit the host resolver, so a run that fails on a refused resolver looks
# like a delivery bug unless this is in the log, and a run that passed says
# nothing about whether the blocklist answered at all.
define dnsbl_probe
-@docker exec $(1) sh /tmp/tests/dnsbl-probe.sh
endef

all: build-no-cache default reverse ldap ldap2 sieve ecdsa traefik_acmev1 traefik_acmev2 clean
no-build: default reverse ldap ldap2 sieve ecdsa traefik_acmev1 traefik_acmev2 clean
default: init_default fixtures_default run_default stop_default
Expand Down Expand Up @@ -232,6 +241,7 @@ fixtures_ldap2:
docker exec mailserver_ldap2 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 11332 ; do sleep 1 ; done" # rspamd
docker exec mailserver_ldap2 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 993 ; do sleep 1 ; done" # dovecot imaps
docker exec mailserver_ldap2 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done" # submission
$(call dnsbl_probe,mailserver_ldap2)
docker exec mailserver_ldap2 /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-existing-user.txt"
docker exec mailserver_ldap2 /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-valid-user-subaddress.txt"
docker exec mailserver_ldap2 /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-non-existing-user.txt"
Expand Down Expand Up @@ -321,6 +331,7 @@ fixtures_reverse:
docker exec mailserver_reverse /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 11332 ; do sleep 1 ; done" # rspamd
docker exec mailserver_reverse /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 993 ; do sleep 1 ; done" # dovecot imaps
docker exec mailserver_reverse /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done" # submission
$(call dnsbl_probe,mailserver_reverse)
docker exec mailserver_reverse /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-existing-user.txt"
docker exec mailserver_reverse /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-valid-user-subaddress-with-default-separator.txt"
docker exec mailserver_reverse /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-non-existing-user.txt"
Expand Down Expand Up @@ -423,6 +434,7 @@ fixtures_default:
# Wait for rspamd to start (default)
docker exec mailserver_default /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 11332 ; do sleep 1 ; done"

$(call dnsbl_probe,mailserver_default)
docker exec mailserver_default /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-existing-user.txt"
docker exec mailserver_default /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-existing-user-spam-learning.txt"
docker exec mailserver_default /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-valid-user-subaddress.txt"
Expand Down
15 changes: 11 additions & 4 deletions rootfs/etc/postfix/main.cf
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,18 @@ smtpd_relay_restrictions=
# * reject_non_fqdn_sender : Reject when the MAIL FROM address is not in fully-qualified domain form
# * reject_unknown_sender_domain : Reject when the MAIL FROM domain has no DNS MX, no DNS A record or a malformed MX record
# * reject_sender_login_mismatch: Reject when the client is not (SASL) logged in as that MAIL FROM address owner or when the client is (SASL) logged in, but the client login name doesn't own the MAIL FROM address
# * reject_rhsbl_sender : Reject when the MAIL FROM domain is blacklisted in dbl.spamhaus.org
# * reject_rhsbl_sender : Reject when the MAIL FROM domain is blacklisted in dbl.spamhaus.org.
# 127.0.1.[2..99] are the listing codes; 127.255.255.0/24 reports a problem with the query
# itself (refused resolver, too many queries) and must not be read as a listing, so it is
# only logged. Without the code filter postfix matches any answer and rejects every sender.

smtpd_sender_restrictions=
reject_non_fqdn_sender,
reject_unknown_sender_domain,
reject_sender_login_mismatch,
reject_unlisted_sender,
reject_rhsbl_sender dbl.spamhaus.org,
reject_rhsbl_sender dbl.spamhaus.org=127.0.1.[2..99],
warn_if_reject reject_rhsbl_sender dbl.spamhaus.org=127.255.255.[1..255],
check_sender_access hash:/etc/postfix/sender_access

##
Expand All @@ -201,7 +205,9 @@ smtpd_sender_restrictions=
# * reject_unknown_recipient_domain : Reject when the RCPT TO domain has no DNS MX or no DNS A record or a malformed MX record
# * reject_non_fqdn_recipient : Reject when the RCPT TO address is not in fully-qualified domain form
# * reject_unlisted_recipient : Reject when the RCPT TO address is not listed in the list of valid recipients for its domain
# * reject_rbl_client : Reject connections from IP addresses blacklisted in zen.spamhaus.org
# * reject_rbl_client : Reject connections from IP addresses blacklisted in zen.spamhaus.org.
# 127.0.0.[2..11] are the listing codes; the 127.255.255.0/24 query errors are only logged,
# as for dbl.spamhaus.org above.

smtpd_recipient_restrictions=
permit_mynetworks,
Expand All @@ -210,7 +216,8 @@ smtpd_recipient_restrictions=
reject_non_fqdn_recipient,
reject_unlisted_recipient,
check_client_access hash:/etc/postfix/rbl_override,
reject_rbl_client zen.spamhaus.org
reject_rbl_client zen.spamhaus.org=127.0.0.[2..11],
warn_if_reject reject_rbl_client zen.spamhaus.org=127.255.255.[1..255]

##
# Restrictions that the Postfix SMTP server applies in the context of a client HELO command
Expand Down
30 changes: 30 additions & 0 deletions test/share/tests/dnsbl-probe.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh
# Print what this container's resolver answers for dbl.spamhaus.org, and which
# resolver that is. Runs inside the mailserver container (needs dig).
#
# dbltest.com -> 127.0.1.2 the permanent test listing: the blocklist works
# dbltest.com -> 127.255.255.x this resolver is refused: every sender gets rejected
# dbltest.com -> (nothing) answered nothing: the blocklist is inert
# gmail.com -> (nothing) the fixtures' sender domain, the name postfix looks up
# example.com -> (nothing) expected, it is not listed
#
# The resolver line gives the nameserver the container is configured with and
# the resolver's egress address as seen by two whoami services, because a
# healthy Spamhaus answer does not name it (only a refusal does). ttl is the
# remaining TTL: a full value is a fresh answer, a lower one came from cache.

q() { dig +time=3 +tries=1 "$@" 2>/dev/null | grep -v '^;'; }

ns=$(awk '/^nameserver/{print $2; exit}' /etc/resolv.conf)
ak=$(q +short A whoami.akamai.net | head -1)
gg=$(q +short TXT o-o.myaddr.l.google.com | grep -v edns0 | head -1 | tr -d '"')
printf '[dnsbl] resolver: nameserver=%s egress-seen-by-akamai=%s egress-seen-by-google=%s\n' "${ns:--}" "${ak:--}" "${gg:--}"

for name in dbltest.com.dbl.spamhaus.org gmail.com.dbl.spamhaus.org example.com.dbl.spamhaus.org; do
out=$(dig +time=3 +tries=1 +noall +comments +answer +authority A "$name" 2>&1)
st=$(printf '%s\n' "$out" | sed -n 's/.*status: \([A-Z]*\).*/\1/p' | head -1); [ -z "$st" ] && st=unreachable
a=$(printf '%s\n' "$out" | grep '[[:space:]]A[[:space:]]' | sed 's/.*[[:space:]]//' | tr '\n' ',' | sed 's/,$//'); [ -z "$a" ] && a=-
ttl=$(printf '%s\n' "$out" | grep -v '^;' | awk 'NF>=5{print $2; exit}'); [ -z "$ttl" ] && ttl=-
t=$(q +short TXT "$name" | grep '^"' | head -1); [ -z "$t" ] && t=-
printf '[dnsbl] %-32s status=%-11s A=%-16s ttl=%-5s %s\n' "$name" "$st" "$a" "$ttl" "$t"
done
Loading