Ubuntu Fail2ban 从原理到实战全指南

在 Linux 服务器运维中,SSH 暴力破解是最常见的安全威胁之一。攻击者通过自动化工具对常见用户名和密码进行大规模尝试,一旦猜中弱密码,服务器的控制权就会落入攻击者手中。

Fail2ban 是一款入侵防御工具,通过监控系统日志(如 SSH 认证日志),自动识别暴力破解行为并将攻击源 IP 加入防火墙黑名单,从而在攻击发生时实时阻断威胁。

Fail2ban 的核心优势:

  • 自动化防御:无需人工干预,实时监控日志并封禁恶意 IP。
  • 逐级惩罚:支持递增封禁时长,让反复攻击的 IP 付出更大代价。
  • 跨服务统计:通过 recidive 监狱跨 jail 累计封禁次数,识别惯犯并长期封禁。
  • 灵活配置:支持白名单、自定义封禁策略和多种日志后端(systemd / 文本文件)。

本文将从 Fail2ban 的工作原理、配置文件结构、逐步构建生产配置、OpenSSH 9.8+ 兼容处理、常见场景与故障排除等方面,帮助你完整掌握 Fail2ban 的部署与优化。


目录


1. 引言:什么是 Fail2ban

Fail2ban 是一个用 Python 编写的入侵防御框架,工作原理如下:

系统日志 → Fail2ban 扫描 → 匹配 filter 规则 → 达到阈值 → 触发 action → iptables/nftables 封禁 IP

1.1 工作流程示例

