FreeBSD and blacklistd

What is blacklistd(8)?

Its similar to fail2ban, but built-in to FreeBSD. Can we integrate it with our favorite firewall (pf(8))? Of course! What about sshd(5) support? Already handled. What if we want to do the opposite of blacklist? Can we whitelist something? Yes! Lets see how:

blacklistd setup

Start by configuring sshd(8) with blacklistd(8) support. This is just adding a flag to the start options.

sudo sysrc sshd_flags="-o UseBlacklist=yes"
sudo service sshd restart

If pf isn’t already enabled, take a look at the pf primer. Enable pf (don’t start it yet) with

sudo sysrc pf_enable="YES"
sudo sysrc pflog_enable="YES"

If you haven’t defined your external interface in pf.conf(8), do that. In addition, at the top of your block rules, add the anchor line for blacklistd(8). Use the correct interface.

ExtIf="lagg0"
anchor "blacklistd/*" in on $ExtIf

Time to start pf. If you’re connected via ssh(8), you’ll get disconnected. You may wish to run the commands under tmux(1). Make sure you have a way into the host in case your firewall rules aren’t configured properly.

sudo service pf start
sudo service pflog start

Hopefully everything worked properly when you started pf. If you are already running pf, you can simply issue

sudo service pf reload

We want to keep block rules in place after a reboot (these hosts were blocked for a reason, right?). Use the -r option for blacklistd_flags.

sudo sysrc blacklistd_enable="YES"
sudo sysrc blacklistd_flags="-r"

We can now configure blacklistd(8). By default, it looks like this

# $FreeBSD: releng/11.1/etc/blacklistd.conf 301226 2016-06-02 19:06:04Z lidl $
#
# Blacklist rule
# adr/mask:port                 type    proto   owner           name    nfail   disable
[local]
ssh                             stream  *       *               *       3       24h
ftp                             stream  *       *               *       3       24h
smtp                            stream  *       *               *       3       24h
submission                      stream  *       *               *       3       24h
#6161                           stream  tcp6    christos        *       2       10m
*                               *       *       *               *       3       60

# adr/mask:port type            proto   owner           name    nfail   disable
[remote]
#129.168.0.0/16                 *       *       *               =       *       *
#6161                           =       =       =               =/24    =       =
#*                              stream  tcp     *               =       =       =

The two most important things to keep straight are [local] and [remote]. [local] stanza entries are for services you are trying to protect (ssh, ftp, smtp, etc). [remote] stanza entries are for how you wish to treat blocks of IPs (for IPv4, /32 or bigger, for IPv6, /128 or bigger).

Once you’ve decided which you want to use, you can look at the fields that you will need to fill in. This is a similar structure to the inetd.conf(8) structure.

The notably missing example is IPv6 (why is IPv6 always forgotten in the examples?). If you want to do an IPv6 entry, it looks like this:

[remote]
[2001:db8:101:ca7::]/56:ssh   *       *       *       =       *       *

Now that you have blacklistd(8) configured, lets start it up

sudo service blacklistd start

Check the blocked hosts list

You can use the command blacklistctl dump to check what host is blocked. I use two options to show everything for IPv4 and IPv6:

sudo blacklistctl dump -bw
                                address/ma:port id      nfail   last access
							 10.10.2.20/32:22   OK      3/3     2018/05/13 01:59:32
                        192.168.123.234/32:22   OK      3/3     2018/05/13 01:51:34
                 [2001:db8:101:ca7::1/128]:22   OK      6/3     2018/05/13 02:35:15

Whitelist

We covered blacklisting something. What about whitelisting? We can do that under the [remote] section with something like

# location      type    proto   owner   name    nfail   duration
[remote]
10.10.2.20:ssh  *       *       *       *       *       *

This means that, for the ssh port (22), always allow the IP 10.10.2.20.

You could do the same thing for a service by moving the entry to the [local] section, and removing the IP. This would make it such that any IP could connect to the ssh port and it would never get blocked by blacklistd. That would look like this

# adr/mask:port                 type    proto   owner           name    nfail   disable
[local]
ssh                             stream  *       *               *		*       *

Postfix Support

Recently (Feb, 2018), Postfix got patched to support blacklistd(8). No config changes necessary, just enable support when you build Postfix.

If you want to examine the entries related to postfix, you can run a loop over the external mail ports:

for p in 25 465 587 ; do
    pfctl -a blacklistd/${p} -sr -v 2> /dev/null
done

You should get output that looks similar to this

block drop in quick proto tcp from <port587> to any port = submission
  [ Evaluations: 2638      Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 1240 State Creations: 0     ]

Lastly, if you need to delete an entry, it would look something like this

pfctl -a blacklistd/${port} -t port${port} -T delete ${ip}

Footnotes and References