System: Using systemd to bind fail2ban to nftables
In the past we've used an init script to initialise our firewalls, orginally using iptables, and more recently with nftables.
This involved an init script including code to start and stop Fail2Ban when the firewall was stopped an started. But this is not the systemd way...
Configuring Fail2Ban to start/stop with nftables
What we're trying to achieve is the following:
- on boot, the server starts first nftables.service and then fail2ban.service;
- if we start nftables.service, it should also start fail2ban.service;
- if we stop nftables.service, it should also stop fail2ban.service;
- if we restart nftables.service, it should also restart fail2ban.service;
- if we start fail2ban.service, it should also start nftables.service;
- stop and restart actions on fail2ban.service should not affect nftables.service.
We can do all this by creating an override file for fail2ban telling it that it depends on nftables, and than nftables wants it to start:
/etc/systemd/system/fail2ban.service.d/override.conf
[Unit]
Requires=nftables.service
PartOf=nftables.service
[Install]
WantedBy=multi-user.target nftables.service
Because we've modified the [Install] section, we need to re-enable the affected service in order for the relevant symlinks to be created. We're also making sure that nftables is installed as a service unit first:
# systemctl enable nftables.service
Created symlink /etc/systemd/system/sysinit.target.wants/nftables.service → /lib/systemd/system/nftables.service.
# systemctl enable fail2ban.service
Created symlink /etc/systemd/system/nftables.service.wants/fail2ban.service → /lib/systemd/system/fail2ban.service.
# systemctl daemon-reload
The 'Created symlink' output with nftables.service.wants tells us that our changes have worked and that nftables now 'wants' fail2ban to be started when it does.
How does it work exactly?
In our override file, or full configuration file (see below), we include the following settings:
[Unit]
- Requires=nftables.service
- starting this service will first start nftables.service.
- PartOf=nftables.service
- causes this unit to stop or restart (but not start) with nftables.service.
[Install]
- WantedBy=multi-user.target nftables.service
- causes this unit to start when any of the listed services are started.
As you can see we need to set Requires, PartOf and WantedBy to get the desired behaviour. You can find more details under References below.
Alternative configuration approach
Systemd keeps its main configuration files under /usr/lib/systemd/system/* and these should not be edited. Local configuration can instead be set up under /etc/systemd/system/* where it will take precedence over the default configuration on a per-file or per-service basis.
In the above example we've created an 'override' configuration file for Fail2Ban binding it to nftables. This takes the original configuration and just adds or replaces the lines that appear in the override. This way an APT upgrade can still affect other settings.
An alternative is to create a whole new configuration file, which can be done using:
# systemctl edit --full fail2ban.service
This will open /usr/lib/systemd/system/fail2ban.service in a text editor which, when saved, will create an override file /etc/systemd/system/fail2ban.service. In this case any APT upgrade cannot affect your configuration.
Confirming that it's working
Starting with both nftables and Fail2Ban running:
# systemctl list-units --type=service | egrep fail2ban\|nftables
fail2ban.service loaded active running Fail2Ban Service
nftables.service loaded active exited nftables
Stopping nftables:
# systemctl stop nftables.service
causes both services to disappear:
# systemctl list-units --type=service | egrep fail2ban\|nftable
(no output)
And starting Fail2Ban causes them to reappear as before:
# systemctl start fail2ban.service
We can also stop and start fail2ban without affecting nftables, assuming it's already active.
Building an nftables firewall
Sorry, but we're not going into any detail here. The point of the nftables service unit is to run the contents of /etc/nftables.conf when nftables is started, and to flush the ruleset when it's stopped.
You can find examples under /usr/share/doc/nftables/examples/.
Running another script when firewall starts
The /etc/nftables.conf file lets you define a static firewall to be brought up and taken down with the nftables.service unit, but in many situations you will want additional scripts to run to insert dynamic rules based on DNS lookups or other non-static variables.
Having a script run at boot time is simple enough, using CRON, and you can insert a delay sufficient to allow the firewall to come up first:
@reboot root sleep 10 && /usr/sbin/nft insert rule inet filter ...
But this only runs after a server reboot and not after a systemd action that restarts nftables.service. We can address this by instead creating another override file:
/etc/systemd/system/nftables.service.d/override.conf
[Service]
ExecStartPost=/bin/sh -c '/usr/sbin/nft insert rule inet filter ...'
After creating this file, don't forget to run:
# systemctl daemon-reload
And then you can:
# systemctl restart nftables.service
There are other options for running scripts before the firewall starts rather than after. See under References below for details.
Gotchas
An update in Fail2Ban 0.11.2-3 appears to have moved the systemd fail2ban.service file from /lib/systemd/system/ to /usr/lib/systemd/system/ breaking some of the above sym-links.
References
- nftables wiki
- Missing nftables dependency in unitfile
- Fail2ban doesn't work with Nftables on Debian 10
- Getting a service started automatically when another gets started
- systemd.unit(5) — Linux manual page
- systemd.service options
Related Articles - Fail2Ban
- System Monitoring the fail2ban log
- System Optimising your Fail2Ban filters
- System Fail2Ban 0.8.3 Howto
- System Using a Fail2Ban Jail to Whitelist a User
- System Implementing Port Knocking with knockd
- System Blocking FTP Hacking Attempts
- System fail2ban and sendmail
- System Using systemd to bind fail2ban to nftables
- System fail2ban and iptables
L. Foerster 3 November, 2022
Why not just easily and simply edit the jail.local file?!
See:
www.ruhnke.cloud/server/debian/fail2ban-von-iptables-zu-nftables/
Nathan Bennett 16 March, 2021
This outlines how to set up the fail2ban and nftables services to be dependent on each other, but how do you get fail2ban to actually pass IP addresses to be blocked by nftables?
The short answer is that you just replace 'iptables' with 'nftables' where it appears in the Fail2Ban configuration files.
In particular, in "jail.conf:
banaction = nftables-multiport
banaction_allports = nftables-allports