以 SSH 暴力破解为例:

  1. 攻击者从 IP 1.2.3.4 连续 6 次输错密码
  2. SSH 服务将失败记录写入 journald(或 /var/log/auth.log
  3. Fail2ban 的 sshd jail 扫描日志,通过正则表达式提取失败记录
  4. 在 30 分钟内累计 5 次失败 → 触发封禁
  5. Fail2ban 调用 iptables 将 1.2.3.4 加入黑名单
  6. 封禁持续 10 分钟后自动解除

1.2 与其他防御方式的对比

防御方式实时性自动化跨服务复杂度
手动 iptables延迟
SSH 内置限流
Fail2ban
云 WAF低(需付费)

Fail2ban 的优势在于开源免费、跨服务统计、支持逐级惩罚,适合中小型服务器部署。


2. 安装 Fail2ban

2.1 检查是否已安装

fail2ban-client --version

若输出版本号(如 Fail2Ban v1.0.2),则已安装。若提示 command not found,需手动安装。

2.2 在 Ubuntu 上安装

sudo apt update && sudo apt install fail2ban -y

2.3 启动并设置开机自启

sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban

预期输出包含 active (running)


3. 核心概念与配置文件结构

3.1 核心概念

术语说明
jail(监狱)一组规则的集合,监控某个服务(如 sshd)。每个 jail 包含 filter + action + 参数
filter(过滤器)正则表达式,用于从日志中识别失败尝试
action(动作)触发封禁后执行的操作,通常是调用 iptables 封禁 IP
bantime封禁持续时长,支持 10m(10分钟)、1h(1小时)、1w(1周)等格式
findtime观察窗口,在此时间内超过 maxretry 次才触发封禁
maxretry触发封禁的最大失败次数
ignoreip白名单 IP,永不封禁
backend日志获取方式,可选 systemd(journald)、auto(文本文件)等

3.2 配置文件目录结构

/etc/fail2ban/
├── fail2ban.conf          # 主程序配置(日志级别、socket 路径等)
├── fail2ban.d/            # 主程序配置覆盖目录
├── jail.conf              # 内置 jail 默认配置(系统维护,勿修改)
├── jail.d/                # jail 配置覆盖目录
│   ├── defaults-debian.conf   # Debian/Ubuntu 预设(仅启用 sshd)
│   └── custom.local           # 自定义配置(推荐在此创建)
├── filter.d/              # 各服务的 filter 正则规则
│   ├── sshd.conf              # sshd filter 默认规则
│   └── sshd.local             # sshd filter 覆盖(可选)
└── action.d/              # 各种封禁 action 脚本
    └── iptables-multiport.conf  # iptables 多端口封禁动作

3.3 配置加载优先级

jail.conf → jail.d/*.conf(按文件名字母顺序)→ jail.d/*.local

后加载的配置会覆盖先加载的配置。所有自定义配置应写入 jail.d/*.local 文件,系统升级时不会被覆盖。

3.4 filter 变量覆盖机制

Fail2ban 允许在 jail 配置中通过 filter = <filter_name>[var1=value1, var2=value2] 覆盖 filter 内部变量,无需修改 filter.d/ 下的文件。例如:

[sshd]
filter = sshd[_daemon=sshd(?:-session)?]

这会覆盖 filter.d/sshd.conf 中的 _daemon 变量,使其同时匹配 sshdsshd-session 进程名。


4. 从零构建生产配置

本节将从系统默认状态出发,逐步构建一份适用于生产环境的完整配置。每一步都会解释为什么这么做。

4.1 第一步:查看系统默认配置

apt 安装 Fail2ban 后,Debian/Ubuntu 自动启用了 sshd jail:

cat /etc/fail2ban/jail.d/defaults-debian.conf

输出:

[sshd]
enabled = true

这份文件只做了一件事:启用 sshd jail。其余参数(bantime、findtime、maxretry 等)全部继承自 /etc/fail2ban/jail.conf 的默认值。

4.2 第二步:创建自定义配置文件

sudo vim /etc/fail2ban/jail.d/custom.local

4.3 第三步:设置基础参数

custom.local 中写入 [DEFAULT] 节,这些参数会被所有 jail 继承:

[DEFAULT]
# 初次封禁时长
bantime  = 10m
 
# 统计窗口(30 分钟内)
findtime = 30m
 
# 最多允许 5 次失败尝试
maxretry = 5
 
# 白名单:本机回环 + IPv6 回环
ignoreip = 127.0.0.1/8 ::1

参数含义串联:任何一个不在白名单中的 IP,如果在 30 分钟内失败超过 5 次,就封禁 10 分钟。

4.4 第四步:使用变量管理白名单

不同项目的服务器管理 IP 可能不同,用变量管理更灵活:

[DEFAULT]
# 自定义白名单变量(换项目时只需修改这一行)
project_management_ips = 10.0.0.1
 
bantime  = 10m
findtime = 30m
maxretry = 5
 
# 引用变量(%(变量名)s 是 Fail2ban 的变量展开语法)
ignoreip = 127.0.0.1/8 ::1 %(project_management_ips)s

变量会被自动展开,等同于 ignoreip = 127.0.0.1/8 ::1 10.0.0.1

4.5 第五步:启用逐级惩罚

默认的封禁策略是「每次违规都封固定时长」。逐级惩罚让攻击者每次被封后,下次封禁时长翻倍:

[DEFAULT]
project_management_ips = 10.0.0.1
 
bantime  = 10m
findtime = 30m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 %(project_management_ips)s
 
# 开启递增封禁
bantime.increment = true
 
# 每次被封后,下次封禁时长乘以该系数
bantime.factor    = 2
 
# 封禁时长上限(防止无限增长)
bantime.maxtime   = 24w
 
# 每次额外附加 0~10 分钟随机时长,防攻击者预测解封时间
bantime.rndtime   = 10m

封禁时长演进示例

第 N 次被封基础时长加随机后实际范围
第 1 次10 分钟10~20 分钟
第 2 次20 分钟20~30 分钟
第 3 次40 分钟40~50 分钟
第 4 次80 分钟80~90 分钟
第 5 次160 分钟 ≈ 2.7 小时2.7~2.8 小时
直至上限 24 周

4.6 第六步:配置 sshd jail

[DEFAULT] 节后追加 [sshd] 节:

[sshd]
enabled      = true
port         = 22
backend      = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service
filter       = sshd[_daemon=sshd(?:-session)?]
banaction    = iptables-multiport
bantime.overalljails = true

逐行说明

  • enabled = true:显式启用(虽然 defaults-debian.conf 已启用,但显式声明更清晰)
  • port = 22:SSH 实际端口(非 22 需同步修改)
  • backend = systemd:从 journald 读取日志(Ubuntu 22.04+ 推荐)
  • journalmatch = _SYSTEMD_UNIT=ssh.service:精准订阅 SSH 服务的 journald 日志源
  • filter = sshd[_daemon=sshd(?:-session)?]:兼容 OpenSSH 9.8+ 的 sshd-session 进程名(详见第 5 节)
  • banaction = iptables-multiport:使用 iptables 多端口封禁动作
  • bantime.overalljails = true:让 sshd 的封禁记录参与 recidive 统计

4.7 第七步:架设 recidive 惯犯监狱

recidive 不监控业务日志,而是监控 Fail2ban 自身日志。只要一个 IP 在短时间内被任何 jail 多次封禁,就视为惯犯并长期封禁:

[recidive]
enabled     = true
backend     = auto
filter      = recidive
logpath     = /var/log/fail2ban.log
findtime    = 1d
maxretry    = 3
bantime     = 12w
ignoreip    = 127.0.0.1/8 ::1 %(project_management_ips)s

关键点

  • backend = auto:必须显式设置,否则会继承 [DEFAULT]backend = systemd(若存在),导致 recidive 错误地去 journald 找日志
  • findtime = 1d:1 天内被任何 jail 封禁 3 次视为惯犯
  • bantime = 12w:惯犯封禁 12 周(约 3 个月)

为什么需要 recidive?前面 [DEFAULT] 中的逐级惩罚需要同一个 IP 在同一个 jail 中反复被封才会升级。如果攻击者打一枪换一个地方——SSH 被封了就换 HTTP,每次在新服务上都是「初犯」。recidive 跨 jail 累计封禁次数,堵住了这个漏洞。


5. OpenSSH 9.8+ sshd-session 兼容处理

5.1 问题背景

OpenSSH 9.8(2024-07-01 发布)将服务端拆分为两个独立二进制:

  • sshd:监听进程,处理新连接
  • sshd-session:会话进程,处理单个会话的认证和通信

影响:日志中的进程名从 sshd 变为 sshd-session,而 Fail2ban 默认 filter 只识别 sshd,导致无法匹配日志。

5.2 诊断方法

如果攻击者多次输错密码,但 fail2ban-client status sshdTotal failed 始终为 0,可能是 filter 不匹配。

验证命令:

journalctl -u ssh --since "1 hour ago" | fail2ban-regex - /etc/fail2ban/filter.d/sshd.conf

若输出中 Matched: 0,且 Missed 行全是 sshd-session 开头,说明 filter 未识别新进程名。

5.3 解决方案 A:行内覆盖(推荐)

jail.d/custom.local[sshd] 节中使用行内覆盖语法:

[sshd]
filter = sshd[_daemon=sshd(?:-session)?]

优点:单文件维护,无需额外文件
原理(?:-session)? 是正则表达式,表示 -session 可选,同时匹配 sshdsshd-session

5.4 解决方案 B:独立 filter 覆盖文件

创建 /etc/fail2ban/filter.d/sshd.local

sudo tee /etc/fail2ban/filter.d/sshd.local > /dev/null << 'EOF'
[Definition]
_daemon = sshd(?:-session)?
EOF

优点:配置语义清晰
缺点:多维护一个文件

5.5 验证

修改后重载 Fail2ban:

sudo fail2ban-client reload sshd

从另一台机器故意输错密码,然后:

sudo fail2ban-client status sshd

预期 Total failedCurrently banned 增加。


6. 常见场景与最佳实践

6.1 常见使用场景

场景 1:新服务器初始化

步骤 1:安装

sudo apt update && sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban  # 确认 active (running)

步骤 2:设置变量

MGMT_IP="10.0.0.1"    # 改为你的管理机 IP,支持 CIDR,多个用空格分隔
SSH_PORT="22"         # 改为实际 SSH 端口

步骤 3:写入完整配置

sudo tee /etc/fail2ban/jail.d/custom.local > /dev/null << EOF
# /etc/fail2ban/jail.d/custom.local
# 生产环境 Fail2ban 完整配置(适配 OpenSSH 9.8+ sshd-session)
 
[DEFAULT]
# ==================== 自定义白名单变量 =====================
project_management_ips = $MGMT_IP
 
# ===================== 基础行为参数 ========================
bantime  = 10m
findtime = 30m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 %(project_management_ips)s
 
# ==================== 递增惩罚 + 随机时长 ===================
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 24w
bantime.rndtime   = 10m
 
# ==================== sshd jail 专用配置 ===================
[sshd]
enabled      = true
port         = $SSH_PORT
backend      = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service
filter       = sshd[_daemon=sshd(?:-session)?]
banaction    = iptables-multiport
bantime.overalljails = true
 
# ==================== recidive 监狱(惯犯监狱) ===============
[recidive]
enabled     = true
backend     = auto
filter      = recidive
logpath     = /var/log/fail2ban.log
findtime    = 1d
maxretry    = 3
bantime     = 12w
ignoreip    = 127.0.0.1/8 ::1 %(project_management_ips)s
EOF

步骤 4:检查语法并重载

sudo fail2ban-client -t && echo "语法正确"
sudo systemctl reload fail2ban
sudo fail2ban-client status

预期输出包含:

Status
|- Number of jail:	2
`- Jail list:	recidive, sshd

场景 2:换项目时修改管理网段

sudo sed -i 's/^project_management_ips = .*/project_management_ips = 192.168.1.0\/24/' \
    /etc/fail2ban/jail.d/custom.local
sudo systemctl reload fail2ban
sudo fail2ban-client get sshd ignoreip  # 确认白名单已更新

场景 3:修改 SSH 端口

修改 SSH 端口后,必须同步更新 Fail2ban 配置:

sudo sed -i 's/^port\s*=.*/port         = 2222/' /etc/fail2ban/jail.d/custom.local
sudo systemctl reload fail2ban

场景 4:临时解除所有封禁

# 停止 fail2ban(自动清空 iptables 规则)
sudo systemctl stop fail2ban
 
# 重启后规则重新生效
sudo systemctl start fail2ban

场景 5:查看某 IP 的封禁历史

zgrep "1.2.3.4" /var/log/fail2ban.log* | grep Ban

6.2 最佳实践

1. 白名单一定要加管理机 IP

防止手误把自己封掉,尤其是配置变更时。如果已被封,从控制台/BMC 登录后:

sudo fail2ban-client set sshd unbanip <your_ip>

2. 不要直接修改 .conf 文件

系统升级会覆盖它们,所有变更写入 jail.d/custom.local

3. 修改配置前先备份

sudo cp /etc/fail2ban/jail.d/custom.local /etc/fail2ban/jail.d/custom.local.bak

4. 先用 fail2ban-client -t 检查语法

sudo fail2ban-client -t && echo "语法正确"

5. 生产环境建议 maxretry 不低于 3

避免因手抖或网络抖动把合法用户误封。

6. 修改 SSH 端口后,记得同步更新 [sshd] 节的 port =

否则封禁动作仍针对 22 端口,形同虚设。

7. recidive 的 findtime 要大于 sshd 的 findtime

否则惯犯统计窗口太短,起不到累积效果。本文配置中 sshd 的 findtime 为 30 分钟、recidive 的 findtime 为 1 天,是合理搭配。

8. backend 选择原则

  • sshd:使用 backend = systemd(journald 日志实时性更好)
  • recidive:必须使用 backend = auto(监控文本日志 /var/log/fail2ban.log
  • 不要在 [DEFAULT] 设置 backend:否则所有 jail 都会继承,导致 recidive 失效

7. 故障排除

现象原因解决
fail2ban-client status 只有 1 个 jailrecidive 配置有误检查 recidive 节是否显式设 backend = auto
多次失败但未触发封禁filter 不匹配 OpenSSH 9.8+确认 filter = sshd[_daemon=sshd(?:-session)?]
journalmatch 报错systemd backend 未启用确认 backend = systemdjournalctl -u ssh 有日志
封禁后仍能登录port 与实际 SSH 端口不一致检查 `ss -tlnpgrep ssh` 与配置是否匹配
重载后白名单未生效语法错误或变量未展开fail2ban-client get sshd ignoreip 确认实际值
recidive 不工作backend 继承错误recidive 节必须显式设 backend = auto
修改配置后未生效未执行 reloadsystemctl reload fail2ban,不要用 restart
自己被封禁管理 IP 未加白名单从控制台/BMC 登录后解封并修正 ignoreip
iptables 规则未生效banaction 不匹配防火墙类型Ubuntu 默认 iptables,确认 banaction = iptables-multiport

8. 总结

Fail2ban 是一款轻量级的入侵防御工具,通过监控日志并自动封禁恶意 IP,有效防御 SSH 暴力破解等攻击。其逐级惩罚机制和跨服务统计(recidive)功能,让攻击者每次尝试都付出更大代价。

掌握 Fail2ban 后,你可以:

  • 在新服务器上通过一条命令快速部署生产级防御配置。
  • 使用变量管理白名单,灵活适配不同项目的管理 IP。
  • 正确配置 systemd backend 和 journalmatch,适配 Ubuntu 22.04+ 的日志系统。
  • 兼容 OpenSSH 9.8+ 的 sshd-session 进程名,避免 filter 不匹配。
  • 通过 recidive 惯犯监狱跨 jail 累计封禁次数,识别并长期封禁惯犯。
  • 快速排查常见问题,如白名单未生效、recidive 不工作、端口不匹配等。

建议从基础配置开始实践,逐步探索逐级惩罚和 recidive 功能,结合最佳实践持续提升服务器安全防护能力。


9. 参考资料