Enforcing Fail2ban bans with PF

Posted by Karl Levik on 2023-08-06, last modified on 2023-08-06
daemon jailing bad IPs from auth.log

Bad IPs go to ... not Hell, but at least to jail!

In the process of configuring my FreeBSD VPS, the time had finally come to attempt configuring Fail2ban1,2 properly. I already had it up and running, but the bans weren't actually being enforced because - to my surprise - it was trying to use iptables, which is a Linux firewall that doesn't even exist for FreeBSD!

PF configuration

Instead, I have a PF3,4 firewall, which is part of the FreeBSD base system. So, the simplest and most obvious solution would be to make use of this.

I added the following lines in /etc/pf.conf:

table <f2b> persist
anchor "f2b/*"
block drop in log quick on $vtnet0 from <f2b> to any  

This creates a table, f2b, for banned IP addresses, an anchor4 and a rule which will block any incoming requests on $vtnet0 (a variable which represents my network interface) from IP addresses found in the table.

Note that the default Fail2ban configuration expects this particular table name (i.e. f2b), so it's easiest to stick with that name.

Fail2ban configuration

To feed the table, we need to configure Fail2ban.

By default, there was a jail bsd-ssh-pf defined and enabled in /usr/local/etc/fail2ban/jail.d/bsd-ssh-pf.conf.

We can overload settings for this in /usr/local/etc/fail2ban/jail.local, e.g:

banaction = pf[actiontype=<allports>]
banaction_allports = pf[actiontype=<allports>]

findtime = 3600
maxretry = 2

(This file is also a good place to enable other jails (enabled = true), e.g. postfix and dovecot for a mail server)

We can reload the PF config file and restart Fail2ban with:

pfctl -f /etc/pf.conf
fail2ban-client reload --restart --all


If you haven't already enabled Fail2ban and PF in your rc.conf file, here are some lines:

pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_flags=""                  # additional flags for pflogd startup

Useful Fail2ban and PF commands

Once that has been running for a while, we can see which IP addresses have ended up in our jails with:

fail2ban-client banned

We can also compare that to the IP addresses in our PF tables:

pfctl -a 'f2b/bsd-ssh-pf' -t 'f2b-bsd-ssh-pf' -Ts

We can find several more pfctl commands inside /usr/local/etc/fail2ban/action.d/pf.conf as Fail2ban is using pfctl to communicate with PF and manage the bans.

A few fairly self-explanatory per-jail fail2ban-client commands:

fail2ban-client status bsd-ssh-pf
fail2ban-client get bsd-ssh-pf banned
fail2ban-client get bsd-ssh-pf maxretry
fail2ban-client get bsd-ssh-pf findtime
fail2ban-client get bsd-ssh-pf bantime
fail2ban-client get bsd-ssh-pf logpath

We can also set configuration variables this way, on-the-fly, without reloading the configuration like we did in a previous section. See the fail2ban-client man page5 for more info.

See also