Fail2ban 防护 Nginx 实战:CC 攻击与扫描自动封禁

适用场景

Fail2ban 最初用于防 SSH 爆破,但其 jail 机制同样适用于 Web 层:对 Nginx 日志进行实时监控,自动封禁触发 CC 攻击特征、频繁扫描敏感路径、暴力猜解登录接口的 IP。相比手工分析日志再写 iptables 规则,Fail2ban 实现了识别、封禁、解封的全自动闭环。本文介绍如何配置 Fail2ban 防护 Nginx,自动封禁 CC 攻击与恶意扫描行为。

前置条件

  • Linux 服务器(本文以 Debian/Ubuntu、CentOS 为例),root 或 sudo 权限
  • 已安装 Nginx 且 access.log 正常记录(含状态码与 URI)
  • 服务器可访问软件源安装 Fail2ban(yum/epeel 或 apt)
  • 确认防火墙工具:Fail2ban 默认调用 iptables,CentOS 7+ 若使用 firewalld 需调整 banaction

原理说明

Fail2ban 由三部分组成:filter(过滤器)是一组正则表达式,从日志文件中匹配出”违规事件”并提取客户端 IP;jail(监牢)将 filter 与具体日志文件、阈值策略绑定——在 findtime 时间窗口内命中 maxretry 次即触发封禁;action(动作)是封禁的执行方式,默认通过 iptables 在 INPUT 链 DROP 目标 IP,封禁时长由 bantime 控制。

应用到 Nginx 场景的典型判定逻辑:同一 IP 在短时间内产生大量 4xx/5xx 请求(扫描特征)、高频请求动态接口(CC 特征)、反复 POST 登录接口失败(爆破特征)。Fail2ban 逐行 tail 日志文件,命中即计数,达到阈值后调用防火墙封禁,超时自动解封,全程无需人工介入。

操作步骤

第一步:安装 Fail2ban

# Debian / Ubuntu
apt update && apt install -y fail2ban

# CentOS / RHEL(需 EPEL 源)
yum install -y epel-release && yum install -y fail2ban

systemctl enable --now fail2ban

第二步:编写 Nginx 过滤器

创建 /etc/fail2ban/filter.d/nginx-cc.conf,匹配高频访问任意页面的行为:

[Definition]
failregex = ^<HOST> -.*"(GET|POST).*
ignoreregex =

创建 /etc/fail2ban/filter.d/nginx-scan.conf,匹配扫描与探测特征(4xx 状态码):

[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*(404|403|400) .*$
ignoreregex =

<HOST> 是 Fail2ban 的占位符,会被替换为日志中的 IP 并自动捕获。

第三步:创建 jail 本地配置

不要直接改 jail.conf,新建 /etc/fail2ban/jail.local

[DEFAULT]
bantime  = 3600
findtime = 60
maxretry = 10
banaction = iptables-multiport

[nginx-cc]
enabled  = true
port     = http,https
filter   = nginx-cc
logpath  = /var/log/nginx/access.log
maxretry = 300
findtime = 60
bantime  = 600

[nginx-scan]
enabled  = true
port     = http,https
filter   = nginx-scan
logpath  = /var/log/nginx/access.log
maxretry = 15
findtime = 60
bantime  = 3600

关键参数说明:nginx-cc 的 maxretry 300 / findtime 60 表示单 IP 每分钟请求超过 300 次即封 10 分钟——阈值需结合正常业务流量设定,误封代价远大于漏放;nginx-scan 的 maxretry 15 更严格,因为正常用户一分钟内产生 15 个 404 的概率极低,而扫描器批量探测不存在的路径会快速命中。bantime 可设为负值(如 -1)实现永久封禁,但建议配合白名单使用。

第四步:加入管理员白名单并启动

在 [DEFAULT] 段追加 ignoreip = 127.0.0.1/8 你的办公IP/32,然后重载:

systemctl restart fail2ban
fail2ban-client status

配置验证

用 ab 从测试机高频请求目标站触发 CC 规则,再查看 jail 状态:

ab -n 1000 -c 10 http://目标站/test

fail2ban-client status nginx-cc
# 预期输出 "Currently banned IP list" 包含测试机 IP

# 查看封禁日志确认匹配明细
tail -20 /var/log/fail2ban.log

确认封禁后测试机无法访问,bantime 到期自动解封。防火墙层面可同步验证:

iptables -L f2b-nginx-cc -n

常见问题

FAQ 1:Fail2ban 未生效,日志明明有大量恶意请求

按顺序排查:① logpath 路径错误——Nginx 若按站点分日志文件,需在 jail 中用逗号列出全部文件或使用通配符;② 日志格式不匹配——Nginx 自定义 log_format 会改变字段顺序,导致 failregex 正则无法命中,用 fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-cc.conf 实测匹配数,返回 matched 行数大于 0 才有效;③ 后端时间不一致——确认系统时区与日志时间戳一致,findtime 计算依赖日志时间。

FAQ 2:CDN 场景下误封了 CDN 节点 IP

与 limit_conn 同样的问题:日志中的 IP 是 CDN 回源节点,一旦触发阈值,整个 CDN 节点网段被封,全站用户受影响。解决方案分两步:第一步让 Nginx 使用 realip 模块还原真实客户端 IP(日志也记录真实 IP);第二步将 CDN 官方网段加入 ignoreip 白名单兜底。若无法还原真实 IP,则应放弃在网络层封禁,改用 CDN 平台自带的频次控制功能。

FAQ 3:重启后封禁记录丢失

Fail2ban 默认将封禁规则保存在内存中,重启服务后全部清空且未到期的封禁提前解除。在 jail.local 的 [DEFAULT] 段配置 dbpurgeage 并启用 sqlite 持久化(默认已启用 /var/lib/fail2ban/fail2ban.sqlite3),重启后 Fail2ban 会恢复数据库中未过期的封禁项。若频繁丢失,检查该数据库文件所在分区是否可写。

总结

Fail2ban 为 Nginx 提供了低成本、高可靠的自动封禁能力:filter 定义特征、jail 定义阈值与时长、action 落地到防火墙。配置核心在于阈值设定——CC 规则放宽(每分钟数百次起步)、扫描规则收紧(几十次即封),并始终保留管理员白名单。上线后每天用 fail2ban-client status 复查封禁量,持续调整阈值,让它成为 WAF 之外的第二道自动防线。