公网服务器只要开放了端口,就会被各种扫描脚本盯上。有人统计过,一台新上架的云服务器,开机能被爆破尝试的频率高达每分钟数次,Nginx 反代的登录接口、后台管理页面更是重灾区。单靠改端口、改路径只能拖延时间,真正有效的做法是让恶意 IP 自动进黑名单。Fail2ban 就是为这件事而生的,它通过监控日志、匹配失败特征、调用防火墙规则,把整个封禁流程完全自动化。本文将从原理到实操,完整讲清楚 Nginx 与 Fail2ban 的配合方式。

Fail2ban 的工作原理与安装
Fail2ban 的核心逻辑并不复杂:它是一个常驻后台的守护进程,会定期轮询你指定的日志文件,用正则表达式去匹配每一行新日志,一旦发现某个 IP 在设定的时间窗口内触发了超过阈值的失败行为,就执行预定义的封禁动作,通常是调用 iptables 或 firewalld 把该 IP 的连接丢弃。封禁到期后自动解封,全程无需人工介入。
整个体系由三部分组成:filter(过滤器)定义什么样的日志行算失败,jail(监狱)定义监控哪个日志、阈值多少、封禁多久,action(动作)定义怎么封。三者解耦的设计让你可以灵活组合,比如同一个 nginx 的 filter,既可以配成封 10 分钟,也可以配成永久封禁。
安装非常简单,以 CentOS 为例:
# CentOS / RHEL yum install epel-release -y yum install fail2ban -y # Debian / Ubuntu apt install fail2ban -y # 启动并设置开机自启 systemctl enable --now fail2ban
需要注意的是,Fail2ban 默认自带的配置文件在 /etc/fail2ban/jail.conf,官方明确建议不要直接改这个文件,而是新建一个 jail.local,同名配置会覆盖 jail.conf,这样升级 Fail2ban 时自定义规则不会被覆盖掉。
配置 Nginx 日志与编写过滤规则
Fail2ban 依赖日志做判断,所以第一步是确保 Nginx 的日志格式包含完整信息。默认的 combined 格式其实已经够用,但如果要精确匹配请求路径,建议在日志中加入 $request 字段的完整记录。查看你当前的格式:
# 查看 Nginx 默认日志格式,一般在 nginx.conf 中定义
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
接下来创建过滤器。假设你的后台登录地址是 /admin/login.php,希望对频繁访问该路径且返回 401 或 403 的 IP 进行封禁,在 /etc/fail2ban/filter.d/nginx-admin.conf 中写入:
[Definition] # 匹配访问后台且认证失败的日志行 failregex = ^<HOST> -.*"(GET|POST) /admin/login\.php.*" (401|403) .*$ # 排除自身健康检查 ignoreregex =
<HOST> 是 Fail2ban 的内置占位符,代表日志行中提取出的来源 IP,必须写在 failregex 中,否则 Fail2ban 无法知道该封谁。写好规则后务必先测试正则是否命中:
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-admin.conf
命令会输出匹配到的行数和示例,如果 matched 是 0,多半是正则和日志格式对不上,把日志里一行原文拿出来对着调,注意空格、引号和横杠的数量,这是新手最容易踩的坑。
创建 jail 并调优封禁策略
过滤器验证通过后,在 /etc/fail2ban/jail.local 中定义 jail:
[nginx-admin] enabled = true port = http,https filter = nginx-admin logpath = /var/log/nginx/access.log maxretry = 5 findtime = 600 bantime = 3600 # 白名单,多个用空格分隔,支持 CIDR 网段 ignoreip = 127.0.0.1/8 192.168.1.0/24 action = iptables-multiport[name=nginx-admin, port="http,https", protocol=tcp]
几个关键参数值得仔细调优。maxretry 是允许的失败次数,findtime 是统计时间窗口,bantime 是封禁时长。这三者的组合决定了防护的灵敏度。对于后台登录这种低频操作,maxretry 设为 5、findtime 设为 10 分钟是比较稳妥的起点;bantime 建议至少 1 小时,太短的话攻击者换个批次又卷土重来。
还有一个进阶技巧是递增封禁。Fail2ban 支持 bantime.increment 参数,开启后重复违规的 IP 封禁时间会指数级增长,配合 bantime.maxtime 可以把惯犯封到几个月甚至永久:
[DEFAULT] bantime = 3600 bantime.increment = true bantime.factor = 2 bantime.maxtime = 604800
配置完成后重启服务并验证运行状态:
systemctl restart fail2ban # 查看所有 jail 状态 fail2ban-client status # 查看具体 jail 的封禁列表 fail2ban-client status nginx-admin # 手动解封某个 IP fail2ban-client set nginx-admin unbanip 1.2.3.4
常见问题排查与注意事项
实际使用中最常见的问题是 jail 不生效。排查思路是:先看 fail2ban-client status 里对应 jail 是否显示启动,再用 fail2ban-regex 验证正则,最后检查日志路径是否正确。如果你的系统用的是 firewalld 或者 nftables,需要把 action 换成对应的防火墙动作,否则 Fail2ban 会调用 iptables 失败,日志里能看到报错。
另一个容易忽视的点是日志轮转。logrotate 切割日志后,Fail2ban 监控的文件句柄可能指向旧文件,导致后续日志读不到。解决办法是在 /etc/logrotate.d/nginx 的配置中加入 sharedscripts 和 postrotate 脚本,轮转后执行 fail2ban-client reload 对应的 jail,保证监控链路不断。
最后提醒三点:一是务必把公司出口 IP 和常用办公网段加入 ignoreip,避免自己把自己封在门外;二是 Nginx 如果前面还有 CDN,日志里记录的是 CDN 节点 IP,需要改用 realip 模块还原真实客户端地址,否则封的全是 CDN;三是 Fail2ban 属于事后封禁,不能替代验证码、限流等前置防护,多层防御叠加才能构建真正可靠的安全边界。