How to set up Fail2ban with nftables and AbuseIPDB

Return to posts

Post by Ozelot on 29/06/2025 (last updated on 10/08/2026)

Tags: GuideSecurity

Intro

If you're running a Linux server, protecting it from brute-force attacks is one of the first things worth setting up - and Fail2ban is the usual answer.

There's one catch on modern Debian, though. Since Debian 12, iptables is no longer installed by default, and Fail2ban's default action expects it. Without a firewall backend to talk to, Fail2ban will not be able to block any IPs - it will be useless with default configuration.

You can check it yourself:

fail2ban-client set sshd banip 198.51.100.42
nft list ruleset | grep 198.51.100.42
fail2ban-client set sshd unbanip 198.51.100.42

If nft returns nothing, the ban never reached the firewall.

In this guide we'll set up Fail2ban with nftables - the framework Debian already ships - and add automatic reporting of blocked IPs to AbuseIPDB.

Why nftables when I have iptables?

You have two ways out of this. Install iptables and keep the legacy setup, or point Fail2ban at the firewall your system is already running.

nftables has been the default framework since Debian 10. It unifies IPv4 and IPv6 filtering, replaces the four separate legacy tools with one, and sits underneath your system whether you use it directly or not. Installing iptables on top of it means adding a compatibility layer to work around a problem that doesn't need working around.

Of course, you can go ahead and install iptables, but in this guide we will focus on the cleaner and default choice for modern Debian.

Configuration

You have to install fail2ban. You can use apt or whatever:

apt install fail2ban

Set up jail

You need to make some changes in /etc/fail2ban/jail.conf.

You can adjust bantime, findtime, and maxretry to your needs, or leave the default values, which are usually sufficient.

Then, find the [sshd] section. You have to make them look like this:

[sshd]

port = ssh
backend = systemd
action = nftables[name=SSH,port=ssh,protocol=tcp]
         abuseipdb[abuseipdb_category="18,22"]

Or if you don't want to use AbuseIPDB, leave the last line like this:

action = nftables[name=SSH,port=ssh,protocol=tcp]

Now fail2ban will use nftables to ban malicious addresses.

Set up AbuseIPDB

Create or edit /etc/fail2ban/action.d/abuseipdb.conf:

[Definition]
actionban = lgm=$(printf '%%.1000s' "<matches>"); curl -sSf "https://api.abuseipdb.com/api/v2/report" \
  -H "Accept: application/json" \
  -H "Key: <abuseipdb_apikey>" \
  --data-urlencode "comment=$lgm" \
  --data-urlencode "ip=<ip>" \
  --data "categories=<abuseipdb_category>"

[Init]
abuseipdb_apikey = "your-abuseipdb-apikey-here"

You can generate your API key after logging into your AbuseIPDB account: AbuseIPDB -> User Account -> API.

Other services

The [sshd] jail is just one example. The same approach works with any service that logs failed login attempts.

There's one catch if the service is behind a reverse proxy or Cloudflare: nftables only sees the proxy's IP, so the attacker's address never gets blocked. For that setup, I covered a different approach in setting up Fail2ban for Portainer via Cloudflare, where bans are handled by Cloudflare instead.

Test your configuration

Finally, restart Fail2ban:

systemctl restart fail2ban

Then be sure to check the status:

systemctl status fail2ban

Check that the jail itself is running:

fail2ban-client status sshd

And confirm that bans actually reach the firewall:

nft list ruleset | grep f2b

If the Fail2ban chain is there, everything is wired up correctly. Fail2ban will block IPs that exceed allowed attempts via nftables, and report them to AbuseIPDB if configured.

keep calm and stay cute~

What do you think about it?

LoaderLoading comments...