firewall: replace `iptables` with `nftables` (#2505)

* first adjustments to use Fail2Ban with nftables

* replace `iptables` -> `nftables` and adjust tests

nftables lists IPs a bit differently , so the order was adjusted for the
tests to be more flexible.

* line correction in mailserver.env

* change from `.conf` -> `.local` and remove redundant config

* revert HEREDOC to `echo`

Co-authored-by: Casper <casperklein@users.noreply.github.com>
This commit is contained in:
Georg Lauterbach 2022-04-05 15:13:59 +02:00 committed by GitHub
parent 7c150402a0
commit a9305a073f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 42 additions and 72 deletions

View File

@ -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 ---------
# -----------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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} [<unban> <ip-address>]" ; }
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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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"