OpenBSD: Block spammers/abusive IP addresses with Pf-badhost. A ‘must have’ security tool!

Written by: Özgür Kazanççı -

Category: My OpenBSD Write-Ups

Updated to: v0.5!
I’d like to introduce pf-badhost, by Jordan Geoghegan.

Pf-badhost is a very practical, robust, stable and lightweight security script for network servers.

It’s compatible with BSD based operating systems such as {Open,Free,Net,Dragonfly}BSD and MacOS. It prevents potentially-bad IP addresses that could possibly attack your servers (and waste your bandwidth and fill your logfiles), by blocking all those IPs contacting your server, and therefore it makes your server network/resources lighter and the logs of important services running on your server become simpler, more readable and efficient.

But how does it do all this?

By periodically pulling IP addresses from well-known and well-respected spammer-IPs databases, where bad IP addresses are frequently logged (dangerous IPs reported by Internet users) and stored.

Then, adding all collected IP addresses to the PF firewall (as an IP-table) that is already active on your server (hopefully?), and through that way, prevents their access to your server. So sort of works with the PF firewall.

The blocklists are pulled from quality, trusted sources. The ‘Spamhaus’, ‘Firehol’, ‘Emerging Threats’ and ‘Binary Defense’ block lists are used as they are popular, regularly updated lists of the Internet’s most egregious offenders.

You can also configure cron to send an email containing the output from each pf-badhost run, which includes statistics such as number of blocked/added/removed IP addresses as well as list totals for each IP address family. Here’s an example pf-badhost statistics report from one of my servers:

11728 addresses added.
5926 addresses deleted.
IPv4 addresses in table:  620584460

620,584,460 IPs! crazy thing!

Since I know this script, I upload and activate it on every server I set up. And I can easily say that my headaches decreased – servers’ performance&safety increased. 🙂

Shortly; For you – For your safety. You must have it!

Installation guide on OpenBSD, in 10 simple steps:

1- Create a new user on your system: (“_pfbadhost” as example). The user should be created with default shell of “nologin“, home folder set to /var/empty/ with no password specified (logins disabled).

# useradd -s /sbin/nologin -d /var/empty _pfbadhost

2- Download pf-badhost:

$ ftp

3- Install script with appropriate permissions:

# install -m 755 -o root -g bin /usr/local/bin/pf-badhost

4- Create required files:

# install -m 640 -o _pfbadhost -g wheel /dev/null /etc/pf-badhost.txt
# install -d -m 755 -o root -g wheel /var/log/pf-badhost
# install -m 640 -o _pfbadhost -g wheel /dev/null /var/log/pf-badhost/pf-badhost.log
# install -m 640 -o _pfbadhost -g wheel /dev/null /var/log/pf-badhost/pf-badhost.log.0.gz

5- Give user “_pfbadhost” strict doas permission for the exact commands the script needs run as superuser. NOTE: Unlike ‘sudo’, _ALL_ users must be explicitly granted permission to use doas, even the root user. (Create /etc/doas.conf if doesn’t exist)

$ cat /etc/doas.conf
permit root
permit nopass _pfbadhost cmd /sbin/pfctl args -nf /etc/pf.conf
permit nopass _pfbadhost cmd /sbin/pfctl args -t pfbadhost -T replace -f /etc/pf-badhost.txt

# Optional rule for authlog scanning
permit nopass _pfbadhost cmd /usr/bin/zcat args -f /var/log/authlog /var/log/authlog.0.gz

6- Add the following lines to your PF’s configuration file located usually in /etc/pf.conf: (Putting it higher-up/earlier in the ruleset is recommended)

table <pfbadhost> persist file "/etc/pf-badhost.txt"
block in quick on egress from <pfbadhost>
block out quick on egress to <pfbadhost>

The lines above in pf.conf do the following things;
1) Create a table titled “pfbadhost”
2) Link the table to the contents (IP addresses) in /etc/pf-badhosts.txt
3) Block both incoming/outgoing traffics coming from those IPs to your server, through egress (‘egress’ contains the interface(s) that holds the default route(s))

7- Run pfbadhost as user “_pfbadhost” using the “-O openbsd” argument:

# doas -u _pfbadhost pf-badhost -O openbsd

8- Reload your pf rule set:

# pfctl -f /etc/pf.conf

9- For good measure, run the script once more:

# doas -u _pfbadhost pf-badhost -O openbsd

10- Edit _pfbadhost user’s crontab to run pf-badhost every night at midnight:

# crontab -u _pfbadhost -e
~ 0~1 * * * -s pf-badhost -O openbsd

Yay! pf-badhost is now installed! With the nightly cron job, the list will be regularly updated with the latest known bad hosts.

Some useful PF commands to manage the PF’s table “pfbadhost”:

-List the table contents (List of blocked IPs – get ready for a huge list):
pfctl -t pfbadhost -T show

-Manually add IPs into the table pfbadhost:
pfctl -t pfbadhost -T add
pfctl -t pfbadhost -T add

-Delete IPs:
pfctl -t pfbadhost -T delete

-Scan IPs inside the table (check if you block a specific IP address):
pfctl -t pfbadhost -T test

Post Scriptum:

To enable additional options such as IPv6, subnet aggregation, geoblocking, bogon filtering or authlog scanning;
open ‘/usr/local/bin/‘ with your text editor of choice and scroll down to around line 400 to the “User Configuration Area“.
From there, you can enable options by setting their value to “1“.

Your comments/thoughts?

Leave a Reply