Skip to content
FreeBSDDeep Dive Published Updated 4 min readViews unavailable

FreeBSD 15.1 Blocklistd: Turn Service Failures into Temporary Firewall Blocks

Connect SSH failure reports to expiring FreeBSD firewall entries, while accounting for the blocklistd rename, PF integration, allowlists, and safe verification.

FreeBSD’s blocklist service is best understood as a bridge between an application that can report an adverse event and a packet filter that can act on that report. It does not inspect every packet, decide whether an SSH password is valid, or terminate an already established connection. An enabled client such as sshd reports attempts; blocklistd matches those reports against policy; its helper installs and later removes a firewall rule.

This guide uses the FreeBSD 15.1 names: blocklistd, blocklistd.conf, and blocklistctl. The 15.1 release notes describe the older blacklist* utilities as deprecated and renamed. Earlier systems may still use those legacy names, so check the installed release’s manual pages and rc scripts before copying commands or renaming a configuration file.

Verify the whole path before changing policy

The flow has separate gates:

  1. The service must report the relevant event. For SSH, enable UseBlocklist in sshd_config.
  2. blocklistd must be running and receive that notification.
  3. A local rule in blocklistd.conf must match the service, socket type, protocol, and reporting user. A remote rule can then adjust the threshold, duration, or exception.
  4. The packet-filter helper must be able to add a rule to the configured filter. This example uses PF.

If any gate is missing, a counter or log line by itself does not prove that a source address is blocked. First confirm the machine is fully patched. FreeBSD’s 15.0 advisory SA-26:03 documents a blocklistd socket leak in 15.0 and identifies the corrected 15.0-RELEASE update; operators on that release should follow the advisory rather than treating service enablement as a substitute for patching.

Enable SSH notifications and the daemon

In /etc/ssh/sshd_config, opt SSH into reporting:

UseBlocklist yes

Validate the SSH configuration before reloading it:

sshd -t
service sshd reload

Persist and start the 15.1 daemon:

sysrc blocklistd_enable=yes
service blocklistd start
service blocklistd status

If SSH is your only administrative path, keep an out-of-band console or another tested access route before changing SSH or firewall policy. A successful service command only establishes that the rc script ran; it does not prove that PF received a usable dynamic rule.

Start with a narrow local rule

The following is a deliberately small SSH policy for /etc/blocklistd.conf:

[local]
ssh     stream  *       *       *       4       15m

[remote]
198.51.100.25:ssh       *       *       *       *       *       *

The seven columns are location, socket type, protocol, reporting process owner, packet-filter rule name, failure threshold, and block duration. stream matches TCP sockets; the wildcards leave protocol, owner, and rule name at their defaults. The threshold here is four reported failures and the entry expires after fifteen minutes. The documentation-only address in [remote] is a placeholder: replace it with an address you deliberately want exempted, or omit the entire section. Remote exceptions are especially important for fixed administrator addresses, but should be reviewed so an exception does not unintentionally cover a larger network.

Do not start with subnet blocking. A /24 or /64 rule can deny unrelated users behind a NAT gateway, VPN, carrier network, or shared office address. The remote-rule mechanism is powerful enough to change the mask and duration, so keep the initial policy to individual source addresses until you have evidence that broader blocking is safe.

Make PF accept the dynamic anchor

The Handbook’s PF example loads the generated rules beneath a dedicated anchor. Adapt the interface variable to the existing ruleset; do not replace a working production ruleset with this fragment:

anchor "blocklistd/*" in on $ext_if
block in
pass out

The matching anchor is where the helper can add the temporary entries. Validate PF syntax first:

pfctl -nf /etc/pf.conf

Only reload a validated ruleset through the approved change window, particularly on a remote host. A syntax check is read-only; loading a ruleset changes live filtering and can cut off management traffic if the surrounding policy is wrong.

Verify behavior without guessing

Use the state database view to see active blocks and their remaining time:

blocklistctl dump -br

The output is evidence about blocklistd’s own state, not a complete firewall audit. The FreeBSD Handbook notes that one SSH TCP connection may generate more failed logins than the threshold before the client is blocked, and that an already-running connection is not stopped by the new rule. If the daemon has entries but packets still pass, inspect the PF anchor and table as well as the active ruleset; do not raise the threshold or duration until you know which stage failed.

Treat this as a small, expiring abuse-control mechanism, not a substitute for key-based SSH, rate controls, network segmentation, or upstream DDoS protection. Address reputation can also be misleading: repeated failures from one shared IP may represent several legitimate users rather than one attacker. Record the threshold, exception list, change window, and a rollback path, and review the blocked table after rollout.

Related:

Sources:

Comments