The Complete Guide to Blacklistd on FreeBSD

Intro

Blacklistd was created to address the problem that every network service is potentially a target for attack, but each responds to threats in its own, unique way. Blacklistd provides a standard way for these services to report that they are under attack. The service maintains the state of each potential attack, and configures a local firewall to block or permit the offending IP addresses.

Blacklistd is one of those ideas that is so simple and so good that you wonder why no one thought of it earlier, and why it is not more popular. Unfortunately, it has not seen a lot of traction. To me, it is one of those “killer features” that makes FreeBSD my first choice for secure systems.

Terminology

For the purposes of this article, I will define the following terms:

  • Service: the blacklistd service
  • Client: any executable that sends notifications to blacklistd. These would be daemons that accept network connections. I realize we usually call these things “services”, but from the perspective of blacklistd, they are clients.

Setup

The main configuration for blacklistd is stored in blacklistd.conf(5). The documentation is pretty good on this file, so I will not try to reproduce it here. I will, however, add one explanatory note that new users will find helpful.

A typical configuration file for blacklistd might look like this:

# Blacklist rule
# adr/mask:port type    proto   owner           name    nfail   disable
[local]
ssh             stream  *       *               *       3       24h
smtp            stream  *       *               *       3       24h
submission      stream  *       *               *       3       24h
*               *       *       *               *       3       60s

That first column is called the location field and is made up of two parts.

  1. Address
  2. Port

It can contain either an address or a port or both, with the address and port separated by a colon.

The address part can be any of the following:

  1. An IPv4 address in numeric format
  2. An IPv4 address and mask in numeric format
  3. An IPv6 address in numeric format, enclosed by square brackets
  4. An IPv6 address and mask in numeric format, enclosed by square brackets
  5. An interface name

The port part can be one of the following:

  1. Port number
  2. Port name, as found in /etc/services

The example above uses exclusively port names. I thought for a long time that these were daemon names, or maybe some unique identifier that the client was passing to blacklistd. Nope. In fact, the clients don’t actually identify themselves to blacklistd at all, so the configuration file only specifies the details of a network connection you want protected, in this case, port names.

To enable the daemon during system boot, add a blacklistd_enable line to /etc/rc.conf like this:

# sysrc blacklistd_enable=yes

To maintain block rules after a reboot, set the -r option in blacklistd_flags:

# sysrc blacklistd_flags="-r"

To start the service manually, run this command:

# service blacklistd start

Using PF

By default, blacklistd puts all blocks under a pf anchor called blacklistd. To enable PF to block the addresses that blacklistd says should be blocked, add the following line to your PF rules.

anchor "blacklistd/*" in on $ext_if

Using IPFW

The configuration for using blacklistd with IPFW does not appear to be documented anywhere. The sparse information I have came from two sources:

Configure the ‘ipfw’ packet filtering on the system, by adding the the following entries to /etc/rc.conf:

firewall_enable="YES"
firewall_quiet="YES"
firewall_type="workstation"
firewall_allowservices="any"
firewall_myservices="ssh smtp http https"

Create the sentinal file detected by the blacklistd-helper daemon to enable ipfw packet filtering commands. Use a non-default offset value, as the default rules for the “workstation” configuration end above rule 2000.

echo "ipfw_offset=4000" > /etc/ipfw-blacklist.rc

The ipfw system needs to have rule numbers, and the blacklistd-helper script defaults to 2000+port_number for each of the rules that it inserts.

It creates a named table (eg: port22) and then just inserts the bad actors into that table, and creates a rule like the following for ssh:

ipfw -q add 2022 drop tcp from table(port22) to any dst-port 22

The filtering rules get added as needed, and IP addresses get inserted/deleted into the tables as needed. The rules are never deleted, but it should be low-cost to check an empty table.

If you have followed the general rules of having the ultimate deny/pass rules near the end of the 64K range of rules, you’ll be OK by just touching the /etc/ipfw-blacklist.rc file to turn it on. The file is sourced by the invoking shell, so if you need a different offset than 2000 for the rules, you can do something like:

echo 'ipfw_offset=4000' > /etc/ipfw-blacklist.rc

Disclaimer: I have never used IPFW, so hopefully someone more knowledgeable on IPFW can make use of these resources.

Client Configuration

As far as I know, this is the only complete list of FreeBSD software with support for blacklistd in the universe. Please send me a note if I have missed anything.

OpenSSH

To activate blacklistd in the OpenSSH daemon, add the following line to /etc/ssh/sshd_config:

UseBlacklist yes

Or include sshd_flags in /etc/rc.conf like this:

# sysrc sshd_flags="-o UseBlacklist=yes"

Restart sshd afterwards to make these changes take effect.

FTP

Blacklisting for ftpd is enabled using -B, either in /etc/inetd.conf or as a flag in /etc/rc.conf like this:

# sysrc ftpd_flags="-B"

Postfix

Support for blacklistd was added to Postfix back in 2018, and it has been included by default since 2020. It will report SASL authentication failures to blacklistd.

Be sure to enable a policy in /etc/blacklistd.conf for any Postfix services for which you have enabled SASL authentication. This would typically be submission and/or submissions

Sendmail

Only the mail/sendmail port includes support for blacklistd - not the sendmail included in the base system. To enable it, set the UseBlacklist flag in in /etc/rc.conf

# sysrc sendmail_flags="-O UseBlacklist"

bozotic HTTP server

The bozotic HTTP server (bozohttpd) includes support for blacklistd enabled by default. Nothing is needed to activate it.

INN (InterNetNews)

INN (InterNetNews) includes support for blacklistd enabled by default. Nothing is needed to activate it.

Operations

Show Blocked IP Addresses

Show Using blacklistctl

Show blocked addresses:

# blacklistctl dump -br
        address/ma:port id      nfail   remaining time
150.241.102.195/32:22   OK      3/3     11h12m5s

Show addresses with some failed logons, but not yet blocked:

# blacklistctl dump -r
        address/ma:port id      nfail   remaining time
 69.121.199.203/32:22           1/3     5h29m13s
 188.32.247.157/32:22           1/3     8h9m21s
 103.250.149.90/32:22           1/3     9h12m41s

Show in PF table

# pfctl -a blacklistd/22 -t port22 -Ts
   75.111.103.146
   84.216.164.177
   91.74.98.113
   105.73.203.35
   188.43.232.65
   191.36.157.227

Re-enable a Blocked IP Address

Blacklistd does not have a mechanism to unblock an address. That must be done by interacting with your firewall directly.

Using PF

# pfctl -a blacklistd/22 -t port22 -T delete ${ip-address}

The address is now removed from PF, but will still show up in the blacklistctl list, since it does not know about any changes made in PF. The entry in blacklistd’s database will eventually expire and be removed from its output. The entry will be added again if the host matches one of the block rules again.

Conclusion

I’ve tried to compile everything I know about running blacklistd on FreeBSD in this article. Let me know if I’ve missed anything!

For more information, check out this excellent presentation by the creator of blacklistd, Christos Zoulas.