本页内容

  • 工作原理;配置 Fail2ban(启用 jail、跟踪日志、故障排查、将自身 IP 地址加入白名单、手动封禁、手动解封、结论)
  • 启用 jail
  • 跟踪日志
  • 故障排查
  • 将自身 IP 地址加入白名单
  • 手动封禁
  • 手动解封
  • 结论

Fail2ban 会监控日志文件中的登录失败记录,并暂时封禁频繁失败的来源 IP 地址,使其无法访问主机。这是一种防御密码猜测暴力破解攻击的手段。在暴露于互联网的主机上部署 Fail2ban 非常有用。

Debian 12 上的 Fail2ban 版本为 1.0.2。

root@posti:~# fail2ban-client version
1.0.2

要查看 Fail2ban 当前是否正在运行,可以使用 fail2ban-client 向服务器发送 ping 命令。如果 Fail2ban 正在运行,服务器会回复 "pong"。

root@posti:/etc/fail2ban# fail2ban-client ping
Server replied: pong
root@posti:/etc/fail2ban#

工作原理

Fail2ban 将过滤器(filter)、动作(action)和受监控的文件组合成一个 jail(监狱)。发行版自带多个 jail,但需要手动启用才能生效。filter 用于定义如何检测认证失败,action 定义了如何执行封禁和解封。

如果在 findtime 时间窗口内,来自同一 IP 地址的登录失败次数达到 maxretry,就会触发封禁。随后该 IP 会被封禁 bantime 秒。封禁持续 bantime 秒后自动解除,该 IP 可再次访问主机。Fail2ban 同时支持 IPv4 和 IPv6。

更多信息可阅读 /usr/share/doc/fail2ban/ 目录下的文件。

配置 Fail2ban

在 Debian GNU/Linux 系统上,Fail2ban 默认安装并启用了 sshd jail,且采用了合理的默认设置。这一配置位于 /etc/fail2ban/jail.d/defaults-debian.conf 文件中。这是默认情况下唯一生效的 jail,其他 jail 必须由系统管理员手动启用。

因此,如果你只需要监控 SSH 登录并封禁行为异常的入侵者,则无需进行额外配置。

遗憾的是,在 Debian 12 上,Fail2ban 可能无法使用默认设置正常工作。Debian 12 将 rsyslog 软件包标记为可选,这意味着它可能不会被安装,日志因此只会被收集到 journald 中 [wiki.debian.org/Rsyslog]。

在 ISPConfig 系统上,Fail2ban 可以正常工作,因为 ISPConfig 自动安装程序会安装 rsyslog,并且 ISPConfig Perfect Server 教程也要求安装 rsyslog。

安装了 rsyslog 后,日志文件会出现在 /var/log/ 目录下,因此 Fail2ban 的默认配置能够找到这些日志文件。如果未安装 rsyslog,则必须修改 Fail2ban 配置,使其从 systemd journal 读取日志。在 jail.local 的 [DEFAULT] 段中添加 backend = systemd,如下所示:

[DEFAULT]
backend = systemd

启用 jail

文档建议将所有自定义修改放在 .local 文件中。这样可以避免在发行版提供的文件被维护者升级或修改时出现冲突 [可阅读文件 /usr/share/doc/fail2ban/README.Debian.gz]。

例如,要启用 pure-ftpd jail,只需在文件 /etc/fail2ban/jail.local 中添加以下内容(如果该文件不存在则先创建):

[pure-ftpd]
enabled = true

方括号中的 jail 名称用于开启该 jail 的配置段。

修改完成后,使用以下命令强制重新读取配置文件:

systemctl reload fail2ban

此时该 jail 应该已经在运行,可以使用 fail2ban-client 进行验证:

