mirror of
https://github.com/docker-mailserver/docker-mailserver.git
synced 2025-08-02 17:14:31 +02:00
* chore: Co-locate process checking and process restart verification Extract the test cases for checking a process is running and properly restarts from various test files into a single one: Core (always running): opendkim, opendmarc, master (postfix) ENV dependent: amavi (amavisd-new), clamd, dovecot, fail2ban-server (fail2ban), fetchmail, postgrey, postsrsd, saslauthd These now run off a single container with the required ENV and call a common function (the revised version in parallel test cases). * fix(saslauthd): Quote wrap supervisor config vars `saslauth.conf` calls `-O` option for most commands defined with an ENV that may be empty/null. This would cause the process to silently fail / die. This doesn't happen if quote wrapping the ENV, which calls `-O` with an empty string. Not necessary, but since one of `postgrey` ENV were quote wrapped in `supervisor-app.conf`, I've also done the same there. * fix(postsrsd): Change supervisor `autorestart` policy to `true` The PR that introduced the config switched from `true` to `unexpected` without any context. That prevents restart working when the process is killed. Setting to `true` instead will correctly restart the service. * chore: Remove disabled postgrey test file `mail_with_postgrey_disabled_by_default.bats` only checked the migrated test cases, removed as no longer serving a purpose. * tests(refactor): Make `_should_restart_when_killed()` more reliable The previous version did not ensure that the last checks process was actually restarted, only that it was running. It turns out that `pkill` is only sending the signal, there can be some delay before the original process is actually killed and restarted. This can be identified with `pgrep --older <seconds>`. First ensure the process is at a specified age, then after killing check that the process is not running that is at least that old, finally check that there is a younger process actually running.. (_could fail if a process doesn't restart, or there is a delay such as imposed by `sleep` in wrapper scripts for postfix and fail2ban_) The helper method is not used anywhere else now, move it into this test instead. It has been refactored to accomodate the needs for `--older`, and `--list-full` provides some output that can be matched (similar for `pkill --echo`). * test(docs): Add inline notes about processes * chore: Compress test cases into single case with loop Moves the list of processes into array vars to iterate through instead. If a failure occurs, the process name is visible along with line number in `_should_restart_when_killed()` to identify what went wrong. * chore: Handle `FETCHMAIL_PARALLEL=1` process checks as well * tests: Add test case for disabled ENV Additional coverage to match what other test files were doing before, ensuring that these ENV can prevent their respective service from running. * chore: Move `clamd` enabled check to it's own test case Not sure about this. It reduces the time of CPU activity (sustained full load on a thread) and increase in memory usage (1GB+ loading signatures database), but as a separate test case it also adds 10 seconds without reducing the time of the test case it was extracted from. * chore: Make `disabled` variant the 1st test case * fix: Adjust test cases to pass when using slower wrapper scripts * tests(refactor): `mail_fetchmail.bats` updated to new format Additionally merges in the parallel test file. * chore: Move `config/fetchmail.cf` into separate sub-directory Keep out of the default base config for tests. * chore: Change `fetchmail.cf` FQDNs to `.test` TLD Changed the first configs remote and local user values to more clearly document what their values should represent (_and that they don't need to be a full mail address, that's just what our Dovecot is configured with for login_). Shifted the `here` to the end of the `is` line. It's optional syntax, only intended to contrast with the remote `there` for readability. Additionally configured imap protocol. Not tested or verified if that's correct configuration for usage with imap protocol instead. The fetchmail feature tests are currently lacking. Added an inline doc into the fetchmail test to reference a PR about the importance of the trailing `.` in the config. Updated the partial matching to ensure it matches for that in the value as well. * chore: Finalize `process-check-restart.bats` Few minor adjustments. The other ENV for clamd doesn't seem to provide any benefit, trim out the noise. Added a note about why it's been split out. Fetchmail parallel configs are matching the config file path in the process command that is returned. The `.rc` suffix is just to add further clarity to that.
190 lines
6.8 KiB
Bash
190 lines
6.8 KiB
Bash
load "${REPOSITORY_ROOT}/test/helper/setup"
|
|
load "${REPOSITORY_ROOT}/test/helper/common"
|
|
|
|
BATS_TEST_NAME_PREFIX='[Fail2Ban] '
|
|
CONTAINER1_NAME='dms-test_fail2ban'
|
|
CONTAINER2_NAME='dms-test_fail2ban_fail-auth-mailer'
|
|
|
|
function setup_file() {
|
|
export CONTAINER_NAME
|
|
|
|
CONTAINER_NAME=${CONTAINER1_NAME}
|
|
local CUSTOM_SETUP_ARGUMENTS=(
|
|
--env ENABLE_FAIL2BAN=1
|
|
--env POSTSCREEN_ACTION=ignore
|
|
--cap-add=NET_ADMIN
|
|
# NOTE: May no longer be needed with newer F2B:
|
|
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)"
|
|
)
|
|
init_with_defaults
|
|
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
|
wait_for_smtp_port_in_container "${CONTAINER_NAME}"
|
|
|
|
# Create a container which will send wrong authentications and should get banned
|
|
CONTAINER_NAME=${CONTAINER2_NAME}
|
|
init_with_defaults
|
|
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
|
|
|
# Set default implicit container fallback for helpers:
|
|
CONTAINER_NAME=${CONTAINER1_NAME}
|
|
}
|
|
|
|
function teardown_file() {
|
|
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}"
|
|
}
|
|
|
|
@test "localhost is not banned because ignored" {
|
|
_run_in_container fail2ban-client status postfix-sasl
|
|
assert_success
|
|
refute_output --regexp '.*IP list:.*127\.0\.0\.1.*'
|
|
|
|
_run_in_container grep 'ignoreip = 127.0.0.1/8' /etc/fail2ban/jail.conf
|
|
assert_success
|
|
}
|
|
|
|
@test "fail2ban-fail2ban.cf overrides" {
|
|
_run_in_container fail2ban-client get loglevel
|
|
assert_success
|
|
assert_output --partial 'DEBUG'
|
|
}
|
|
|
|
@test "fail2ban-jail.cf overrides" {
|
|
for FILTER in 'dovecot' 'postfix' 'postfix-sasl'
|
|
do
|
|
_run_in_container fail2ban-client get "${FILTER}" bantime
|
|
assert_output 1234
|
|
|
|
_run_in_container fail2ban-client get "${FILTER}" findtime
|
|
assert_output 321
|
|
|
|
_run_in_container fail2ban-client get "${FILTER}" maxretry
|
|
assert_output 2
|
|
|
|
_run_in_container fail2ban-client -d
|
|
assert_output --partial "['set', 'dovecot', 'addaction', 'nftables-multiport']"
|
|
assert_output --partial "['set', 'postfix', 'addaction', 'nftables-multiport']"
|
|
assert_output --partial "['set', 'postfix-sasl', 'addaction', 'nftables-multiport']"
|
|
done
|
|
}
|
|
|
|
# NOTE: This test case is fragile if other test cases were to be run concurrently.
|
|
# - After multiple login fails and a slight delay, f2b will ban that IP.
|
|
# - You could hard-code `sleep 5` on both cases to avoid the alternative assertions,
|
|
# but the polling + piping into grep approach here reliably minimizes the delay.
|
|
@test "ban ip on multiple failed login" {
|
|
CONTAINER1_IP=$(get_container_ip ${CONTAINER1_NAME})
|
|
# Trigger a ban by failing to login twice:
|
|
_run_in_container_explicit "${CONTAINER2_NAME}" bash -c "nc ${CONTAINER1_IP} 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt"
|
|
_run_in_container_explicit "${CONTAINER2_NAME}" bash -c "nc ${CONTAINER1_IP} 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt"
|
|
|
|
# Checking that CONTAINER2_IP is banned in "${CONTAINER1_NAME}"
|
|
CONTAINER2_IP=$(get_container_ip ${CONTAINER2_NAME})
|
|
run repeat_in_container_until_success_or_timeout 10 "${CONTAINER_NAME}" bash -c "fail2ban-client status postfix-sasl | grep -F '${CONTAINER2_IP}'"
|
|
assert_success
|
|
assert_output --partial 'Banned IP list:'
|
|
|
|
# Checking that CONTAINER2_IP is banned by nftables
|
|
_run_in_container bash -c 'nft list set inet f2b-table addr-set-postfix-sasl'
|
|
assert_success
|
|
assert_output --partial "elements = { ${CONTAINER2_IP} }"
|
|
}
|
|
|
|
# NOTE: Depends on previous test case, if no IP was banned at this point, it passes regardless..
|
|
@test "unban ip works" {
|
|
CONTAINER2_IP=$(get_container_ip ${CONTAINER2_NAME})
|
|
_run_in_container fail2ban-client set postfix-sasl unbanip "${CONTAINER2_IP}"
|
|
assert_success
|
|
|
|
# Checking that CONTAINER2_IP is unbanned in "${CONTAINER1_NAME}"
|
|
_run_in_container fail2ban-client status postfix-sasl
|
|
assert_success
|
|
refute_output --partial "${CONTAINER2_IP}"
|
|
|
|
# Checking that CONTAINER2_IP is unbanned by nftables
|
|
_run_in_container bash -c 'nft list set inet f2b-table addr-set-postfix-sasl'
|
|
refute_output --partial "${CONTAINER2_IP}"
|
|
}
|
|
|
|
@test "bans work properly (single IP)" {
|
|
_run_in_container fail2ban ban 192.0.66.7
|
|
assert_success
|
|
assert_output 'Banned custom IP: 1'
|
|
|
|
_run_in_container fail2ban
|
|
assert_success
|
|
assert_output --regexp 'Banned in custom:.*192\.0\.66\.7'
|
|
|
|
_run_in_container nft list set inet f2b-table addr-set-custom
|
|
assert_success
|
|
assert_output --partial 'elements = { 192.0.66.7 }'
|
|
|
|
_run_in_container fail2ban unban 192.0.66.7
|
|
assert_success
|
|
assert_output --partial 'Unbanned IP from custom: 1'
|
|
|
|
_run_in_container nft list set inet f2b-table addr-set-custom
|
|
refute_output --partial '192.0.66.7'
|
|
}
|
|
|
|
@test "bans work properly (subnet)" {
|
|
_run_in_container fail2ban ban 192.0.66.0/24
|
|
assert_success
|
|
assert_output 'Banned custom IP: 1'
|
|
|
|
_run_in_container fail2ban
|
|
assert_success
|
|
assert_output --regexp 'Banned in custom:.*192\.0\.66\.0/24'
|
|
|
|
_run_in_container nft list set inet f2b-table addr-set-custom
|
|
assert_success
|
|
assert_output --partial 'elements = { 192.0.66.0/24 }'
|
|
|
|
_run_in_container fail2ban unban 192.0.66.0/24
|
|
assert_success
|
|
assert_output --partial 'Unbanned IP from custom: 1'
|
|
|
|
_run_in_container nft list set inet f2b-table addr-set-custom
|
|
refute_output --partial '192.0.66.0/24'
|
|
}
|
|
|
|
@test "FAIL2BAN_BLOCKTYPE is really set to drop" {
|
|
# ban IPs here manually so we can be sure something is inside the jails
|
|
for JAIL in dovecot postfix-sasl custom; do
|
|
_run_in_container fail2ban-client set "${JAIL}" banip 192.33.44.55
|
|
assert_success
|
|
done
|
|
|
|
_run_in_container nft list table inet f2b-table
|
|
assert_success
|
|
assert_output --partial 'tcp dport { 110, 143, 465, 587, 993, 995, 4190 } ip saddr @addr-set-dovecot drop'
|
|
assert_output --partial 'tcp dport { 25, 110, 143, 465, 587, 993, 995 } ip saddr @addr-set-postfix-sasl drop'
|
|
assert_output --partial 'tcp dport { 25, 110, 143, 465, 587, 993, 995, 4190 } ip saddr @addr-set-custom drop'
|
|
|
|
# unban the IPs previously banned to get a clean state again
|
|
for JAIL in dovecot postfix-sasl custom; do
|
|
_run_in_container fail2ban-client set "${JAIL}" unbanip 192.33.44.55
|
|
assert_success
|
|
done
|
|
}
|
|
|
|
@test "setup.sh fail2ban" {
|
|
_run_in_container fail2ban-client set dovecot banip 192.0.66.4
|
|
_run_in_container fail2ban-client set dovecot banip 192.0.66.5
|
|
|
|
# Originally: run ./setup.sh -c "${CONTAINER1_NAME}" fail2ban
|
|
_run_in_container setup fail2ban
|
|
assert_output --regexp '^Banned in dovecot:.*192\.0\.66\.4'
|
|
assert_output --regexp '^Banned in dovecot:.*192\.0\.66\.5'
|
|
|
|
_run_in_container setup fail2ban unban 192.0.66.4
|
|
assert_output --partial "Unbanned IP from dovecot: 1"
|
|
|
|
_run_in_container setup fail2ban
|
|
assert_output --regexp '^Banned in dovecot:.*192\.0\.66\.5'
|
|
|
|
_run_in_container setup fail2ban unban 192.0.66.5
|
|
assert_output --partial 'Unbanned IP from dovecot: 1'
|
|
|
|
_run_in_container setup fail2ban unban
|
|
assert_output --partial 'You need to specify an IP address: Run'
|
|
}
|