31.6. 黑名单

Blacklistd 是侦听套接字的守护程序,用于接收来自其他守护程序的有关失败或已成功的连接尝试的通知。它最广泛地用于阻止开放端口上的太多连接尝试。一个主要的例子是在互联网上运行的 SSH 从试图猜测密码并获得访问权限的机器人或脚本中获取大量请求。使用黑名单,守护程序可以通知防火墙创建过滤规则,以阻止在多次尝试后从单个源进行过多的连接尝试。Blacklistd 最初是在 NetBSD 上开发的,并在第 7 版中出现。FreeBSD 11 从 NetBSD 导入黑名单。

本章介绍如何设置黑名单、配置黑名单,并提供如何使用黑名单的示例。读者应熟悉规则等基本防火墙概念。有关详细信息,请参阅防火墙章节。示例中使用了 PF,但 FreeBSD 上提供的其他防火墙也应该能够使用黑名单。

31.6.1. 启用黑名单

黑名单的主要配置存储在 blacklistd.conf(5)open in new window 中。各种命令行选项也可用于更改黑名单的运行时行为。跨重新启动的持久配置应存储在 /etc/blacklistd.conf 中。要在系统引导期间启用守护程序,请在 /etc/rc.conf 中添加一行 blacklistd_enable,如下所示:

# sysrc blacklistd_enable=yes

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

# service blacklistd start

31.6.2. 创建列入黑名单的规则集

黑名单的规则在 blacklistd.conf(5)open in new window 中配置,每行一个条目。每个规则都包含一个由空格或制表符分隔的元组。规则属于localremote,分别适用于运行黑名单的计算机或外部源。

31.6.2.1. 本地规则

本地规则的示例 blacklistd.conf 条目如下所示:

[local]
ssh             stream  *       *               *       3       24h

该部分后面的所有规则都被视为本地规则 [local](这是默认规则),并应用于本地计算机。当遇到 [remote] 节时,它后面的所有规则都被作为远程机器规则处理。

七个字段定义由制表符或空格分隔的规则。前四个字段标识应列入黑名单的流量。后面的三个字段定义了 backlistd 的行为。通配符表示为星号(*),匹配此字段中的任何内容。第一个字段定义位置。在本地规则中,这些是网络端口。位置字段的语法如下:

[address|interface][/mask][:port]

地址可以指定为数字格式的 IPv4 或方括号中的 IPv6。也可以使用接口名称em0

套接字类型由第二个字段定义。TCP 套接字的类型为 stream,而 UDP 表示为 dgram。上面的示例使用 TCP,因为 SSH 使用该协议。

协议可用于列入黑名单的规则的第三个字段。可以使用以下协议:tcpudptcp6udp6 或数字。通配符(如示例中所示)通常用于匹配所有协议,除非有理由通过某个协议来区分流量。

在第四个字段中,定义了报告事件的守护程序进程的有效用户或所有者。用户名或 UID 可以在此处使用,也可以使用通配符(请参阅上面的示例规则)。

数据包筛选器规则名称由第五个字段声明,该字段启动规则的行为部分。默认情况下,blacklistd 将所有块放在pf.conf 中的一个名为 blacklistd 的 pf 锚点下,如下所示:

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

对于单独的阻止列表,可以在此字段中使用锚点名称。在其他情况下,通配符就足够了。当名称以连字符(-)开头时,表示应使用前面附加了默认规则名称的锚点。上面使用连字符的修改示例如下所示:

ssh             stream  *       *               -ssh       3       24h

使用这样的规则,任何新的阻止列表规则都将添加到名为 blacklistd-ssh 的锚点中。

为了阻止整个子网对单一规则的违反,可以在规则名称中使用一个 /。这导致名称的其余部分被解释为应用于规则中指定的地址的掩码。例如,这条规则将阻止与 /24 相邻的每个地址。

22              stream  tcp       *               */24    3       24h

注意

在这里指定适当的协议是很重要的。IPv4 和 IPv6 对 /24 的处理方式不同,这就是为什么不能在此规则的第三个字段使用 * 的原因。

此规则定义,如果该网络中的任何一台主机行为异常,则该网络上的其他所有内容也将被阻止。

第六个字段名为 nfail,设置将相关远程 IP 列入黑名单所需的登录失败次数。当在此位置使用通配符时,这意味着块将永远不会发生。在上面的示例规则中,定义了三个限制,这意味着在一个连接上三次尝试登录 SSH 后,IP 将被阻止。

列入黑名单的规则定义中的最后一个字段指定主机被列入黑名单的时间长度。默认单位为秒,后缀也可以分别指定为分钟 m、小时 h 和天 d

