diff --git a/Dockerfile b/Dockerfile index 79d09353..15916ae2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,10 +55,10 @@ RUN \ dovecot-ldap dovecot-lmtpd dovecot-managesieved dovecot-pop3d \ dovecot-sieve dovecot-solr dumb-init \ # E - O - ed fetchmail file gamin gnupg gzip iproute2 iptables \ + ed fetchmail file gamin gnupg gzip iproute2 \ locales logwatch lhasa libdate-manip-perl libldap-common liblz4-tool \ libmail-spf-perl libnet-dns-perl libsasl2-modules lrzip lzop \ - netcat-openbsd nomarch opendkim opendkim-tools opendmarc \ + netcat-openbsd nftables nomarch opendkim opendkim-tools opendmarc \ # P - Z pax pflogsumm postgrey p7zip-full postfix-ldap postfix-pcre \ postfix-policyd-spf-python postsrsd pyzor \ @@ -194,11 +194,6 @@ COPY target/opendmarc/opendmarc.conf /etc/opendmarc.conf COPY target/opendmarc/default-opendmarc /etc/default/opendmarc COPY target/opendmarc/ignore.hosts /etc/opendmarc/ignore.hosts -RUN \ - # switch iptables and ip6tables to legacy for Fail2Ban - update-alternatives --set iptables /usr/sbin/iptables-legacy && \ - update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy - # ----------------------------------------------- # --- Fetchmail, Postfix & Let'sEncrypt --------- # ----------------------------------------------- diff --git a/config-examples/fail2ban-jail.cf b/config-examples/fail2ban-jail.cf index 04bd5ba6..146c8282 100644 --- a/config-examples/fail2ban-jail.cf +++ b/config-examples/fail2ban-jail.cf @@ -16,6 +16,6 @@ #ignoreip = 127.0.0.1/8 # Default ban action -# iptables-multiport: block IP only on affected port -# iptables-allports: block IP on all ports -#banaction = iptables-allports +# nftables-multiport: block IP only on affected port +# nftables-allports: block IP on all ports +#banaction = nftables-allports diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index f8706531..2a645150 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -100,7 +100,7 @@ cap_add: - NET_ADMIN ``` -Otherwise, `iptables` won't be able to ban IPs. +Otherwise, `nftables` won't be able to ban IPs. ##### FAIL2BAN_BLOCKTYPE diff --git a/docs/content/config/security/fail2ban.md b/docs/content/config/security/fail2ban.md index 43d326ee..9adf63c8 100644 --- a/docs/content/config/security/fail2ban.md +++ b/docs/content/config/security/fail2ban.md @@ -29,7 +29,7 @@ Example configuration volume bind: ``` !!! attention - `docker-mailserver` must be launched with the `NET_ADMIN` capability in order to be able to install the iptable rules that actually ban IP addresses. + `docker-mailserver` must be launched with the `NET_ADMIN` capability in order to be able to install the nftables rules that actually ban IP addresses. Thus either include `--cap-add=NET_ADMIN` in the `docker run` command, or the equivalent in `docker-compose.yml`: @@ -38,16 +38,6 @@ Example configuration volume bind: - NET_ADMIN ``` -If you don't you will see errors the form of: - -```log -iptables -w -X f2b-postfix -- stderr: "getsockopt failed strangely: Operation not permitted\niptables v1.4.21: can't initialize iptabl -es table `filter': Permission denied (you must be root)\nPerhaps iptables or your kernel needs to be upgraded.\niptables v1.4.21: can' -t initialize iptables table `filter': Permission denied (you must be root)\nPerhaps iptables or your kernel needs to be upgraded.\n" -2016-06-01 00:53:51,284 fail2ban.action [678]: ERROR iptables -w -D INPUT -p tcp -m multiport --dports smtp,465,submission - -j f2b-postfix -``` - ## Running fail2ban in a rootless container [`RootlessKit`][rootless::rootless-kit] is the _fakeroot_ implementation for supporting _rootless mode_ in Docker and Podman. By default RootlessKit uses the [`builtin` port forwarding driver][rootless::port-drivers], which does not propagate source IP addresses. diff --git a/mailserver.env b/mailserver.env index 31497b4c..158fb9a0 100644 --- a/mailserver.env +++ b/mailserver.env @@ -112,7 +112,7 @@ ENABLE_DNSBL=0 # If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`: # cap_add: # - NET_ADMIN -# Otherwise, `iptables` won't be able to ban IPs. +# Otherwise, `nftables` won't be able to ban IPs. ENABLE_FAIL2BAN=0 # Fail2Ban blocktype diff --git a/target/bin/fail2ban b/target/bin/fail2ban index 7e97a73a..f35f2f06 100755 --- a/target/bin/fail2ban +++ b/target/bin/fail2ban @@ -3,26 +3,12 @@ # shellcheck source=../scripts/helpers/index.sh source /usr/local/bin/helpers/index.sh -if ! IPTABLES_OUTPUT=$(iptables -L -n 2>&1) -then - _exit_with_error "IPTables is not functioning correctly - -The output of \`iptables -L\` was: - -${IPTABLES_OUTPUT} - -Possible causes for this error are - -1. Missing capabilities (you need CAP_NET_RAW & CAP_NET_ADMIN, see \`capsh --print\`) -2. Modifications caused by user-patches.sh -3. Host is configured incorrectly -" -fi - function __usage { echo "Usage: ${0} [ ]" ; } unset JAILS declare -a JAILS +IP_REGEXP='((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' + for LIST in $(fail2ban-client status | grep "Jail list" | cut -f2- | sed 's/,/ /g') do JAILS+=("${LIST}") @@ -30,25 +16,22 @@ done if [[ -z ${1} ]] then - - IP_COUNT=0 + IPS_BANNED=0 for JAIL in "${JAILS[@]}" do - BANNED_IP="$(iptables -L "f2b-${JAIL}" -n 2>/dev/null | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -v '0.0.0.0')" + BANNED_IPS=$(fail2ban-client status "${JAIL}" \ + | grep 'Banned IP list' \ + | grep -oE "${IP_REGEXP}") - if [[ -n ${BANNED_IP} ]] + if [[ -n ${BANNED_IPS} ]] then - echo "Banned in ${JAIL}: ${BANNED_IP//$'\n'/, }" - IP_COUNT=$(( IP_COUNT + 1 )) + echo "Banned in ${JAIL}: ${BANNED_IPS//$'\n'/, }" + IPS_BANNED=1 fi done - if [[ ${IP_COUNT} -eq 0 ]] - then - _log 'info' 'No IPs have been banned' - fi - + [[ ${IPS_BANNED} -eq 0 ]] && _log 'info' "No IPs have been banned" else case "${1}" in diff --git a/target/fail2ban/jail.local b/target/fail2ban/jail.local index 496b7581..fcf351c5 100644 --- a/target/fail2ban/jail.local +++ b/target/fail2ban/jail.local @@ -15,10 +15,10 @@ maxretry = 3 # can be defined using space (and/or comma) separator. ignoreip = 127.0.0.1/8 -# Default ban action -# iptables-multiport: block IP only on affected port -# iptables-allports: block IP on all ports -banaction = iptables-allports +# default ban action +# nftables-multiport: block IP only on affected port +# nftables-allports: block IP on all ports +banaction = nftables-allports [dovecot] enabled = true diff --git a/target/scripts/startup/setup-stack.sh b/target/scripts/startup/setup-stack.sh index 6a68b656..e5d03032 100644 --- a/target/scripts/startup/setup-stack.sh +++ b/target/scripts/startup/setup-stack.sh @@ -1144,7 +1144,7 @@ function _setup_fail2ban _log 'debug' 'Setting up Fail2Ban' if [[ ${FAIL2BAN_BLOCKTYPE} != 'reject' ]] then - echo -e '[Init]\nblocktype = DROP' >/etc/fail2ban/action.d/iptables-common.local + echo -e '[Init]\nblocktype = drop' >/etc/fail2ban/action.d/nftables-common.local fi } diff --git a/test/config/fail2ban-jail.cf b/test/config/fail2ban-jail.cf index 3ac4dd98..8d2e9e86 100644 --- a/test/config/fail2ban-jail.cf +++ b/test/config/fail2ban-jail.cf @@ -11,6 +11,6 @@ findtime = 321 maxretry = 2 # Default ban action -# iptables-multiport: block IP only on affected port -# iptables-allports: block IP on all ports -banaction = iptables-multiport +# nftables-multiport: block IP only on affected port +# nftables-allports: block IP on all ports +banaction = nftables-multiport diff --git a/test/mail_fail2ban.bats b/test/mail_fail2ban.bats index bb1c6df8..a2393d3c 100644 --- a/test/mail_fail2ban.bats +++ b/test/mail_fail2ban.bats @@ -64,14 +64,14 @@ function teardown_file() { run docker exec mail_fail2ban /bin/sh -c "fail2ban-client get ${FILTER} maxretry" assert_output 2 - run docker exec mail_fail2ban /bin/sh -c "fail2ban-client -d | grep -F \"['set', 'dovecot', 'addaction', 'iptables-multiport']\"" - assert_output "['set', 'dovecot', 'addaction', 'iptables-multiport']" + run docker exec mail_fail2ban /bin/sh -c "fail2ban-client -d | grep -F \"['set', 'dovecot', 'addaction', 'nftables-multiport']\"" + assert_output "['set', 'dovecot', 'addaction', 'nftables-multiport']" - run docker exec mail_fail2ban /bin/sh -c "fail2ban-client -d | grep -F \"['set', 'postfix', 'addaction', 'iptables-multiport']\"" - assert_output "['set', 'postfix', 'addaction', 'iptables-multiport']" + run docker exec mail_fail2ban /bin/sh -c "fail2ban-client -d | grep -F \"['set', 'postfix', 'addaction', 'nftables-multiport']\"" + assert_output "['set', 'postfix', 'addaction', 'nftables-multiport']" - run docker exec mail_fail2ban /bin/sh -c "fail2ban-client -d | grep -F \"['set', 'postfix-sasl', 'addaction', 'iptables-multiport']\"" - assert_output "['set', 'postfix-sasl', 'addaction', 'iptables-multiport']" + run docker exec mail_fail2ban /bin/sh -c "fail2ban-client -d | grep -F \"['set', 'postfix-sasl', 'addaction', 'nftables-multiport']\"" + assert_output "['set', 'postfix-sasl', 'addaction', 'nftables-multiport']" done } @@ -96,9 +96,9 @@ function teardown_file() { run docker exec mail_fail2ban /bin/sh -c "fail2ban-client status postfix-sasl | grep '${FAIL_AUTH_MAILER_IP}'" assert_success - # Checking that FAIL_AUTH_MAILER_IP is banned by iptables and blocktype set to DROP - run docker exec mail_fail2ban /bin/sh -c "iptables -n -L f2b-postfix-sasl" - assert_output --regexp "DROP.+all.+${FAIL_AUTH_MAILER_IP}" + # Checking that FAIL_AUTH_MAILER_IP is banned by nftables and blocktype set to DROP + run docker exec mail_fail2ban /bin/sh -c "nft list set inet f2b-table addr-set-postfix-sasl 2>/dev/null" + assert_output --regexp "${FAIL_AUTH_MAILER_IP}" } @test "checking fail2ban: unban ip works" { @@ -111,9 +111,9 @@ function teardown_file() { run docker exec mail_fail2ban /bin/sh -c "fail2ban-client status postfix-sasl | grep 'IP list:.*${FAIL_AUTH_MAILER_IP}'" assert_failure - # Checking that FAIL_AUTH_MAILER_IP is unbanned by iptables - run docker exec mail_fail2ban /bin/sh -c "iptables -L f2b-postfix-sasl -n | grep REJECT | grep '${FAIL_AUTH_MAILER_IP}'" - assert_failure + # Checking that FAIL_AUTH_MAILER_IP is unbanned by nftables + run docker exec mail_fail2ban /bin/sh -c "nft list set inet f2b-table addr-set-postfix-sasl 2>/dev/null" + refute_output "${FAIL_AUTH_MAILER_IP}" } # @@ -128,13 +128,15 @@ function teardown_file() { sleep 10 run ./setup.sh -c mail_fail2ban debug fail2ban - assert_output --regexp "^Banned in dovecot: 192.0.66.5, 192.0.66.4.*" + assert_output --partial 'Banned in dovecot:' + assert_output --partial '192.0.66.5' + assert_output --partial '192.0.66.4' run ./setup.sh -c mail_fail2ban debug fail2ban unban 192.0.66.4 assert_output --partial "Unbanned IP from dovecot: 1" run ./setup.sh -c mail_fail2ban debug fail2ban - assert_output --regexp "^Banned in dovecot: 192.0.66.5.*" + assert_output --regexp "^Banned in dovecot:.*192.0.66.5.*" run ./setup.sh -c mail_fail2ban debug fail2ban unban 192.0.66.5 assert_output --partial "Unbanned IP from dovecot: 1"