Ubuntu Fail2ban 从原理到实战全指南
在 Linux 服务器运维中,SSH 暴力破解是最常见的安全威胁之一。攻击者通过自动化工具对常见用户名和密码进行大规模尝试,一旦猜中弱密码,服务器的控制权就会落入攻击者手中。
Fail2ban 是一款入侵防御工具,通过监控系统日志(如 SSH 认证日志),自动识别暴力破解行为并将攻击源 IP 加入防火墙黑名单,从而在攻击发生时实时阻断威胁。
Fail2ban 的核心优势:
- 自动化防御:无需人工干预,实时监控日志并封禁恶意 IP。
- 逐级惩罚:支持递增封禁时长,让反复攻击的 IP 付出更大代价。
- 跨服务统计:通过 recidive 监狱跨 jail 累计封禁次数,识别惯犯并长期封禁。
- 灵活配置:支持白名单、自定义封禁策略和多种日志后端(systemd / 文本文件)。
本文将从 Fail2ban 的工作原理、配置文件结构、逐步构建生产配置、OpenSSH 9.8+ 兼容处理、常见场景与故障排除等方面,帮助你完整掌握 Fail2ban 的部署与优化。
目录
- 1. 引言:什么是 Fail2ban
- 2. 安装 Fail2ban
- 3. 核心概念与配置文件结构
- 4. 从零构建生产配置
- 5. OpenSSH 9.8+ sshd-session 兼容处理
- 6. 常见场景与最佳实践
- 7. 故障排除
- 8. 总结
- 9. 参考资料
1. 引言:什么是 Fail2ban
Fail2ban 是一个用 Python 编写的入侵防御框架,工作原理如下:
系统日志 → Fail2ban 扫描 → 匹配 filter 规则 → 达到阈值 → 触发 action → iptables/nftables 封禁 IP
1.1 工作流程示例
以 SSH 暴力破解为例:
- 攻击者从 IP
1.2.3.4连续 6 次输错密码 - SSH 服务将失败记录写入 journald(或
/var/log/auth.log) - Fail2ban 的 sshd jail 扫描日志,通过正则表达式提取失败记录
- 在 30 分钟内累计 5 次失败 → 触发封禁
- Fail2ban 调用 iptables 将
1.2.3.4加入黑名单 - 封禁持续 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 -y2.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 变量,使其同时匹配 sshd 和 sshd-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.local4.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 sshd 的 Total 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 可选,同时匹配 sshd 和 sshd-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 failed 和 Currently 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 Ban6.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.bak4. 先用 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 个 jail | recidive 配置有误 | 检查 recidive 节是否显式设 backend = auto | |
| 多次失败但未触发封禁 | filter 不匹配 OpenSSH 9.8+ | 确认 filter = sshd[_daemon=sshd(?:-session)?] | |
journalmatch 报错 | systemd backend 未启用 | 确认 backend = systemd 且 journalctl -u ssh 有日志 | |
| 封禁后仍能登录 | port 与实际 SSH 端口不一致 | 检查 `ss -tlnp | grep ssh` 与配置是否匹配 |
| 重载后白名单未生效 | 语法错误或变量未展开 | fail2ban-client get sshd ignoreip 确认实际值 | |
| recidive 不工作 | backend 继承错误 | recidive 节必须显式设 backend = auto | |
| 修改配置后未生效 | 未执行 reload | systemctl 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. 参考资料
- Fail2ban 官方文档
- Fail2ban GitHub 仓库
- Ubuntu Fail2ban Package
- OpenSSH 9.8 Release Notes(sshd-session 变更说明)
- boypt/openssh-deb(OpenSSH 反向移植与 Fail2ban 兼容说明)
- Fail2ban sshd.conf 上游提交(sshd-session 兼容补丁)