root@posti:/etc/fail2ban# fail2ban-client status pure-ftpd
Status for the jail: pure-ftpd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	106
|  `- File list:	/var/log/syslog
`- Actions
   |- Currently banned:	0
   |- Total banned:	4
   `- Banned IP list:	
root@posti:/etc/fail2ban# 

由于 jail.local 中只设置了该 jail 的 "enabled",其余设置均为发行版的默认配置。这些默认值通常都比较合理,因此一般无需进一步配置。如有需要,可在 jail.local 中该 jail 的段落添加覆盖 .conf 文件中设置的项。

以下示例修改了 pure-ftpd jail 的 findtime、maxretry 和 bantime:

[pure-ftpd]
enabled = true
findtime = 2h
maxretry = 6
bantime = 1d

fail2ban-client 以秒为单位显示时间,但在配置文件中可以使用更直观的格式,例如使用 10h 代替 36000 秒。man jail.conf 中的 "TIME ABBREVIATION FORMAT" 章节介绍了这些易用的时间输入格式。

fail2ban-client 提供将易用时间格式转换为秒的选项。

# fail2ban-client --str2sec 1d3h7m
97620

跟踪日志

要确定应启用哪些 jail,需要跟踪主机上运行的服务所产生的日志。像 logwatch 或 pflogsumm 这样能够生成日志摘要的工具会很有帮助。直接阅读原始日志既耗时又枯燥。

查看主机上运行的服务是否已有对应的 jail 可用,可以阅读 jail.conf 文件。

当日志中出现值得关注或令人担忧的内容时,就该深入排查了。例如,pflogsumm 发送的邮件摘要中包含如下内容:

136   unknown[91.224.92.40]: SASL LOGIN authentication failed: UGFzc3...
136   hostname srv-91-224-92-40.serveroffer.net does not resolve to a...
123   unknown[193.32.162.23]: SASL LOGIN authentication failed: UGFzc...
123   hostname mail.whatami.co does not resolve to address 193.32.162.23

这表明 IP 91.224.92.40 在尝试登录邮件服务时失败了 136 次,另外还有一个类似的案例。Fail2ban 本应阻止如此多次的尝试。要查明为何没有生效,需要查看 Fail2ban 的日志。

root@posti:/etc/apt/apt.conf.d# grep 91.224.92.40 /var/log/fail2ban.log | head
2024-02-18 00:01:38,718 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 00:01:38
2024-02-18 00:11:50,261 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 00:11:50
2024-02-18 00:21:54,337 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 00:21:54
2024-02-18 00:32:14,232 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 00:32:14
2024-02-18 00:42:37,921 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 00:42:37
2024-02-18 00:53:06,796 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 00:53:06
2024-02-18 01:03:35,293 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 01:03:35
2024-02-18 01:14:03,765 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 01:14:03
2024-02-18 01:24:24,628 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 01:24:24
2024-02-18 01:34:43,876 fail2ban.filter         [996]: INFO    [postfix-sasl] Found 91.224.92.40 - 2024-02-18 01:34:43
root@posti:/etc/apt/apt.conf.d#

该 IP 大约每隔 10 分钟尝试连接一次,而 Fail2ban 的 postfix-sasl jail 确实检测到了它。

不妨先确认一下该 IP 是否可能属于本机的合法用户——也许只是其智能手机或其他设备仍在使用旧密码,并以固定间隔尝试连接。我使用 geoiplookup 来查看 IP 来自哪个国家。我的用户都来自本国,因此来自国外的用户往往是不怀好意的攻击者。geoiplookup 来自 Debian 软件包 geoip-database 和 geoip-bin。

$ geoiplookup 91.224.92.40
GeoIP Country Edition: LT, Lithuania

要查明为何该 IP 未被封禁,可查看该 jail 的 findtime:

root@posti:/etc/apt/apt.conf.d# fail2ban-client get postfix-sasl findtime
600
root@posti:/etc/apt/apt.conf.d#

我最近遇到了这类缓慢攻击:攻击者知道 findtime 为 10 分钟,于是以更长的间隔尝试登录,从而规避封禁。在本例中这一策略确实奏效了。为了让失败的登录真正触发封禁,可以增大该 jail 的 findtime,例如增大到 10 小时。在 jail.local 文件的 [postfix-sasl] 段中添加:

findtime = 10h

随后在执行 systemctl reload fail2ban 后,该 jail 的 findtime 就变长了:

root@posti:/etc/fail2ban# fail2ban-client get postfix-sasl findtime
36000
root@posti:/etc/fail2ban#

入侵者尝试入侵的另一种方式是坚持不懈。即使被封禁,他们也会等到封禁结束后继续猜测密码。虽然可以延长 bantime,但这会给合法用户带来麻烦——他们可能只是输错了密码,却要在 bantime 结束前都无法访问自己的账户。Fail2ban 提供了一个针对惯犯的 jail,名为 recidive。其工作原理是在 Fail2ban 日志中查找某个 IP 被反复封禁的记录,然后对其执行长时间封禁,例如一周。

以下摘录来自 logwatch 报告:

--------------------- pam_unix Begin ------------------------ 

 sshd:

    Authentication Failures:

       unknown (212.70.149.150): 59 Time(s)

在日志文件中搜索该 IP 显示:

2024-02-21 03:42:39,121 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 03:42:38
2024-02-21 03:42:39,508 fail2ban.actions        [895]: NOTICE  [sshd] Ban 212.70.149.150
2024-02-21 03:52:38,386 fail2ban.actions        [895]: NOTICE  [sshd] Unban 212.70.149.150
2024-02-21 03:54:33,560 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 03:54:33
2024-02-21 03:54:35,364 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 03:54:35
2024-02-21 04:00:37,017 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 04:00:36
2024-02-21 04:00:39,021 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 04:00:38
2024-02-21 04:06:43,036 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 04:06:42
2024-02-21 04:06:45,039 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 04:06:44
2024-02-21 04:06:45,426 fail2ban.actions        [895]: NOTICE  [sshd] Ban 212.70.149.150
2024-02-21 04:16:44,302 fail2ban.actions        [895]: NOTICE  [sshd] Unban 212.70.149.150
2024-02-21 04:19:04,868 fail2ban.filter         [895]: INFO    [sshd] Found 212.70.149.150 - 2024-02-21 04:19:04

Fail2ban 按预期工作,发现了错误的登录尝试并发出了 10 分钟的封禁。在封禁期间,攻击者无法访问本机,但封禁解除后又会卷土重来。这看起来是一次严重的入侵尝试。解决方法是启用 recidive jail,并将 maxretry 设低、bantime 设长。

我倾向于使用较短的初始封禁时间,比如 10 分钟。如果真实用户被封禁,只需等待这段时间,希望他能借此找出正确密码再试。那些在同一天内再次被封禁的用户,会被封禁一周。recidive jail 的默认值可在 jail.conf 文件中找到,其中 bantime 为 1 周、findtime 为 1 天,我觉得基本合理。但我会把 maxretry 设为 2,以便更快地封禁攻击者。我在 jail.local 中添加如下内容:

[recidive]
enabled = true
maxretry = 2

在我看来,超过一周的封禁并不值得。最终,旧的 IP 地址会在各处被封禁或列入黑名单,攻击者便会更换新的 IP。而旧的 IP 会被分配给某个无辜的新互联网用户,不该因为该 IP 前任持有者的恶行而受到惩罚。

故障排查

在操作系统启动或 Fail2ban 重启后,状态会被恢复,因此封禁会持续到 bantime 结束。正因如此,测试和故障排查过程中可能会涉及重启操作。

要测试配置,可以使用 test 选项:

fail2ban-server --test

要查看某个 IP 是否被封禁,较新版本的 Fail2ban 可以执行

# fail2ban-client banned 43.131.9.186
[['recidive']]

该命令会列出当前封禁了指定 IP 的 jail 列表。

较旧版本的 Fail2ban 没有该命令。可以逐个 jail 进行测试,方法如下:

# fail2ban-client status recidive | tr " " "\n" | grep 43.163.219.232
43.163.219.232

如果输出中显示了该 IP,说明它位于被封禁 IP 列表中。其中 tr 命令的作用是将被封禁 IP 列表(它是一整行)拆分为每行一个 IP。

要查看某个 IP 的详细情况,可在 fail2ban.log 中查找:

root@posti:/etc/mysql# grep 43.131.9.186 /var/log/fail2ban.log | cut --characters=-80

2024-03-06 09:00:40,295 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:02:53,954 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:02:55,958 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:04:34,193 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:04:36,195 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:04:36,388 fail2ban.actions [3574846]: NOTICE  [sshd] Ban 43
2024-03-06 09:04:36,626 fail2ban.filter  [3574846]: INFO    [recidive] Fo
2024-03-06 09:14:35,180 fail2ban.actions [3574846]: NOTICE  [sshd] Unban
2024-03-06 09:15:10,073 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:16:55,919 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:16:58,522 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:18:44,972 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:20:30,018 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:20:30,499 fail2ban.actions [3574846]: NOTICE  [sshd] Ban 43
2024-03-06 09:20:30,620 fail2ban.filter  [3574846]: INFO    [recidive] Fo
2024-03-06 09:20:30,899 fail2ban.actions [3574846]: NOTICE  [recidive] Ba
2024-03-06 09:20:32,021 fail2ban.filter  [3574846]: INFO    [sshd] Found
2024-03-06 09:30:29,289 fail2ban.actions [3574846]: NOTICE  [sshd] Unban

以详细模式转储 Fail2ban 配置,可以查看各项设置用于排查:

# fail2ban-client -vvv -d

将自身 IP 地址加入白名单

也许你自己的主机被封禁了,或者你希望确保某些外部主机即使行为异常也永远不会被封禁。这些 IP 地址可以加入白名单。默认情况下没有任何地址被加入白名单,你可以通过以下命令验证:

root@posti:~# fail2ban-client get sshd ignoreip
No IP address/network is ignored
root@posti:~

ignoreip 设置可以放在每个 jail 的段落中,但由于该设置应作用于所有 jail,因此最好将其放在 [DEFAULT] 段中。最理想的做法是将 ignore 设置为内部子网,这样你所有的主机都不会被封禁。以下来自 jail.local 的开头部分:

[DEFAULT]
ignoreip = 92.237.123.96/27

[DEFAULT] 段还可以包含影响所有 jail 的其他设置。例如,可以在其中添加 findtime = 10h。

手动封禁

要测试封禁时会发生什么,可以手动设置封禁。建议先使用一个 bantime 较短的 jail 进行测试,这样即使误封了自己,也能在一段时间后恢复。例如:

# fail2ban-client set sshd banip 8.8.4.4</>

如果某个 IP 行为异常,但你无法让 Fail2ban 检测到它并自动封禁,则可以在 recidive jail 中手动封禁该 IP,从而将其清除一周。

# fail2ban-client set recidive banip 8.8.4.4

使用 CIDR 表示法可以对整个子网进行封禁:

fail2ban-client set recidive banip 5.188.87.0/24

手动解封

解除封禁是可行的,但需要考虑:如果不良行为持续,该 IP 很可能会被再次封禁。因此你应该先查明发生了什么(例如通过阅读日志),并解决导致异常行为的问题。

# fail2ban-client set recidive unbanip 8.8.4.4

该 IP 可能在多个 jail 中被封禁。要解除该 IP 在所有 jail 中的封禁,可使用

# fail2ban-client unban 8.8.4.4

不过我很少需要这样做。我的 bantime 为 10 分钟(recidive jail 除外,其为 1 周),因此我只需从 recidive jail 解除封禁,其他 jail 的封禁早已过期。如果你没有使用 recidive jail,IP 可能会同时在多个 jail 中被封禁,此时该命令就很有用。

要解除所有 jail 中所有 IP 的封禁,可执行

fail2ban-client unban --all

结论

Fail2ban 增加了密码猜测的难度,但并不能完全阻止攻击者尝试访问主机。对于 SSH,强制使用 SSH 密钥可通过禁止密码登录来提供更强的安全保护。对于其他服务,多因素认证可在仅知道密码的情况下阻止登录。