在CentOS服务器上安装fail2ban并针对Nginx日志做自定义正则过滤,核心就是三步:装软件、写正则规则、配置jail。fail2ban是一款轻量级的入侵防御工具,它通过监控日志文件中的异常行为,自动将恶意IP加入防火墙黑名单。Nginx作为最常见的Web服务器,日志格式多样,默认的fail2ban规则往往不能精准匹配你的实际场景,所以自定义正则过滤是必做的功课。下面我把完整流程、常见坑点和优化技巧一次性讲透。
一、CentOS安装fail2ban的完整步骤CentOS 7和CentOS 8/Stream的安装方式略有不同,但大体一致。首先确保系统已更新到最新状态,然后安装EPEL源(CentOS 7需要),再装fail2ban本身。
# CentOS 7 yum update -y yum install -y epel-release yum install -y fail2ban # CentOS 8 / Stream dnf update -y dnf install -y epel-release dnf install -y fail2ban
安装完成后,启动fail2ban服务并设置开机自启:
systemctl start fail2ban systemctl enable fail2ban systemctl status fail2ban
确认状态为active (running)就说明没问题。接下来要做的关键事情是:不要直接改/etc/fail2ban/jail.conf,而是复制一份到jail.local,所有自定义都写在jail.local里,这样升级fail2ban时不会覆盖你的配置。
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
在jail.local里,把默认的backend改成systemd(CentOS 7用systemd,CentOS 8也是),同时禁用你不需要的默认jail,只保留你要用的。
[DEFAULT] bantime = 3600 findtime = 600 maxretry = 5 backend = systemd二、理解Nginx日志格式是自定义正则的前提
fail2ban能不能精准拦截,完全取决于正则表达式写得对不对。而正则写得对不对,取决于你是否清楚自己Nginx的日志长什么样。Nginx有两种主要日志:access_log和error_log。做fail2ban过滤,主要针对的是access_log,因为恶意请求都记录在这里。
先看一眼你的Nginx日志格式。打开/etc/nginx/nginx.conf或者你站点的配置文件,找到log_format指令。常见的combined格式如下:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
这意味着每一行日志大概长这样:
192.168.1.100 - - [15/Jun/2024:10:23:45 +0800] "GET /wp-login.php HTTP/1.1" 403 156 "-" "Mozilla/5.0..."
关键字段:IP地址、时间、请求方法+路径、状态码、User-Agent。你的正则就是要从这一行里提取出IP,然后匹配特定的恶意行为模式。
三、编写自定义正则过滤规则fail2ban的过滤规则放在/etc/fail2ban/filter.d/目录下,文件名以.conf结尾。我们新建一个针对Nginx的自定义规则文件:
vim /etc/fail2ban/filter.d/nginx-custom.conf
文件内容如下,我逐段解释:
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*\.(php|asp|aspx|jsp|cgi)\?.* HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*wp-login\.php HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*xmlrpc\.php HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*\.env HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*\.git/config HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*adminer\.php HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*\.(bak|sql|log|conf|ini) HTTP.*" (403|404|499|500|502|503)
^<HOST> -.*"(GET|POST|HEAD).*\.htaccess HTTP.*" (403|404|499|500|502|503)
ignoreregex =
这里面的关键点:<HOST>是fail2ban的内置变量,会自动匹配IP地址。failregex里每一行是一个独立的匹配规则,用空格分隔表示"或"的关系。上面的规则覆盖了几种常见攻击:扫描PHP/ASP等动态脚本、暴力破解wp-login、探测xmlrpc、读取敏感文件、探测.git目录、尝试访问数据库管理工具、下载备份文件、尝试访问.htaccess。
状态码选了403(禁止)、404(未找到)、499(客户端关闭连接,通常是扫描工具)、500/502/503(服务器错误,可能是恶意请求触发的)。你可以根据自己的实际情况增删规则。
ignoreregex留空,如果你想排除某些IP(比如监控服务器、CDN节点),可以在这里加正则,但更推荐在jail.local里用ignoreip参数。
四、配置jail.local启用自定义规则规则写好了,现在要在jail.local里定义一个新的jail来使用它。打开jail.local,在文件末尾添加:
[nginx-custom] enabled = true port = http,https filter = nginx-custom logpath = /var/log/nginx/access.log maxretry = 3 bantime = 7200 findtime = 300 action = iptables-multiport[name=nginx-custom, port="http,https", protocol=tcp] ignoreip = 127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 你的监控IP
这里几个参数的含义:maxretry设为3,意味着3次匹配就封禁;bantime设为7200秒(2小时);findtime是300秒(5分钟)内统计次数;action指定用iptables封禁http和https端口。ignoreip一定要加上本地回环地址和内网段,否则可能把自己锁在外面。
如果你用的是firewalld而不是iptables,action可以改成:
action = firewallcmd-ipset[name=nginx-custom, port="http,https", protocol=tcp]
或者直接用默认的iptables action也没问题,fail2ban会自动处理。
五、测试正则规则是否生效配置完不要急着重启,先测试。fail2ban提供了一个专门的测试命令fail2ban-regex,可以用它来验证你的正则能不能正确匹配日志。
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-custom.conf
这个命令会输出匹配结果,包括总共扫描了多少行、匹配到多少行、匹配的IP列表。如果结果显示Matched: 0,说明正则有问题,需要调整。常见问题包括:日志格式和正则不一致(比如你的Nginx用了自定义log_format)、正则中的特殊字符没有转义、或者日志路径不对。
如果你的Nginx日志路径不是默认的/var/log/nginx/access.log,记得在jail.local里改logpath。有些人把日志放在/var/log/nginx/站点名/access.log,那就要对应修改。
六、重启fail2ban并验证封禁效果测试通过后,重启fail2ban让新配置生效:
systemctl restart fail2ban
然后查看fail2ban的状态和具体jail的运行情况:
fail2ban-client status nginx-custom
这个命令会显示当前jail是否运行、已经封禁了多少个IP、被封禁的IP列表。你也可以查看具体某个IP为什么被封:
fail2ban-client get nginx-custom banned
如果一切正常,你会看到被封禁的IP列表。可以用iptables -L -n查看防火墙规则,确认fail2ban添加的规则确实存在。
七、进阶优化:多规则组合和性能调优实际生产环境中,单一规则往往不够用。你可以创建多个filter文件,分别针对不同类型的攻击,然后在jail.local里定义多个jail。比如一个专门针对SQL注入,一个针对CC攻击,一个针对目录扫描。
针对CC攻击(大量请求导致服务过载),可以单独写一个filter,匹配短时间内同一IP的大量请求:
[Definition] failregex = ^<HOST> -.*"(GET|POST).*HTTP.*" datepattern = ^[^\[]*\[%%d/%%b/%%Y:%%H:%%M:%%S
然后在jail.local里设置更短的findtime和更大的maxretry,配合更长的bantime。但要注意,CC攻击的检测如果阈值设得太低,可能会误封正常用户,尤其是通过CDN或代理访问的场景。
性能方面,如果你的Nginx日志非常大(每天几个GB),fail2ban的监控会消耗一定资源。建议配合logrotate做日志切割,并且在fail2ban的配置里避免对过大的日志文件做全量扫描。fail2ban默认只扫描文件末尾新增的内容,这一点做得比较好,但如果你重启了fail2ban,它会从头扫一遍,大日志文件会比较慢。
八、常见问题排查清单最后给一个实战中经常遇到的问题清单,帮你快速定位:
第一,fail2ban启动失败。检查/var/log/fail2ban.log,通常是配置语法错误或者backend不对。CentOS 8必须用systemd backend,用auto可能出问题。
第二,正则匹配不到任何内容。先确认日志路径正确,再确认日志格式和正则一致。用fail2ban-regex手动测一下就知道了。
第三,封了IP但攻击还在继续。检查action是否正确,确认iptables或firewalld规则确实生效。有些云服务器还有安全组规则,fail2ban封的是系统层面,安全组是另一层,两层都要配合。
第四,误封了正常IP。在jail.local里把该IP加到ignoreip,或者调整maxretry和findtime参数,让触发阈值更合理。
第五,fail2ban占用内存过高。在jail.local里调整maxlines参数,限制每个日志文件最多扫描多少行,默认是2500行,可以适当降低。
总的来说,fail2ban配合自定义Nginx正则过滤,是CentOS服务器安全防护的基础操作。不需要花哨的工具,关键是把规则写准、参数调好、持续观察。建议部署后至少观察一周,根据实际日志调整规则,才能达到最佳防护效果。
