18.6 blocklistd
blocklistd 是一个守护进程:它通过套接字监听,接收其他守护进程发送的连接成功或失败通知,主要用于封锁发起过多连接尝试的来源地址。典型的例子是互联网上运行的 SSH 收到大量来自机器人或脚本的请求,这些请求试图猜测密码来获取访问权限。使用 blocklistd 后,守护进程可通知防火墙,在单个来源地址尝试多次后创建过滤规则,以封锁过多的连接尝试。
blocklistd 最初在 NetBSD 上开发,并于 NetBSD 7 发布,最初名为 blacklistd,后更名为 blocklistd。自 FreeBSD 11 起从 NetBSD 引入。
本节介绍了 blocklistd 的使用方法,并提供了使用示例。读者应熟悉基本的防火墙概念。
18.6.1 启用 blocklistd
要在系统启动时启用该守护进程,执行以下命令:
# service blocklistd enable要手动启动服务,运行以下命令:
# service blocklistd start18.6.2 创建 blocklistd 规则集
blocklistd 规则集位于 /etc/blocklistd.conf 文件,每行填写一条规则。每条规则由空格或制表符分隔的七个字段组成。规则分为 local 和 remote,分别作用于 blocklistd 所在的本机和外部来源。
18.6.2.1 本地规则
默认配置文件 /etc/blocklistd.conf 内容如下:
# Blocklist rule
# adr/mask:port type proto owner name nfail duration
[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 duration
[remote]
#129.168.0.0/16 * * * = * *
#[2001:db8::]/32:ssh * * * = * *
#6161 = = = =/24 = =
#* stream tcp * = = =文件 /etc/blocklistd.conf 中的默认本地规则如下所示:
[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[local] 段后的所有规则均视为本地规则(此为默认设置),适用于本机。遇到 [remote] 段后,后续规则将作为远程规则处理。通配符由星号 (*) 表示,匹配该字段中的任意内容。
七个字段共同定义一条规则,字段之间以制表符或空格分隔:
adr/mask:port、type、proto、owner:前四个字段用于标识需列入封禁列表的流量。adr/mask:port字段:定义地址。在本地规则中,该字段是网络端口。adr/mask:port地址字段的语法:sh[地址|接口][/掩码][:端口]adr/mask:port地址字段可按数字格式指定为 IPv4 或 IPv6(以方括号表示),也可使用接口名称,如em0。type字段:定义套接字类型。TCP 套接字为stream类型,而 UDP 则用dgram表示。因为 SSH 基于 TCP 协议,上例使用的是stream。proto字段:定义协议。可用协议包括tcp、udp、tcp6、udp6或数值形式的协议号。除非有必要按特定协议区分流量,否则通常如示例所示使用通配符来匹配所有协议。owner字段:定义报告事件的守护进程的有效用户或属主。守护进程报告事件时,以此字段标识其身份。这里可以使用用户名或 UID,也可以使用通配符。
name、nfail、duration:后三个字段定义 blocklistd 的行为。name字段:定义数据包过滤规则名称,标志着规则行为部分的开始。 如果需要单独的封锁列表,可以在此字段中使用锚点名称。在其他情况下,使用通配符即可。名称以连字符(-)开头时,表示应使用带有默认规则名称的锚点。修改后的示例如下:shssh stream * * -ssh 3 24h在此规则下,新的封禁规则将添加到锚点
blocklistd-ssh中。若因某 IP 违反某条规则而需封锁整个子网,可以在规则名称中使用 /。这样名称中 / 后面的部分会当作掩码,应用到规则中指定的地址。例如,以下这条规则会封锁与指定地址相邻的所有 /24 网段的地址。
sh22 stream tcp * */24 3 24h注意
这里需指定正确的协议。IPv4 和 IPv6 对 /24 的处理不同,因此在此规则的第三个字段中不能使用
*。此规则定义:若该网络中任何主机行为不当,则整个网络中的其他主机均将受到封禁。
nfail字段:设置需多少次登录失败方可将远程 IP 列入封禁列表。若在此位置使用通配符,则永不触发封禁。在上例中,定义了 3 次登录失败的限制,即在 SSH 上登录失败 3 次后,该 IP 被封禁。
duration字段:指定主机列入封禁列表的时间。默认单位为秒,亦可指定m、h、d等后缀,分别表示分钟、小时和天。
完整的示例规则 ssh stream * * * 3 24h 表示:SSH 验证失败三次后,该主机将生成一条新的 PF 封锁规则。规则匹配时,首先按顺序检查 local 规则,从最具体(匹配条件最严格、范围最小的规则)到最不具体(匹配条件宽泛、范围大的规则)。匹配成功后,会应用 remote 规则,并由匹配的 remote 规则修改字段 name、nfail 和 duration。
18.6.2.2 远程规则
remote 规则用于指定 blocklistd 如何根据当前评估的远程主机调整其行为。remote 规则中的每个字段与 local 规则相同,唯一的区别在于 blocklistd 使用这些字段的方式不同。以下用示例规则说明:
# adr/mask:port type proto owner name nfail duration
[remote]
#129.168.0.0/16 * * * = * *
#[2001:db8::]/32:ssh * * * = * *
#6161 = = = =/24 = =
#* stream tcp * = = =地址字段可为 IP 地址(IPv4 或 IPv6)、端口,或二者兼有。这样可以为特定的远程地址范围设置特殊规则(如本例所示)。套接字类型、协议和属主字段的解释与 local 规则完全相同。
name 字段有所不同:在 remote 规则中,等号(=)表示 blocklistd 使用匹配的 local 规则中的值。也就是说,防火墙规则条目会沿用匹配的 local 规则中的值,并加上 /16 前缀(掩码为 255.255.0.0)。该地址范围内的任一连接被封锁时,整个子网均会受到影响。此处也可使用 PF 锚点名称,此时 blocklistd 会将该地址块的规则添加到该锚点下。若使用通配符,则使用默认表格。
nfail 列可为某个地址定义自定义失败次数。这在设置特定规则的例外时很有用,比如给予某些用户较宽松的登录尝试机会。第六个字段使用星号时,封禁功能即告禁用。
与来自本地网络(如办公室)的登录尝试相比,remote 规则可以施加更严格的限制。
18.6.3 配置 blocklistd 客户端
在 FreeBSD 中,有一些软件包可以利用 blocklistd 的功能。最常用的是 ftpd(8) 和 sshd(8),可阻止过多的连接尝试。
要为 SSH 守护进程启用 blocklistd,在 /etc/ssh/sshd_config 文件中添加以下行:
UseBlocklist yes然后重新启动 sshd 使更改生效。
对于 Port ftp/freebsd-ftpd,可通过 -B 参数启用 blocklist 功能。既可以在 /etc/inetd.conf 中设置该参数,也可以像下面这样作为标志写入 /etc/rc.conf:
ftpd_flags="-B"以上即为让这些程序与 blocklistd 通信所需的全部配置。
18.6.4 启用 PF 防火墙
blocklistd 只是一个监控的守护进程,本身并无封禁能力,需依赖防火墙才能封禁。可以参考其他章节安装并启用 PF 防火墙。示例中使用了 PF,但 FreeBSD 上其他可用防火墙也可与 blocklistd 配合使用。
blocklistd 会将所有封禁规则放入 /etc/pf.conf 文件中的 PF 锚点 blocklistd 下。 因此,需要将以下内容写入 PF 防火墙配置文件 /etc/pf.conf 中:
ext_if="em0" # 需替换为实际的网卡接口
table <blocklistd> persist
anchor "blocklistd/*"
block drop in log quick on $ext_if from <blocklistd> to any
pass out all随后启用 PF 防火墙。
18.6.5 管理 blocklistd
blocklistd 提供了管理工具 blocklistctl(8),用于查看 blocklistd.conf(5) 中定义的规则所封禁的地址和网络。
要查看当前封禁的主机列表,可使用以下命令:
# blocklistctl dump -b
rulename address/ma:port id nfail last access
blocklistd 192.168.179.32/32:22 OK 3/3 2026/04/16 12:33:43此示例显示,从地址范围 192.168.179.32/32 发起的端口 22 登录尝试已达到 3 次上限。由于 SSH 允许客户端在单条 TCP 连接上进行多次登录尝试,实际尝试次数可能略超配置的 nfail 值。输出中的“last access”列显示了最后一次连接尝试的时间。
技巧
测试时可先将配置文件 /etc/blocklistd.conf 保存到其他位置,再新建空的配置文件,写入
* * * * * 3 180(对任意端口的任意连接,超过三次即封禁 180 秒)。另外,blocklistd 不会中断当前正在进行的连接,因此测试前需断开相关连接。 切勿在生产环境执行此测试!
要查看该主机在封锁列表上剩余的时间,可在之前的命令中添加 -r 参数。
# blocklistctl dump -br
rulename address/ma:port id nfail remaining time
blocklistd 192.168.179.32/32:22 OK 3/3 2m13在此示例中,距离该主机解除封锁还有 2 分 13 秒。
18.6.6 从 blocklistd 中移除主机
有时需在剩余时间重置前将主机从封锁列表中移出。但 blocklistd 本身并未提供此功能。
不过,可以使用 pfctl 命令从 PF 表中移除该地址。每个封禁端口在 /etc/pf.conf 的 blocklistd 锚点下都有一个子锚点。例如,用于封锁端口 22 的子锚点是 blocklistd/22。该子锚点内有一个包含所有封禁地址的表,表名为 port 加上端口号,在此例中为 port22。
有了这些信息,就可以使用 pfctl(8) 来显示列出的所有地址:
# pfctl -a blocklistd/22 -t port22 -T show
192.168.179.1从列表中识别出需解除封禁的地址后,使用以下命令将其从列表中移除:
# pfctl -a blocklistd/22 -t port22 -T delete 192.168.179.1blocklistd 无法感知 PF 中的更改,即使已从 PF 中移除该地址,该地址仍会出现在 blocklistctl 列表中。blocklistd 数据库中的该条目最终会过期,并从输出中移除。
若该主机再次匹配 blocklistd 中的某条封禁规则,则此主机的对应条目将再次被添加。