最近查看 Nginx 的访问日志,发现有不少恶意扫描漏洞的记录,同时也有不少的伪造蜘蛛的爬虫导致消耗了不少服务器资源,由此准备做一些缓解措施。其实 Nginx 自带的 limit_req_zone
和 limit_conn_zone
参数已经可以限制连接频率和并发数量,但对于一些“聪明”的恶意爬虫来说,显然是不够的。

下面介绍一下如何用 Fail2Ban 工具匹配分析 Nginx 的日志文件,禁止特定 IP,以达到减缓恶意扫描或者是应用层 DDoS 攻击的目的。
关于 DDoS 攻击的一些知识,可以看看我先前写的这篇文章:聊聊 DDoS 攻击那些事
这里先简单说说 fail2ban 这款工具,其实 Linux 用户可能都不会陌生,fail2ban 是一款入侵检测系统框架工具,通过扫描日志文件并用正则匹配分析,然后通过更新防火墙规则来禁止某些有恶意迹象的 IP(密码失败过多,寻求漏洞利用等)来提高服务器安全性。开箱即用的 fail2Ban 自带了用于各种服务(Apache、Courier、SSH 等)的过滤器,最常用的场景是扫描 SSH 连接日志,禁止失败次数过多的 IP,防止 SSH 暴力破解。
安装 Fail2Ban
演示环境为 Debian 11,使用 root 用户,大多数 Linux 发行版软件包仓库中都有 Fail2ban,直接执行安装命令即可:
apt install -y fail2ban
安装成功后配置文件都位于 /etc/fail2ban/
目录中,其中需要关注的有以下三个文件或文件夹:
/etc/fail2ban/filter.d/
:恶意行为的过滤规则,其中预设于了 SSH、Nginx、Apache 的监控规则/etc/fail2ban/action.d/
:发现恶意 IP 后采取的操作,其中预设了许多常用操作,其中预设了 iptables、firewalld、sendmail 等操作/etc/fail2ban/jail.conf
:即小黑屋,Jail 由 Filter 和 Action 组成,作用是通过组合前面的配置来设置 fail2ban 的工作行为
常用命令:
- 查看指定 Jail 规则下被封禁的ip情况:
fail2ban-client status <JAIL>
- 添加/解除指定 IP 的封禁:
fail2ban-client set <JAIL> banip/unbanip <IP>
- 添加/解除指定 IP 的忽略:
fail2ban-client set <JAIL> addignoreip/delignoreip <IP>
- 测试匹配规则是否正确:
fail2ban-regex <日志文件> <过滤规则>
- 查看所有命令:
fail2ban-client -h
- 查看日志:
tail /var/log/fail2ban.log
配置 Fail2Ban
添加过滤规则
首先设置过滤规则,在 /etc/fail2ban/filter.d/
目前下新建一个 .conf
文件,名字自取,比如我新建的是 nginx-atpx-com.conf
,然后进行设置:
[Definition]
failregex =
ignoreregex =
其中:
failregex:表示过滤规则的正则表达式;
ignoreregex:表示忽略规则的正则表达式,可以设置为 .*(webp|svg|jpg|png)
忽略对图片文件的请求,防止图片文件过多误伤;
而这里要实现我们想要的效果,也有两个选择,配合开篇提到的 Nginx 流控产生的日志文件(error.log)进行匹配过滤或者直接对 Nginx 的访问日志文件(access.log)进行匹配过滤。原理都一样,fail2ban 预置了很多常见服务的日志文件匹配模板,在 /etc/fail2ban/filter.d/
目录下可以找到。如果你修改了日志格式,那么需要根据你的日志文件格式改写相应的表达式。
这里我用 Nginx 的 limit_req_zone
流控模块做一个示例,下面是一条超过限制产生的错误信息:
2021/10/13 01:02:39 [error] 14174#0: *41792 limiting requests, excess: 5.920 by zone "request", client: x.x.x.x, server: www.atpx.com, request: "HEAD /?feed=rss HTTP/2.0", host: "www.atpx.com"
参考自带的 /etc/fail2ban/filter.d/nginx-limit-req.conf
模板,可以写成下面的表达式:
[Definition]
failregex = ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone ".*", client: <HOST>,
ignoreregex =
failregex
也可以直接简写为:
failregex = limiting requests, excess:.* by zone.*client: <HOST>
其中 <HOST>
是必须包含的,fail2ban 通过这个来获取 IP 地址,测试自定义的规则是否生效:
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-atpx-com.conf
如果成功匹配会返回匹配到的信息:
Results
=======
Failregex: 4 total
|- #) [# of hits] regular expression
| 1) [4] ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone ".*", client: <HOST>,
`-
Ignoreregex: 0 total
关于一些正则表达式的基本语法,网上有很多不错的教程,有兴趣可以去了解一下。
配置 Jail 规则
Fail2ban 读取配置文件的顺序如下,.local
会覆盖 .conf
:
- /etc/fail2ban/jail.conf
- /etc/fail2ban/jail.d/*.conf
- /etc/fail2ban/jail.local
- /etc/fail2ban/jail.d/*.local
对于大多数用户来收,最简单的方式是直接在 /etc/fail2ban/
目录下将 jail.conf
复制为 jail.local
文件:
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
然后在 jail.local
中修改即可,其中可配置项非常丰富,并且都有详细的介绍,你可以根据自己的需要进行修改。
个人推荐设置一下白名单,方便你自己的本地 IP 测试使用,直接在 jail.local
中找到 ignoreip
参数,取消注释,然后添加你希望设置白名单的 IP:
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 1.1.1.1
可以直接在 jail.local
的末尾添加你自定义的规则,我这里选择了新建一个 /etc/fail2ban/jail.d/custom.conf
文件,便于后期的查看和维护:
[nginx-atpx-com]
enabled = true
port = http,https
filter = nginx-atpx-com
logpath = /var/log/nginx/access.log
bantime = 12h
findtime = 60
maxretry = 5
简单说明一下参数:
port
:封禁的端口,这里设置的是一般做站的 80 和 443 端口filter
:过滤规则,使用前面自定义的 nginx-atpx-comlogpath
:监控的日志文件路径bantime
:封禁时间,单位为秒,-1 表示永久findtime
:查找时间段,单位为秒maxretry
:允许的最大失败次数,结合前面的 findtime,比如这里配置的是每 60 秒内触发 5 次规则,那么就封禁掉该 IP 12 小时
然后重启 fail2ban 即可生效:
service fail2ban restart
fail2ban-client status nginx-atpx-com
此时你可以在另外一台服务器上创建一个 shell 脚本,执行 curl 请求循环来检测是否生效:
#!/bin/bash
for ((i=1;i<=50;i++)); do
curl -H "Fail2ban test" https://your-domian/test > /dev/null 2>&1
done
echo "done"
注意:如果服务器上启用了 ufw 防火墙,会出现 Fail2ban 无法阻止 IP 的情况。这是由于 iptables 的顺序问题,关于 ufw 的具体使用可以参考我前面写的文章:修改SSH端口使用密钥登录并配置防火墙
要解决这个问题,你可以将 fail2ban 默认的 iptables 替换为 ufw,首先确认 /etc/fail2ban/action.d/
目录下有 ufw.conf
这个文件。然后修改 /etc/fail2ban/jail.local
中的 banaction
参数:
banaction = ufw
banaction_allports = ufw
最后,你也可以添加一个 cron 任务定期清理 Nginx 和 fail2ban 的日志文件,减少硬盘空间占用,这里就不再介绍了。