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.
- Address
- 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:
- An IPv4 address in numeric format
- An IPv4 address and mask in numeric format
- An IPv6 address in numeric format, enclosed by square brackets
- An IPv6 address and mask in numeric format, enclosed by square brackets
- An interface name
The port part can be one of the following:
- Port number
- 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:
- A posting from FreeBSD developer Kurt Lidl who passed away in 2019 (may he rest in peace). His developer home page is no longer listed on https://people.freebsd.org, so I will duplicate his instructions here for posterity.
- Reading the /usr/libexec/blacklistd-helper script.
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.