tests: Use swaks instead of nc for sending mail (#3732)

See associated `CHANGELOG.md` entry for details.

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
This commit is contained in:
Georg Lauterbach 2024-01-03 01:17:54 +01:00 committed by GitHub
parent 0889b0ff06
commit 9e81517fe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
119 changed files with 355 additions and 455 deletions

View file

@ -225,9 +225,12 @@ function teardown_file() { _default_teardown ; }
sleep 10
# send some big emails
_send_email 'email-templates/quota-exceeded' '0.0.0.0 25'
_send_email 'email-templates/quota-exceeded' '0.0.0.0 25'
_send_email 'email-templates/quota-exceeded' '0.0.0.0 25'
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded'
assert_success
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded'
assert_success
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded'
assert_success
# check for quota warn message existence
run _repeat_until_success_or_timeout 20 _exec_in_container grep -R 'Subject: quota warning' /var/mail/otherdomain.tld/quotauser/new/
assert_success

View file

@ -26,9 +26,11 @@ function setup_file() {
_wait_for_smtp_port_in_container
# Single mail sent from 'spam@spam.com' that is handled by User (relocate) and Global (copy) sieves for user1:
_send_email 'email-templates/sieve-spam-folder'
_send_email --data 'sieve/spam-folder'
assert_success
# Mail for user2 triggers the sieve-pipe:
_send_email 'email-templates/sieve-pipe'
_send_email --to 'user2@otherdomain.tld' --data 'sieve/pipe'
assert_success
_wait_for_empty_mail_queue_in_container
}

View file

@ -26,7 +26,8 @@ function teardown() { _default_teardown ; }
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_send_email 'email-templates/existing-user1'
_send_email --data 'existing/user1'
assert_success
_wait_for_empty_mail_queue_in_container
# Mail received should be stored as `u.1` (one file per message)
@ -47,7 +48,8 @@ function teardown() { _default_teardown ; }
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_send_email 'email-templates/existing-user1'
_send_email --data 'existing/user1'
assert_success
_wait_for_empty_mail_queue_in_container
# Mail received should be stored in `m.1` (1 or more messages)

View file

@ -14,7 +14,8 @@ function setup_file() {
function teardown_file() { _default_teardown ; }
@test 'normal delivery works' {
_send_email 'email-templates/existing-user1'
_send_email --data 'existing/user1'
assert_success
_count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new 1
}
@ -26,7 +27,7 @@ function teardown_file() { _default_teardown ; }
}
@test "(IMAP) special-use folders should be created when necessary" {
_send_email 'nc_templates/imap_special_use_folders' '-w 8 0.0.0.0 143'
_nc_wrapper 'nc/imap_special_use_folders' '-w 8 0.0.0.0 143'
assert_output --partial 'Drafts'
assert_output --partial 'Junk'
assert_output --partial 'Trash'

View file

@ -25,34 +25,35 @@ function setup_file() {
_wait_for_service postfix
_wait_for_smtp_port_in_container
_send_email 'email-templates/amavis-virus'
_send_email --from 'virus@external.tld' --data 'amavis/virus'
assert_success
_wait_for_empty_mail_queue_in_container
}
function teardown_file() { _default_teardown ; }
@test "log files exist at /var/log/mail directory" {
@test 'log files exist at /var/log/mail directory' {
_run_in_container_bash "ls -1 /var/log/mail/ | grep -E 'clamav|freshclam|mail.log' | wc -l"
assert_success
assert_output 3
}
@test "should be identified by Amavis" {
@test 'should be identified by Amavis' {
_run_in_container grep -i 'Found secondary av scanner ClamAV-clamscan' /var/log/mail/mail.log
assert_success
}
@test "freshclam cron is enabled" {
@test 'freshclam cron is enabled' {
_run_in_container_bash "grep '/usr/bin/freshclam' -r /etc/cron.d"
assert_success
}
@test "env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly" {
@test 'env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly' {
_run_in_container grep -q '^MaxFileSize 30M$' /etc/clamav/clamd.conf
assert_success
}
@test "rejects virus" {
@test 'rejects virus' {
_run_in_container_bash "grep 'Blocked INFECTED' /var/log/mail/mail.log | grep '<virus@external.tld> -> <user1@localhost.localdomain>'"
assert_success
}

View file

@ -12,12 +12,14 @@ function setup_file() {
--env ENABLE_CLAMAV=0
--env ENABLE_SPAMASSASSIN=0
--env AMAVIS_LOGLEVEL=2
--env PERMIT_DOCKER=container
)
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_send_email 'email-templates/existing-user1'
_send_email --data 'existing/user1'
assert_success
_wait_for_empty_mail_queue_in_container
}

View file

@ -73,8 +73,17 @@ function teardown_file() {
@test "ban ip on multiple failed login" {
CONTAINER1_IP=$(_get_container_ip "${CONTAINER1_NAME}")
# Trigger a ban by failing to login twice:
CONTAINER_NAME=${CONTAINER2_NAME} _send_email 'auth/smtp-auth-login-wrong' "${CONTAINER1_IP} 465"
CONTAINER_NAME=${CONTAINER2_NAME} _send_email 'auth/smtp-auth-login-wrong' "${CONTAINER1_IP} 465"
for _ in {1..2}; do
CONTAINER_NAME=${CONTAINER2_NAME} _send_email \
--server "${CONTAINER1_IP}" \
--port 465 \
--auth PLAIN \
--auth-user user1@localhost.localdomain \
--auth-password wrongpassword
assert_failure
assert_output --partial 'authentication failed'
assert_output --partial 'No authentication type succeeded'
done
# Checking that CONTAINER2_IP is banned in "${CONTAINER1_NAME}"
CONTAINER2_IP=$(_get_container_ip "${CONTAINER2_NAME}")

View file

@ -51,17 +51,15 @@ function teardown_file() { _default_teardown ; }
_reload_postfix
# Send test mail (it should fail to deliver):
_send_test_mail '/tmp/docker-mailserver-test/email-templates/postgrey.txt' '25'
_send_email --from 'user@external.tld' --port 25 --data 'postgrey'
assert_failure
assert_output --partial 'Recipient address rejected: Delayed by Postgrey'
# Confirm mail was greylisted:
_should_have_log_entry \
'action=greylist' \
'reason=new' \
'client_address=127.0.0.1/32, sender=user@external.tld, recipient=user1@localhost.localdomain'
_repeat_until_success_or_timeout 10 _run_in_container grep \
'Recipient address rejected: Delayed by Postgrey' \
/var/log/mail/mail.log
}
# NOTE: This test case depends on the previous one
@ -69,7 +67,8 @@ function teardown_file() { _default_teardown ; }
# Wait until `$POSTGREY_DELAY` seconds pass before trying again:
sleep 3
# Retry delivering test mail (it should be trusted this time):
_send_test_mail '/tmp/docker-mailserver-test/email-templates/postgrey.txt' '25'
_send_email --from 'user@external.tld' --port 25 --data 'postgrey'
assert_success
# Confirm postgrey permitted delivery (triplet is now trusted):
_should_have_log_entry \
@ -78,8 +77,9 @@ function teardown_file() { _default_teardown ; }
'client_address=127.0.0.1/32, sender=user@external.tld, recipient=user1@localhost.localdomain'
}
# NOTE: These two whitelist tests use `test-files/nc_templates/` instead of `test-files/email-templates`.
# NOTE: These two whitelist tests use `files/nc/` instead of `files/emails`.
# `nc` option `-w 0` terminates the connection after sending the template, it does not wait for a response.
# This is required for port 10023, otherwise the connection never drops.
# - This allows to bypass the SMTP protocol on port 25, and send data directly to Postgrey instead.
# - Appears to be a workaround due to `client_name=localhost` when sent from Postfix.
# - Could send over port 25 if whitelisting `localhost`,
@ -87,7 +87,7 @@ function teardown_file() { _default_teardown ; }
# - It'd also cause the earlier greylist test to fail.
# - TODO: Actually confirm whitelist feature works correctly as these test cases are using a workaround:
@test "should whitelist sender 'user@whitelist.tld'" {
_send_test_mail '/tmp/docker-mailserver-test/nc_templates/postgrey_whitelist.txt' '10023'
_nc_wrapper 'nc/postgrey_whitelist' '-w 0 0.0.0.0 10023'
_should_have_log_entry \
'action=pass' \
@ -96,7 +96,7 @@ function teardown_file() { _default_teardown ; }
}
@test "should whitelist recipient 'user2@otherdomain.tld'" {
_send_test_mail '/tmp/docker-mailserver-test/nc_templates/postgrey_whitelist_recipients.txt' '10023'
_nc_wrapper 'nc/postgrey_whitelist_recipients' '-w 0 0.0.0.0 10023'
_should_have_log_entry \
'action=pass' \
@ -104,21 +104,10 @@ function teardown_file() { _default_teardown ; }
'client_address=127.0.0.1/32, sender=test@nonwhitelist.tld, recipient=user2@otherdomain.tld'
}
function _send_test_mail() {
local MAIL_TEMPLATE=$1
local PORT=${2:-25}
# `-w 0` terminates the connection after sending the template, it does not wait for a response.
# This is required for port 10023, otherwise the connection never drops.
# It could increase the number of seconds to wait for port 25 to allow for asserting a response,
# but that would enforce the delay in tests for port 10023.
_run_in_container_bash "nc -w 0 0.0.0.0 ${PORT} < ${MAIL_TEMPLATE}"
}
function _should_have_log_entry() {
local ACTION=$1
local REASON=$2
local TRIPLET=$3
local ACTION=${1}
local REASON=${2}
local TRIPLET=${3}
# Allow some extra time for logs to update to avoids a false-positive failure:
_run_until_success_or_timeout 10 _exec_in_container grep \

View file

@ -37,46 +37,35 @@ function teardown_file() {
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}"
}
# `POSTSCREEN_ACTION=enforce` (DMS default) should reject delivery with a 550 SMTP reply
# A legitimate mail client should speak SMTP by waiting it's turn, which postscreen defaults enforce (only on port 25)
# https://www.postfix.org/postconf.5.html#postscreen_greet_wait
#
# Use `nc` to send all SMTP commands at once instead (emulate a misbehaving client that should be rejected)
# NOTE: Postscreen only runs on port 25, avoid implicit ports in test methods
@test 'should fail send when talking out of turn' {
CONTAINER_NAME=${CONTAINER2_NAME} _send_email 'email-templates/postscreen' "${CONTAINER1_IP} 25"
CONTAINER_NAME=${CONTAINER2_NAME} _nc_wrapper 'emails/nc_raw/postscreen' "${CONTAINER1_IP} 25"
# Expected postscreen log entry:
assert_output --partial 'Protocol error'
# Expected postscreen log entry:
_run_in_container cat /var/log/mail/mail.log
_run_in_container cat /var/log/mail.log
assert_output --partial 'COMMAND PIPELINING'
assert_output --partial 'DATA without valid RCPT'
}
@test "should successfully pass postscreen and get postfix greeting message (respecting postscreen_greet_wait time)" {
# NOTE: Sometimes fails on first attempt (trying too soon?),
# Instead of a `run` + asserting partial, Using repeat + internal grep match:
_repeat_until_success_or_timeout 10 _should_wait_turn_speaking_smtp \
"${CONTAINER2_NAME}" \
"${CONTAINER1_IP}" \
'/tmp/docker-mailserver-test/email-templates/postscreen.txt' \
'220 mail.example.test ESMTP'
# Configure `send_email()` to send from the mail client container (CONTAINER2_NAME) via ENV override,
# mail is sent to the DMS server container (CONTAINER1_NAME) via `--server` parameter:
CONTAINER_NAME=${CONTAINER2_NAME} _send_email --server "${CONTAINER1_IP}" --port 25 --data 'postscreen'
# NOTE: Cannot assert_success due to sender address not being resolvable.
# TODO: Uncomment when proper resolution of domain names is possible:
# assert_success
# Expected postscreen log entry:
_run_in_container cat /var/log/mail/mail.log
# TODO: Prefer this approach when `_send_email_and_get_id()` can support separate client and server containers:
# local MAIL_ID=$(_send_email_and_get_id --port 25 --data 'postscreen')
# _print_mail_log_for_id "${MAIL_ID}"
# assert_output --partial "stored mail into mailbox 'INBOX'"
_run_in_container cat /var/log/mail.log
assert_output --partial 'PASS NEW'
}
# When postscreen is active, it prevents the usual method of piping a file through nc:
# (Won't work: CONTAINER_NAME=${CLIENT_CONTAINER_NAME} _send_email "${SMTP_TEMPLATE}" "${TARGET_CONTAINER_IP} 25")
# The below workaround respects `postscreen_greet_wait` time (default 6 sec), talking to the mail-server in turn:
# https://www.postfix.org/postconf.5.html#postscreen_greet_wait
function _should_wait_turn_speaking_smtp() {
local CLIENT_CONTAINER_NAME=$1
local TARGET_CONTAINER_IP=$2
local SMTP_TEMPLATE=$3
local EXPECTED=$4
# shellcheck disable=SC2016
local UGLY_WORKAROUND='exec 3<>/dev/tcp/'"${TARGET_CONTAINER_IP}"'/25 && \
while IFS= read -r cmd; do \
head -1 <&3; \
[[ ${cmd} == "EHLO"* ]] && sleep 6; \
echo ${cmd} >&3; \
done < '"${SMTP_TEMPLATE}"
docker exec "${CLIENT_CONTAINER_NAME}" bash -c "${UGLY_WORKAROUND}" | grep "${EXPECTED}"
}

View file

@ -45,10 +45,10 @@ function setup_file() {
# We will send 3 emails: the first one should pass just fine; the second one should
# be rejected due to spam; the third one should be rejected due to a virus.
export MAIL_ID1=$(_send_email_and_get_id 'email-templates/rspamd-pass')
export MAIL_ID2=$(_send_email_and_get_id 'email-templates/rspamd-spam')
export MAIL_ID3=$(_send_email_and_get_id 'email-templates/rspamd-virus')
export MAIL_ID4=$(_send_email_and_get_id 'email-templates/rspamd-spam-header')
export MAIL_ID1=$(_send_email_and_get_id --from 'rspamd-pass@example.test' --data 'rspamd/pass')
export MAIL_ID2=$(_send_email_and_get_id --from 'rspamd-spam@example.test' --data 'rspamd/spam')
export MAIL_ID3=$(_send_email_and_get_id --from 'rspamd-virus@example.test' --data 'rspamd/virus')
export MAIL_ID4=$(_send_email_and_get_id --from 'rspamd-spam-header@example.test' --data 'rspamd/spam-header')
for ID in MAIL_ID{1,2,3,4}; do
[[ -n ${!ID} ]] || { echo "${ID} is empty - aborting!" ; return 1 ; }
@ -256,7 +256,7 @@ function teardown_file() { _default_teardown ; }
# Move an email to the "Junk" folder from "INBOX"; the first email we
# sent should pass fine, hence we can now move it.
_send_email 'nc_templates/rspamd_imap_move_to_junk' '0.0.0.0 143'
_nc_wrapper 'nc/rspamd_imap_move_to_junk' '0.0.0.0 143'
sleep 1 # wait for the transaction to finish
_run_in_container cat /var/log/mail/mail.log
@ -270,7 +270,7 @@ function teardown_file() { _default_teardown ; }
# Move an email to the "INBOX" folder from "Junk"; there should be two mails
# in the "Junk" folder, since the second email we sent during setup should
# have landed in the Junk folder already.
_send_email 'nc_templates/rspamd_imap_move_to_inbox' '0.0.0.0 143'
_nc_wrapper 'nc/rspamd_imap_move_to_inbox' '0.0.0.0 143'
sleep 1 # wait for the transaction to finish
_run_in_container cat /var/log/mail/mail.log

View file

@ -95,7 +95,7 @@ function teardown() { _default_teardown ; }
function _should_send_spam_message() {
_wait_for_smtp_port_in_container
_wait_for_tcp_port_in_container 10024 # port 10024 is for Amavis
_send_email 'email-templates/amavis-spam'
_send_email --from 'spam@external.tld' --data 'amavis/spam'
}
function _should_be_received_by_amavis() {

View file

@ -38,7 +38,7 @@ function teardown() { _default_teardown ; }
# - A warning is raised about usage of potentially insecure parameters.
@test "Custom" {
export CONTAINER_NAME=${CONTAINER2_NAME}
local DH_PARAMS_CUSTOM='test/test-files/ssl/custom-dhe-params.pem'
local DH_PARAMS_CUSTOM='test/files/ssl/custom-dhe-params.pem'
local DH_CHECKSUM_CUSTOM=$(sha512sum "${DH_PARAMS_CUSTOM}" | awk '{print $1}')
_init_with_defaults

View file

@ -88,7 +88,7 @@ function _initial_setup() {
# All of these certs support both FQDNs (`mail.example.test` and `example.test`),
# Except for the wildcard cert (`*.example.test`), that was created with `example.test` intentionally excluded from SAN.
# We want to maintain the same FQDN (`mail.example.test`) between the _acme_ecdsa and _acme_rsa tests.
local LOCAL_BASE_PATH="${PWD}/test/test-files/ssl/example.test/with_ca/rsa"
local LOCAL_BASE_PATH="${PWD}/test/files/ssl/example.test/with_ca/rsa"
function _prepare() {
# Default `acme.json` for _acme_ecdsa test:
@ -240,7 +240,7 @@ function _copy_to_letsencrypt_storage() {
FQDN_DIR=$(echo "${DEST}" | cut -d '/' -f1)
mkdir -p "${TEST_TMP_CONFIG}/letsencrypt/${FQDN_DIR}"
if ! cp "${PWD}/test/test-files/ssl/${SRC}" "${TEST_TMP_CONFIG}/letsencrypt/${DEST}"; then
if ! cp "${PWD}/test/files/ssl/${SRC}" "${TEST_TMP_CONFIG}/letsencrypt/${DEST}"; then
echo "Could not copy cert file '${SRC}'' to '${DEST}'" >&2
exit 1
fi

View file

@ -20,7 +20,7 @@ function setup_file() {
export TEST_DOMAIN='example.test'
local CUSTOM_SETUP_ARGUMENTS=(
--volume "${PWD}/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/:/config/ssl/:ro"
--volume "${PWD}/test/files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/:/config/ssl/:ro"
--env LOG_LEVEL='trace'
--env SSL_TYPE='manual'
--env TLS_LEVEL='modern'
@ -108,10 +108,10 @@ function teardown_file() { _default_teardown ; }
@test "manual cert changes are picked up by check-for-changes" {
printf '%s' 'someThingsChangedHere' \
>>"$(pwd)/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem"
>>"$(pwd)/test/files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem"
run timeout 15 docker exec "${CONTAINER_NAME}" 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/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem"
sed -i '/someThingsChangedHere/d' "$(pwd)/test/files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem"
}

View file

@ -17,7 +17,7 @@ function setup_file() {
# Contains various certs for testing TLS support (read-only):
export TLS_CONFIG_VOLUME
TLS_CONFIG_VOLUME="${PWD}/test/test-files/ssl/${TEST_DOMAIN}/:/config/ssl/:ro"
TLS_CONFIG_VOLUME="${PWD}/test/files/ssl/${TEST_DOMAIN}/:/config/ssl/:ro"
# Used for connecting testssl and DMS containers via network name `TEST_DOMAIN`:
# NOTE: If the network already exists, the test will fail to start

View file

@ -207,7 +207,7 @@ function _should_have_correct_mail_headers() {
# (eg: OVERRIDE_HOSTNAME or `--hostname mail --domainname example.test`)
local EXPECTED_HOSTNAME=${3:-${EXPECTED_FQDN}}
_send_email 'email-templates/existing-user1'
_send_email --from 'user@external.tld' --data 'existing/user1'
_wait_for_empty_mail_queue_in_container
_count_files_in_directory_in_container '/var/mail/localhost.localdomain/user1/new/' '1'

View file

@ -47,9 +47,11 @@ function teardown_file() {
@test "should always send a DSN when requested" {
export CONTAINER_NAME=${CONTAINER1_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
# TODO replace with _send_email as soon as it supports DSN
# TODO ref: https://github.com/jetmore/swaks/issues/41
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
@ -60,7 +62,7 @@ function teardown_file() {
@test "should only send a DSN when requested from ports 465/587" {
export CONTAINER_NAME=${CONTAINER2_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated'
_wait_for_empty_mail_queue_in_container
# DSN requests can now only be made on ports 465 and 587,
@ -72,8 +74,8 @@ function teardown_file() {
assert_failure
# These ports are excluded via master.cf.
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
@ -83,9 +85,9 @@ function teardown_file() {
@test "should never send a DSN" {
export CONTAINER_NAME=${CONTAINER3_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
# DSN requests are rejected regardless of origin.

View file

@ -38,7 +38,7 @@ function teardown_file() { _default_teardown ; }
@test "delivers mail to existing account" {
_wait_for_smtp_port_in_container
_send_email 'email-templates/existing-user1' # send a test email
_send_email --data 'existing/user1' # send a test email
# Verify delivery was successful, log line should look similar to:
# postfix/lmtp[1274]: 0EA424ABE7D9: to=<user1@localhost.localdomain>, relay=127.0.0.1[127.0.0.1]:24, delay=0.13, delays=0.07/0.01/0.01/0.05, dsn=2.0.0, status=sent (250 2.0.0 <user1@localhost.localdomain> ixPpB+Zvv2P7BAAAUi6ngw Saved)

View file

@ -25,7 +25,11 @@ function teardown_file() { _default_teardown ; }
# this test covers https://github.com/docker-mailserver/docker-mailserver/issues/681
@test "(Postfix) remove privacy details of the sender" {
_run_in_container_bash "openssl s_client -quiet -starttls smtp -connect 0.0.0.0:587 < /tmp/docker-mailserver-test/email-templates/send-privacy-email.txt"
_send_email \
--port 587 -tls --auth LOGIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--data 'privacy'
assert_success
_run_until_success_or_timeout 120 _exec_in_container_bash '[[ -d /var/mail/localhost.localdomain/user1/new ]]'

View file

@ -63,34 +63,55 @@ function setup_file() {
# TODO: Move to clamav tests (For use when ClamAV is enabled):
# _repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /var/run/clamav/clamd.ctl
# _send_email 'email-templates/amavis-virus'
# _send_email --from 'virus@external.tld' --data 'amavis/virus'
# Required for 'delivers mail to existing alias':
_send_email 'email-templates/existing-alias-external'
_send_email --to alias1@localhost.localdomain --data 'existing/alias-external'
# Required for 'delivers mail to existing alias with recipient delimiter':
_send_email 'email-templates/existing-alias-recipient-delimiter'
_send_email --to alias1~test@localhost.localdomain --data 'existing/alias-recipient-delimiter'
# Required for 'delivers mail to existing catchall':
_send_email 'email-templates/existing-catchall-local'
_send_email --to wildcard@localdomain2.com --data 'existing/catchall-local'
# Required for 'delivers mail to regexp alias':
_send_email 'email-templates/existing-regexp-alias-local'
_send_email --to test123@localhost.localdomain --data 'existing/regexp-alias-local'
# Required for 'rejects mail to unknown user':
_send_email 'email-templates/non-existing-user'
_send_email --to nouser@localhost.localdomain --data 'non-existing-user'
# Required for 'redirects mail to external aliases':
_send_email 'email-templates/existing-regexp-alias-external'
_send_email 'email-templates/existing-alias-local'
_send_email --to bounce-always@localhost.localdomain --data 'existing/regexp-alias-external'
_send_email --to alias2@localhost.localdomain --data 'existing/alias-local'
# Required for 'rejects spam':
_send_email 'email-templates/amavis-spam'
_send_email --from 'spam@external.tld' --data 'amavis/spam'
# Required for 'delivers mail to existing account':
_send_email 'email-templates/existing-user1'
_send_email 'email-templates/existing-user2'
_send_email 'email-templates/existing-user3'
_send_email 'email-templates/existing-added'
_send_email 'email-templates/existing-user-and-cc-local-alias'
_send_email 'email-templates/sieve-spam-folder'
_send_email 'email-templates/sieve-pipe'
_run_in_container_bash 'sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt'
_send_email --data 'existing/user1'
assert_success
_send_email --to user2@otherdomain.tld
assert_success
_send_email --to user3@localhost.localdomain
assert_success
_send_email --to added@localhost.localdomain --data 'existing/added'
assert_success
_send_email --to user1@localhost.localdomain --data 'existing/user-and-cc-local-alias'
assert_success
_send_email --data 'sieve/spam-folder'
assert_success
_send_email --to user2@otherdomain.tld --data 'sieve/pipe'
assert_success
_run_in_container_bash 'sendmail root < /tmp/docker-mailserver-test/emails/sendmail/root-email.txt'
assert_success
}
function _unsuccessful() {
_send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password wrongpassword
assert_failure
assert_output --partial 'authentication failed'
assert_output --partial 'No authentication type succeeded'
}
function _successful() {
_send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password mypassword --quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
}
@test "should succeed at emptying mail queue" {
@ -103,44 +124,35 @@ function setup_file() {
}
@test "should successfully authenticate with good password (plain)" {
_send_email 'auth/smtp-auth-plain' '-w 5 0.0.0.0 465'
assert_output --partial 'Authentication successful'
_successful PLAIN user1@localhost.localdomain
}
@test "should fail to authenticate with wrong password (plain)" {
_send_email 'auth/smtp-auth-plain-wrong' '-w 20 0.0.0.0 465'
assert_output --partial 'authentication failed'
_unsuccessful PLAIN user1@localhost.localdomain
}
@test "should successfully authenticate with good password (login)" {
_send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 465'
assert_output --partial 'Authentication successful'
_successful LOGIN user1@localhost.localdomain
}
@test "should fail to authenticate with wrong password (login)" {
_send_email 'auth/smtp-auth-login-wrong' '-w 20 0.0.0.0 465'
assert_output --partial 'authentication failed'
_unsuccessful LOGIN user1@localhost.localdomain
}
@test "[user: 'added'] should successfully authenticate with good password (plain)" {
_send_email 'auth/added-smtp-auth-plain' '-w 5 0.0.0.0 465'
assert_output --partial 'Authentication successful'
_successful PLAIN added@localhost.localdomain
}
@test "[user: 'added'] should fail to authenticate with wrong password (plain)" {
_send_email 'auth/added-smtp-auth-plain-wrong' '-w 20 0.0.0.0 465'
assert_output --partial 'authentication failed'
_unsuccessful PLAIN added@localhost.localdomain
}
@test "[user: 'added'] should successfully authenticate with good password (login)" {
_send_email 'auth/added-smtp-auth-login' '-w 5 0.0.0.0 465'
assert_success
assert_output --partial 'Authentication successful'
_successful LOGIN added@localhost.localdomain
}
@test "[user: 'added'] should fail to authenticate with wrong password (login)" {
_send_email 'auth/added-smtp-auth-login-wrong' '-w 20 0.0.0.0 465'
assert_output --partial 'authentication failed'
_unsuccessful LOGIN added@localhost.localdomain
}
# TODO: Add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default)
@ -258,7 +270,13 @@ function setup_file() {
# Dovecot does not support SMTPUTF8, so while we can send we cannot receive
# Better disable SMTPUTF8 support entirely if we can't handle it correctly
@test "not advertising smtputf8" {
_send_email 'email-templates/smtp-ehlo'
# Query supported extensions; SMTPUTF8 should not be available.
# - This query requires a EHLO greeting to the destination server.
_send_email \
--ehlo mail.external.tld \
--protocol ESMTP \
--server mail.example.test \
--quit-after FIRST-EHLO
refute_output --partial 'SMTPUTF8'
}

View file

@ -32,7 +32,16 @@ function teardown_file() { _default_teardown ; }
assert_success
# it looks as if someone tries to send mail to another domain outside of DMS
_send_email 'email-templates/smtp-only'
_send_email \
--ehlo mail.origin.test \
--protocol SSMTPA \
--server mail.origin.test \
--from user@origin.test \
--to user@destination.test \
--auth PLAIN \
--auth-user user@origin.test \
--auth-password secret
assert_success
_wait_for_empty_mail_queue_in_container
# this seemingly succeeds, but looking at the logs, it doesn't

View file

@ -24,11 +24,13 @@ function teardown_file() { _default_teardown ; }
}
@test 'authentication works' {
_send_email 'auth/pop3-auth' '-w 1 0.0.0.0 110'
_nc_wrapper 'auth/pop3-auth' '-w 1 0.0.0.0 110'
assert_success
}
@test 'added user authentication works' {
_send_email 'auth/added-pop3-auth' '-w 1 0.0.0.0 110'
_nc_wrapper 'auth/added-pop3-auth' '-w 1 0.0.0.0 110'
assert_success
}
@test '/var/log/mail/mail.log is error-free' {

View file

@ -21,7 +21,8 @@ function setup_file() {
function teardown_file() { _default_teardown ; }
@test '(Dovecot) LDAP RIMAP connection and authentication works' {
_send_email 'auth/imap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143'
assert_success
}
@test '(SASLauthd) SASL RIMAP authentication works' {
@ -30,13 +31,30 @@ function teardown_file() { _default_teardown ; }
}
@test '(SASLauthd) RIMAP SMTP authentication works' {
_send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 25'
assert_output --partial 'Error: authentication not enabled'
_send_email \
--auth LOGIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--quit-after AUTH
assert_failure
assert_output --partial 'Host did not advertise authentication'
_send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 465'
_send_email \
--port 465 \
--auth LOGIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
_send_email 'auth/smtp-auth-login' '-w 5 0.0.0.0 587'
_send_email \
--port 587 \
--auth LOGIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
}

View file

@ -122,7 +122,6 @@ function setup_file() {
# Extra ENV needed to support specific test-cases:
local ENV_SUPPORT=(
--env PERMIT_DOCKER=container # Required for attempting SMTP auth on port 25 via nc
# Required for openssl commands to be successul:
# NOTE: snakeoil cert is created (for `docker-mailserver.invalid`) via Debian post-install script for Postfix package.
# TODO: Use proper TLS cert
@ -249,7 +248,7 @@ function teardown() {
# dovecot
@test "dovecot: ldap imap connection and authentication works" {
_run_in_container_bash 'nc -w 1 0.0.0.0 143 < /tmp/docker-mailserver-test/auth/imap-ldap-auth.txt'
_nc_wrapper 'auth/imap-ldap-auth' '-w 1 0.0.0.0 143'
assert_success
}
@ -327,12 +326,25 @@ function teardown() {
@test "spoofing (with LDAP): rejects sender forging" {
_wait_for_smtp_port_in_container_to_respond dms-test_ldap
_run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed.txt'
_send_email \
--port 465 -tlsc --auth LOGIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--ehlo mail \
--from ldap@localhost.localdomain \
--data 'auth/ldap-smtp-auth-spoofed'
assert_output --partial 'Sender address rejected: not owned by user'
}
@test "spoofing (with LDAP): accepts sending as alias" {
_run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed-alias.txt'
_send_email \
--port 465 -tlsc --auth LOGIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--ehlo mail \
--from postmaster@localhost.localdomain \
--to some.user@localhost.localdomain \
--data 'auth/ldap-smtp-auth-spoofed-alias'
assert_output --partial 'End data with'
}
@ -341,19 +353,42 @@ function teardown() {
# Template used has invalid AUTH: https://github.com/docker-mailserver/docker-mailserver/pull/3006#discussion_r1073321432
skip 'TODO: This test seems to have been broken from the start (?)'
_run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt'
_send_email \
--port 465 -tlsc --auth LOGIN \
--auth-user some.user.email@localhost.localdomain \
--auth-password secret \
--ehlo mail \
--from randomspoofedaddress@localhost.localdomain \
--to some.user@localhost.localdomain \
--data 'auth/ldap-smtp-auth-spoofed-sender-with-filter-exception'
assert_output --partial 'Sender address rejected: not owned by user'
}
@test "saslauthd: ldap smtp authentication" {
# Requires ENV `PERMIT_DOCKER=container`
_send_email 'auth/sasl-ldap-smtp-auth' '-w 5 0.0.0.0 25'
assert_output --partial 'Error: authentication not enabled'
_send_email \
--auth LOGIN \
--auth-user some.user@localhost.localdomain \
--auth-password wrongpassword \
--quit-after AUTH
assert_failure
assert_output --partial 'Host did not advertise authentication'
_run_in_container_bash 'openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/sasl-ldap-smtp-auth.txt'
_send_email \
--port 465 -tlsc \
--auth LOGIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
_run_in_container_bash 'openssl s_client -quiet -starttls smtp -connect 0.0.0.0:587 < /tmp/docker-mailserver-test/auth/sasl-ldap-smtp-auth.txt'
_send_email \
--port 587 -tls \
--auth LOGIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
}
@ -391,7 +426,7 @@ function _should_successfully_deliver_mail_to() {
local SENDER_ADDRESS='user@external.tld'
local RECIPIENT_ADDRESS=${1:?Recipient address is required}
local MAIL_STORAGE_RECIPIENT=${2:?Recipient storage location is required}
local MAIL_TEMPLATE='/tmp/docker-mailserver-test/email-templates/test-email.txt'
local MAIL_TEMPLATE='/tmp/docker-mailserver-test/emails/test-email.txt'
_run_in_container_bash "sendmail -f ${SENDER_ADDRESS} ${RECIPIENT_ADDRESS} < ${MAIL_TEMPLATE}"
_wait_for_empty_mail_queue_in_container

View file

@ -13,7 +13,7 @@ setup_file() {
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 \
-v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \
-e SMTP_ONLY=1 \
-e PERMIT_DOCKER=connected-networks \
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
@ -26,7 +26,7 @@ setup_file() {
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 \
-v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \
-e SMTP_ONLY=1 \
-e PERMIT_DOCKER=connected-networks \
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
@ -39,7 +39,7 @@ setup_file() {
# 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 \
-v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \
-e SMTP_ONLY=1 \
-e PERMIT_DOCKER=none \
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
@ -68,7 +68,7 @@ teardown_file() {
_reload_postfix mail_smtponly_second_network
# 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"
run docker exec mail_smtponly_second_network_sender /bin/sh -c "nc mail_smtponly_second_network 25 < /tmp/docker-mailserver-test/emails/nc_raw/smtp-only.txt"
assert_output --partial "250 2.0.0 Ok: queued as "
repeat_in_container_until_success_or_timeout 60 mail_smtponly_second_network /bin/sh -c 'grep -cE "to=<user2\@external.tld>.*status\=sent" /var/log/mail/mail.log'
}
@ -80,7 +80,7 @@ teardown_file() {
_reload_postfix mail_smtponly_force_authentication
# 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"
run docker exec mail_smtponly_force_authentication /bin/sh -c "nc localhost 25 < /tmp/docker-mailserver-test/emails/nc_raw/smtp-only.txt"
assert_output --partial "550 5.5.1 Protocol error"
[[ ${status} -ge 0 ]]
}

View file

@ -171,7 +171,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:'
# 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 \
-v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \
-e ENABLE_CLAMAV=1 \
-h mail.my-domain.com \
-t "${NAME}")
@ -186,7 +186,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:'
[[ ${SECONDS} -lt 5 ]]
# 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"
docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/emails/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}"
@ -203,7 +203,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:'
# 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 \
-v "$(pwd)/test/files":/tmp/docker-mailserver-test:ro \
-e ENABLE_CLAMAV=1 \
-h mail.my-domain.com \
-t "${NAME}")
@ -213,7 +213,7 @@ BATS_TEST_NAME_PREFIX='test helper functions:'
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"
docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/emails/amavis-virus.txt"
# give it some time to clear the queue
SECONDS=0

View file

@ -80,11 +80,13 @@ function teardown_file() { _default_teardown ; }
}
@test "imap: authentication works" {
_send_email 'auth/imap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143'
assert_success
}
@test "imap: added user authentication works" {
_send_email 'auth/added-imap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/added-imap-auth' '-w 1 0.0.0.0 143'
assert_success
}
#
@ -288,13 +290,34 @@ EOF
@test "spoofing: rejects sender forging" {
# rejection of spoofed sender
_wait_for_smtp_port_in_container_to_respond
_run_in_container_bash "openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed.txt"
# An authenticated user cannot use an envelope sender (MAIL FROM)
# address they do not own according to `main.cf:smtpd_sender_login_maps` lookup
_send_email \
--port 465 -tlsc --auth LOGIN \
--auth-user added@localhost.localdomain \
--auth-password mypassword \
--ehlo mail \
--from user2@localhost.localdomain \
--data 'auth/added-smtp-auth-spoofed'
assert_output --partial 'Sender address rejected: not owned by user'
}
@test "spoofing: accepts sending as alias" {
_run_in_container_bash "openssl s_client -quiet -connect 0.0.0.0:465 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed-alias.txt | grep 'End data with'"
# An authenticated account should be able to send mail from an alias,
# Verifies `main.cf:smtpd_sender_login_maps` includes /etc/postfix/virtual
# The envelope sender address (MAIL FROM) is the lookup key
# to each table. Address is authorized when a result that maps to
# the DMS account is returned.
_send_email \
--port 465 -tlsc --auth LOGIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--ehlo mail \
--from alias1@localhost.localdomain \
--data 'auth/added-smtp-auth-spoofed-alias'
assert_success
assert_output --partial 'End data with'
}
#

View file

@ -20,7 +20,7 @@ function setup_file() {
function teardown_file() { _default_teardown ; }
@test 'should successfully deliver mail' {
_send_email 'email-templates/existing-user1'
_send_email --data 'existing/user1'
_wait_for_empty_mail_queue_in_container
# Should be successfully sent (received) by Postfix: