Skip to content

18.6 blocklistd

blocklistd 是一个守护进程:它通过套接字监听,接收其他守护进程发送的连接成功或失败通知,主要用于封锁发起过多连接尝试的来源地址。典型的例子是互联网上运行的 SSH 收到大量来自机器人或脚本的请求,这些请求试图猜测密码来获取访问权限。使用 blocklistd 后,守护进程可通知防火墙,在单个来源地址尝试多次后创建过滤规则,以封锁过多的连接尝试。

blocklistd 最初在 NetBSD 上开发,并于 NetBSD 7 发布,最初名为 blacklistd,后更名为 blocklistd。自 FreeBSD 11 起从 NetBSD 引入。

本节介绍了 blocklistd 的使用方法,并提供了使用示例。读者应熟悉基本的防火墙概念。

18.6.1 启用 blocklistd

要在系统启动时启用该守护进程,执行以下命令:

sh
# service blocklistd enable

要手动启动服务,运行以下命令:

sh
# service blocklistd start

18.6.2 创建 blocklistd 规则集

blocklistd 规则集位于 /etc/blocklistd.conf 文件,每行填写一条规则。每条规则由空格或制表符分隔的七个字段组成。规则分为 localremote,分别作用于 blocklistd 所在的本机和外部来源。

18.6.2.1 本地规则

默认配置文件 /etc/blocklistd.conf 内容如下:

ini
# 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 中的默认本地规则如下所示:

ini
[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:porttypeprotoowner:前四个字段用于标识需列入封禁列表的流量。
    • adr/mask:port 字段:定义地址。在本地规则中,该字段是网络端口。

      adr/mask:port 地址字段的语法:

      sh
      [地址|接口][/掩码][:端口]

      adr/mask:port 地址字段可按数字格式指定为 IPv4 或 IPv6(以方括号表示),也可使用接口名称,如 em0

    • type 字段:定义套接字类型。TCP 套接字为 stream 类型,而 UDP 则用 dgram 表示。因为 SSH 基于 TCP 协议,上例使用的是 stream

    • proto 字段:定义协议。可用协议包括 tcpudptcp6udp6 或数值形式的协议号。除非有必要按特定协议区分流量,否则通常如示例所示使用通配符来匹配所有协议。

    • owner 字段:定义报告事件的守护进程的有效用户或属主。守护进程报告事件时,以此字段标识其身份。这里可以使用用户名或 UID,也可以使用通配符。

  • namenfailduration:后三个字段定义 blocklistd 的行为。
    • name 字段:定义数据包过滤规则名称,标志着规则行为部分的开始。 如果需要单独的封锁列表,可以在此字段中使用锚点名称。在其他情况下,使用通配符即可。名称以连字符(-)开头时,表示应使用带有默认规则名称的锚点。修改后的示例如下:

      sh
      ssh             stream  *       *               -ssh       3       24h

      在此规则下,新的封禁规则将添加到锚点 blocklistd-ssh 中。

      若因某 IP 违反某条规则而需封锁整个子网,可以在规则名称中使用 /。这样名称中 / 后面的部分会当作掩码,应用到规则中指定的地址。例如,以下这条规则会封锁与指定地址相邻的所有 /24 网段的地址。

      sh
      22              stream  tcp       *               */24    3       24h

      注意

      这里需指定正确的协议。IPv4 和 IPv6 对 /24 的处理不同,因此在此规则的第三个字段中不能使用 *

      此规则定义:若该网络中任何主机行为不当,则整个网络中的其他主机均将受到封禁。

    • nfail 字段:设置需多少次登录失败方可将远程 IP 列入封禁列表。若在此位置使用通配符,则永不触发封禁。在上例中,定义了 3 次登录失败的限制,即在 SSH 上登录失败 3 次后,该 IP 被封禁。

  • duration 字段:指定主机列入封禁列表的时间。默认单位为秒,亦可指定 mhd 等后缀,分别表示分钟、小时和天。

完整的示例规则 ssh stream * * * 3 24h 表示:SSH 验证失败三次后,该主机将生成一条新的 PF 封锁规则。规则匹配时,首先按顺序检查 local 规则,从最具体(匹配条件最严格、范围最小的规则)到最不具体(匹配条件宽泛、范围大的规则)。匹配成功后,会应用 remote 规则,并由匹配的 remote 规则修改字段 namenfailduration

18.6.2.2 远程规则

remote 规则用于指定 blocklistd 如何根据当前评估的远程主机调整其行为。remote 规则中的每个字段与 local 规则相同,唯一的区别在于 blocklistd 使用这些字段的方式不同。以下用示例规则说明:

ini
# 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 文件中添加以下行:

ini
UseBlocklist yes

然后重新启动 sshd 使更改生效。

对于 Port ftp/freebsd-ftpd,可通过 -B 参数启用 blocklist 功能。既可以在 /etc/inetd.conf 中设置该参数,也可以像下面这样作为标志写入 /etc/rc.conf

ini
ftpd_flags="-B"

以上即为让这些程序与 blocklistd 通信所需的全部配置。

18.6.4 启用 PF 防火墙

blocklistd 只是一个监控的守护进程,本身并无封禁能力,需依赖防火墙才能封禁。可以参考其他章节安装并启用 PF 防火墙。示例中使用了 PF,但 FreeBSD 上其他可用防火墙也可与 blocklistd 配合使用。

blocklistd 会将所有封禁规则放入 /etc/pf.conf 文件中的 PF 锚点 blocklistd 下。 因此,需要将以下内容写入 PF 防火墙配置文件 /etc/pf.conf 中:

ini
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) 中定义的规则所封禁的地址和网络。

要查看当前封禁的主机列表,可使用以下命令:

sh
# 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 参数。

sh
# 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) 来显示列出的所有地址:

sh
# pfctl -a blocklistd/22 -t port22 -T show
192.168.179.1

从列表中识别出需解除封禁的地址后,使用以下命令将其从列表中移除:

sh
# pfctl -a blocklistd/22 -t port22 -T delete 192.168.179.1

blocklistd 无法感知 PF 中的更改,即使已从 PF 中移除该地址,该地址仍会出现在 blocklistctl 列表中。blocklistd 数据库中的该条目最终会过期,并从输出中移除。

若该主机再次匹配 blocklistd 中的某条封禁规则,则此主机的对应条目将再次被添加。