1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2026-03-05 15:57:23 +01:00

Add tests for dmarc reject and spf softfail

This commit is contained in:
downtownallday
2020-12-21 08:46:12 -05:00
parent 4cc672e852
commit f5521b45b5
3 changed files with 243 additions and 49 deletions

View File

@@ -36,8 +36,11 @@ ensure_root_user() {
dovecot_mailbox_home() {
local email="$1"
echo -n "${STORAGE_ROOT}/mail/mailboxes/"
awk -F@ '{print $2"/"$1}' <<< "$email"
local mailbox="${2:-INBOX}"
local path
/usr/bin/doveadm mailbox path -u "$email" "$mailbox"
#echo -n "${STORAGE_ROOT}/mail/mailboxes/"
#awk -F@ '{print $2"/"$1}' <<< "$email"
}
@@ -60,16 +63,24 @@ start_log_capture() {
start_mail_capture() {
local email="$1"
local newdir="$(dovecot_mailbox_home "$email")/new"
record "[Start mail capture $email]"
DOVECOT_CAPTURE_USER="$email"
DOVECOT_CAPTURE_FILECOUNT=0
if [ -e "$newdir" ]; then
DOVECOT_CAPTURE_FILECOUNT=$(ls "$newdir" 2>>$TEST_OF | wc -l)
[ $? -ne 0 ] && die "Error accessing mailbox of $email"
fi
record "mailbox: $(dirname $newdir)"
record "mailbox has $DOVECOT_CAPTURE_FILECOUNT files"
DOVECOT_CAPTURE_FILECOUNT=()
DOVECOT_CAPTURE_MAILBOXES=(INBOX Spam)
for mailbox in ${DOVECOT_CAPTURE_MAILBOXES[@]}; do
local mbhome
mbhome="$(dovecot_mailbox_home "$email" "$mailbox" 2>>$TEST_OF)"
[ $? -ne 0 ] && die "Error accessing $mailbox of $email"
local newdir="$mbhome/new"
local count=0
if [ -e "$newdir" ]; then
count=$(ls "$newdir" 2>>$TEST_OF | wc -l)
[ $? -ne 0 ] && die "Error accessing mailbox of $email"
fi
DOVECOT_CAPTURE_FILECOUNT+=($count)
record "$mailbox location: $mbhome"
record "$mailbox has $count files"
done
}
dump_capture_logs() {
@@ -299,14 +310,23 @@ wait_mail() {
}
get_captured_mail_files() {
local newdir="$(dovecot_mailbox_home "$DOVECOT_CAPTURE_USER")/new"
local count
let count="$DOVECOT_CAPTURE_FILECOUNT + 1"
[ ! -e "$newdir" ] && return 0
# output absolute path names
local file
for file in $(ls "$newdir" 2>>$TEST_OF | tail --lines=+${count}); do
echo "$newdir/$file"
local idx=0
while [ $idx -lt ${#DOVECOT_CAPTURE_MAILBOXES[*]} ]; do
local mailbox=${DOVECOT_CAPTURE_MAILBOXES[$idx]}
local filecount=${DOVECOT_CAPTURE_FILECOUNT[$idx]}
local mbhome
mbhome="$(dovecot_mailbox_home "$DOVECOT_CAPTURE_USER" "$mailbox" 2>>$TEST_OF)"
[ $? -ne 0 ] && die "Error accessing mailbox of $email"
local newdir="$mbhome/new"
[ ! -e "$newdir" ] && return 0
local count
let count="$filecount + 1"
# output absolute path names
local file
for file in $(ls "$newdir" 2>>$TEST_OF | tail --lines=+${count}); do
echo "$newdir/$file"
done
let idx+=1
done
}

View File

@@ -31,7 +31,26 @@ _test_greylisting_x() {
}
postgrey_reset() {
postgrey_whitelist_recipents() {
local wl="/etc/postgrey/whitelist_recipients.local"
rm -f "$wl"
local recipient
for recipient; do
echo "$recipient" >> "$wl" || \
die "Could not add postgrey whitelist recipient to $wl"
done
systemctl reload postgrey
}
postgrey_reset_whitelists() {
local wl="/etc/postgrey/whitelist_recipients.local"
rm -f "$wl"
systemctl reload postgrey
}
postgrey_reset_state() {
# when postgrey receives a message for processing that is suspect,
# it will:
# 1. initally reject it
@@ -52,6 +71,7 @@ postgrey_reset() {
systemctl start postgrey >>$TEST_OF 2>&1
die "unable to remove the postgrey database files"
fi
systemctl start postgrey >>$TEST_OF 2>&1 || die "unble to start postgrey"
}
@@ -61,15 +81,15 @@ test_greylisting() {
test_start "greylisting"
# reset postgrey's database to start the cycle over
postgrey_reset
postgrey_reset_state
# create standard user alice
local alice="alice@somedomain.com"
create_user "$alice" "alice"
# IMPORTANT: bob's domain must be from one that has no SPF record
# in DNS. At the time of creation of this script, yahoo.com did
# not...
# in DNS. At the time of creation of this script, yahoo.com's
# is set to "?all" ("neutral" or "no policy statement")
local bob="bob@yahoo.com"
# send to alice anonymously from bob
@@ -95,15 +115,18 @@ test_relay_prohibited() {
}
test_spf() {
# test mail rejection due to SPF policy of FROM address
test_start "spf"
test_spf_fail() {
# test mail rejection due to SPF policy of envelope FROM address
test_start "spf-fail"
# create standard user alice
local alice="alice@somedomain.com"
create_user "$alice" "alice"
# who we will impersonate
#
# note: we've configured policyd-spf to fail instead of softfail
# on google.com
local from="test@google.com"
local domain=$(awk -F@ '{print $2}' <<<"$from")
@@ -125,6 +148,135 @@ test_spf() {
test_end
}
test_spf_softfail() {
# When policyd-spf-postfix determines a message's SPF disposition
# is "Softfail", it attaches a "Received-SPF: Softfail" header
# (see RFC 7208) and allows the message to move along the postfix
# delivery chain.
#
# PR #1836 sets custom spam assassin rules that cause spam
# assassin to add a score of 5.0 for Softfail messages (see
# setup/dkim.sh and setup/spamassassin.sh)
#
# This test checks that a softfail message is properly scored.
local SPF_SOFTFAIL_SCORE="5.0"
test_start "spf-softfail"
# create user alice
local alice="alice@somedomain.com"
local alice_pw="123alice"
create_user "$alice" "$alice_pw"
# who we will impersonate
#
# important: the MAIL FROM address we choose here must currently
# have dns spf configuration set to softfail (eg: ~all)
#
local from="test@guest.com"
local domain=$(awk -F@ '{print $2}' <<<"$from")
# alice must be whitelisted to avoid greylisting
postgrey_whitelist_recipents "$alice"
# send to alice anonymously from imposter
start_log_capture
start_mail_capture "$alice"
record "[Test SPF for $domain FROM $from TO $alice]"
local output
local subject="$(generate_uuid)"
output="$($PYMAIL -no-delete -subj "$subject" -f $from -to $alice '' $PRIVATE_IP '' '' 2>&1)"
if assert_python_success $? "$output"; then
if ! wait_mail; then
test_failure "Timeout waiting for mail"
else
record_captured_mail
local files=( $(get_captured_mail_files) )
local score
score=$(grep -F "SPF_SOFTFAIL" "$files" | grep -F '*' | awk '{print $2}')
floor_score=$(awk -F. '{print $1}' <<<"$score")
floor_expected=$(awk -F. '{print $1}' <<<"$SPF_SOFTFAIL_SCORE")
if [ -z "$score" ]; then
test_failure "No spam score for SPF_SOFTFAIL"
elif [ $floor_score -ne $floor_expected ]; then
test_failure "Got score $score, but exptected $SPF_SOFTFAIL_SCORE"
fi
fi
# clean up: delete the delivered mail
record "[delete delivered message]"
output="$($PYMAIL -no-send -subj $subject -timeout 1 $PRIMARY_HOSTNAME $alice "$alice_pw" 2>&1)"
record "$output"
fi
check_logs
# clean up
postgrey_reset_whitelists
delete_user "$alice"
test_end
}
test_dmarc_reject() {
# PR #1836 sets custom spam assassin rules that cause spam
# assassin to add a score of 10.0 for dmarc p=reject messages (see
# setup/dkim.sh and setup/spamassassin.sh)
#
# This test checks that a p=reject message is properly scored.
local DMARC_FAIL_REJECT_SCORE="10.0"
test_start "dmarc-reject"
# create user alice - must be a domain that does not result in SPF Fail
local alice="alice@guest.com"
local alice_pw="alice123"
create_user "$alice" "$alice_pw"
# who we will impersonate: their domain (_dmarc.domain.com dns TXT
# record), must have a dmarc policy with p=reject
local header_from="test@google.com"
# alice must be whitelisted to avoid greylisting
postgrey_whitelist_recipents "$alice"
# send to alice from alice with header From: imposter
start_log_capture
start_mail_capture "$alice"
record "[Test dmarc reject TO $alice, From: $header_from]"
local subject="$(generate_uuid)"
local output
output="$($PYMAIL -smptd -subj $subject -no-delete -f $alice -hfrom $header_from -to $alice '' $PRIVATE_IP 2>&1)"
if assert_python_success $? "$output"; then
if ! wait_mail; then
test_failure "Timeout waiting for mail"
else
record_captured_mail
local files=( $(get_captured_mail_files) )
local score
score=$(grep -F "DMARC_FAIL_REJECT" "$files" | grep -F '*' | awk '{print $2}')
floor_score=$(awk -F. '{print $1}' <<<"$score")
floor_expected=$(awk -F. '{print $1}' <<<"$DMARC_FAIL_REJECT_SCORE")
if [ -z "$score" ]; then
test_failure "No spam score for DMARC_FAIL_REJECT"
elif [ $floor_score -ne $floor_expected ]; then
test_failure "Got score $score, but exptected $DMARC_FAIL_REJECT_SCORE"
fi
fi
# clean up: delete the delivered mail
record "[delete delivered message]"
output="$($PYMAIL -no-send -subj $subject -timeout 1 $PRIMARY_HOSTNAME $alice "$alice_pw" 2>&1)"
record "$output"
fi
check_logs
# clean up
postgrey_reset_whitelists
delete_user "$alice"
test_end
}
test_mailbox_pipe() {
# postfix allows piped commands in aliases for local processing,
@@ -193,7 +345,9 @@ suite_start "mail-access" ensure_root_user
test_greylisting
test_relay_prohibited
test_spf
test_spf_fail
test_spf_softfail
test_dmarc_reject
test_mailbox_pipe
suite_end