这个例子的规则整体上意味着,在三次验证 SSH 后,将导致该主机的新PF块规则。规则的匹配是通过首先一个接一个地检查本地规则,从最具体到最不具体。当匹配发生时,将应用 remote 规则,并由匹配的 remote 规则更改 name、nfail 和 disable 字段。

31.6.2.2. 远程规则

远程规则用于指定黑名单如何根据当前正在评估的远程主机更改其行为。远程规则中的每个字段都与本地规则中的字段相同。唯一的区别在于黑名单使用它们的方式。为了解释它,使用了以下示例规则:

[remote]
203.0.113.128/25 *      *       *               =/25    =       48h

地址字段可以是 IP 地址(v4 或 v6)、端口或两者。这允许为特定的远程地址范围设置特殊规则,如本例所示。类型、协议和所有者字段的解释与本地规则中的字段相同。

但是,名称字段是不同的:远程规则中的等号(=)告诉 blacklistd 使用匹配的本地规则中的值。这意味着将采用防火墙规则条目并添加前缀 /25(的网络掩码255.255.255.128)。当来自该地址范围的连接被列入阻止列表时,整个子网都会受到影响。此处也可以使用 PF 锚点名称,在这种情况下,黑名单会将此地址块的规则添加到该名称的锚点。指定通配符时使用默认表。

nfail 列中可以为一个地址定义一个自定义的失败次数。这对特定规则的例外情况很有用,也许可以允许某人不那么严格地应用规则,或者在登录尝试中更宽松一点。当在这第六个字段中使用星号时,阻断功能将被禁用。

与来自本地网络(如办公室)的尝试相比,远程规则允许对登录尝试实施更严格的限制。

31.6.3. 列入黑名单的客户端配置

FreeBSD 中有一些软件包可以利用黑名单的功能。两个最突出的是 ftpd(8)open in new windowsshd(8)open in new window,用于阻止过多的连接尝试。要激活 SSH 守护程序中的黑名单,请将以下行添加到 /etc/ssh/sshd_config

UseBlacklist yes

之后重新启动 sshd 以使这些更改生效。

ftpd(8)open in new window 的黑名单使用 -B 来启用,或者在 /etc/inetd.conf 中,或者作为 /etc/rc.conf 中的一个标志,像这样:

ftpd_flags="-B"

这就是使这些程序与黑名单对话所需的全部内容。

31.6.4. 黑名单管理

Blacklistd 为用户提供了一个名为 blacklistctl(8)open in new window 的管理实用程序。它显示被阻止的地址和网络,这些地址和网络被 blacklistd.conf(5)open in new window 中定义的规则列入了阻止列表。要查看当前被阻止的主机列表,请像这样结合使用 dump-b

# blacklistctl dump -b
      address/ma:port id      nfail   last access
213.0.123.128/25:22   OK      6/3     2019/06/08 14:30:19

这个例子显示,在端口 22 上有 6 次允许的尝试,这些尝试来自地址范围213.0.123.128/25。列出的尝试次数多于允许的次数,因为 SSH 允许客户端在一个 TCP 连接上尝试多次登录。目前正在进行的连接不会被 blacklistd 阻止。最后的连接尝试被列在输出的 last access 列中。

要查看此主机将在黑名单上的剩余时间,请添加 -r 到上一个命令中。

# blacklistctl dump -br
      address/ma:port id      nfail   remaining time
213.0.123.128/25:22   OK      6/3     36s

在此示例中,还剩下 36 秒,直到此主机不再被阻止。

31.6.5. 从阻止列表中删除主机

有时,有必要在剩余时间到期之前将一个主机从封锁名单中删除。不幸的是,blacklistd 中没有这样的功能。不过,可以用 pfctl 把地址从 PF 表中删除。对于每个被封锁的端口,在**/etc/pf.conf** 中定义的 blacklistd 锚内都有一个子锚。例如,如果有一个封锁端口 22 的子锚,它就被称为 blacklistd/22。在这个子锚里面有一个表,包含了被封的地址。这个表的名字是 port,后面是端口号。在这个例子中,它被称为 port22。有了这些信息,现在就可以用 pfctl(8)open in new window 来显示所有列出的地址,如下所示:

# pfctl -a blacklistd/22 -t port22 -T show
...
213.0.123.128/25
...

从列表中确定要取消阻止的地址后,以下命令会将其从列表中删除:

# pfctl -a blacklistd/22 -t port22 -T delete 213.0.123.128/25

该地址现已从 PF 中删除,但仍将显示在 blacklistctl 列表中,因为它不知道在 PF 中所做的任何更改。黑名单数据库中的条目最终将过期,并最终从其输出中删除。如果主机再次与列入黑名单的阻止规则之一匹配,则将再次添加该条目。