Many security practitioners are familiar with Fail2ban, an application that scans log files for various types of suspicious failures and bans the source IP after too many attempts. Most users implement it to protect their Linux systems (via Netfilter/iptables and TCP wrappers), but it also includes methods for Sendmail and IPFW (FreeBSD and OSX).
What is notably missing from the above list is the wildly popular PF (Packet Filter). It was originally designed by Daniel Hartmeier to replace IPF in OpenBSD, but has since been adopted by FreeBSD, NetBSD, and DragonflyBSD. PF is widely embraced due to the simplicity and clarity of the syntax, and the comprehensive array of professional-grade features available.
Ironically, PF is probably better known now due to FreeBSD than the originating project, OpenBSD. It's somewhat startling that no one has yet included PF support in Fail2ban. It's also disappointing that Apple hasn't switch from IPFW to PF as their packet filtering firewall (hint hint).
In the spirit of the Open Source "submit a patch or GTFO" mentality, here's how you can use Fail2ban to insert rules into your PF firewall.
Now I had a working Fail2ban installation. All I had to do was teach it how to use PF. One of the brilliant features of PF is the ability to use anchors. These are call-outs in your firewall ruleset that allow dynamic loading of rules at that point. Incidentally this is how Authpf is implemented to allow creation of firewall rules based on successful ssh authentication.
Table names are always surrounded by angle brackets, to differentiate them from other objects. The counters keyword tells PF to keep individual traffic counters for each address in the table, rather than just an aggregate number for all the addresses in the table. The next line is fairly self-explanatory (the beauty of PF syntax). It's going to drop any traffic going through PF from a banned address to any address.
The quick keyword tells PF to immediately block the packet, short-cutting the normal behavior which is to traverse the ruleset completely and take the action of the last matching rule (i.e. whatever rule's conditions most recently matched the packet at the end of the ruleset wins). The log keyword tells PF to record it's action in /var/log/pflog, which is a PCAP file that can be read via tcpdump -netttr /var/log/pflog. I encourage increasing the pflogd snaplength to 256 bytes by adding 'pflogd_flags="-s 256"' to /etc/rc.conf.local to facilitate easier debugging.
Note that the table name does not need to match the anchor name. Also note that tables declared inside an anchor are "private" tables, only visible to rules in that anchor. This can be a bit confusing if you're used to using pfctl to view tables. Just remember that you need to use '-a fail2ban' to see any rules and tables specific to the fail2ban anchor when using pfctl.
Now I need to tell Fail2ban how to interact with PF. This is done by creating a template in the fail2ban/action.d directory. I decided to copy the ipfw action template as a basis for customization.
The above action allows me to ban IPs entirely from connecting to this machine. It needs to be used carefully (i.e. only for TCP violations) to avoid an attacker being able to DoS friendly IPs by spoofing malicious packets from them. Speaking of violations, now I need to define what bad actions warrant a ban.
Do that step for any of the fail2ban/filter.d/apache-*.conf files you intend to use.
The very straight-forward syntax means anything matching fail2ban/filter.d/*.conf can be used as a filter = parameter, and anything matching fail2ban/action.d/*.conf can be used as an action. Kudos to the Fail2ban team for making it so easy to plug in actions and filters.
If you want to test your filters, you can use the fail2ban-regex command to specify a log file or log line and a filter.d file or regex to match. It will tell you wether the given filter will match the given input. You should familiarize yourself with the command as it is very useful.
Now for the moment of truth! Time to fire off fail2ban-client start to get things rolling! I haven't written an init script yet, but that should be pretty simple. If you want to check the status of various aspects of Fail2ban's running state, check out the arguments to fail2ban-client. In general, any fail2ban- command when supplied without arguments will give you a usage message.
Last, if you want to check the status of PF to see what IPs are blocked, or whether the rules are loaded, use the commands below.
Really good article, however I couldn't get PF table rule "counter" to work on my FreeBSD since we have older version of PF in our ports. Do you know any work around for this?
Hello Mihail, which version of FreeBSD? I was under the impression that the latest version is sync'd to the OpenBSD version periodically. I've tweaked my config a bit since this article, so it's due for an update.
FreeBSD 8.2-RELEASE. I believe this version of FreeBSD is using old 4.6 version of PF so there is no counter option available :(.
tnx for post it. it didn't solve my problem but it's nice point to undestand better pf and fail 2 ban. code section "white" on "light grey" is a bit unreadable but firebug solve it.