自定义fail2ban规则拦截跨境扫描IP:实战
服务器日志里经常出现大量来自境外IP的端口扫描或暴力破解尝试?
默认的fail2ban规则只能根据访问频率封禁,但无法区分地域。
本文教你通过自定义fail2ban的filter和action,搭配GeoIP数据库,自动拦截所有匹配扫描模式的跨境IP,从源头减少无效请求。
适用场景与准备工作
这套方案适合以下场景:你的业务主要面向国内用户,但服务器24小时被海外IP扫端口或测漏洞;
或者你想在fail2ban原有的频率封锁基础上,加一道地域白名单/黑名单。
准备工作清单:
- 一台已安装fail2ban的Linux服务器(CentOS 7+/Ubuntu 18.04+都可以)
- root或sudo权限
- 安装
geoip-bin和iptables(iptables通常自带),用于获取IP归属地并执行封禁
# 安装fail2ban(如果未安装)
sudo apt install fail2ban -y # Debian/Ubuntu
sudo yum install epel-release -y && sudo yum install fail2ban -y # CentOS
# 安装geoip工具
sudo apt install geoip-bin -y # 提供geoiplookup命令
# 或从MaxMind下载免费GeoLite2库,但简单起见用系统包自带
第一步:编写自定义filter匹配扫描行为
fail2ban通过filter来匹配日志中的“违规”行。
我们创建一个专门识别端口扫描特征的自定义filter,例如匹配nmap扫描、大量SYN请求或零窗口扫描。
sudo vim /etc/fail2ban/filter.d/custom-scan.conf
写入以下内容(以匹配ssh日志中“Failed password”为例,但扫描往往不一定是密码错误,也可能是连接超时。
这里用更通用的[sshd]默认filter?
不,我们单独写一个:)
[Definition]
failregex = ^%(__prefix_line)sConnection from port \d+ on .* invalid$
^%(__prefix_line)sDid not receive identification string from $
^%(__prefix_line)sBad protocol version identification .* from $
ignoreregex =
这样匹配的是连接时未发送识别字符串、错误协议版本等典型扫描特征。
你可以根据实际日志调整正则。
第二步:编写自定义action——封禁前先判断地域
默认action直接封IP。
我们要改造它:在封禁前检查IP来源,如果是境内IP则跳过(避免误封),境外IP才加入黑名单。
创建自定义action文件:
sudo vim /etc/fail2ban/action.d/custom-block.conf
内容如下(使用iptables + ipset,并调用geoiplookup判断):
[Definition]
actionstart = ipset create fail2ban-custom hash:ip timeout 86400 2>/dev/null || true
iptables -I INPUT -m set --match-set fail2ban-custom src -j DROP
actionban = if [ "$(geoiplookup | grep -c 'Country: CN')" -eq 0 ]; then
ipset add fail2ban-custom
fi
actionunban = ipset del fail2ban-custom
注意:这里假设geoiplookup输出包含“Country: CN”表示中国IP;
如果不是中国IP则加入ipset。
如果你的服务器面向全球,请根据实际需求调整。
第三步:在jail.local中启用自定义规则
编辑/etc/fail2ban/jail.local(如果没有则创建):
sudo vim /etc/fail2ban/jail.local
追加以下配置:
[custom-scan]
enabled = true
filter = custom-scan
logpath = /var/log/auth.log
/var/log/secure
maxretry = 3
findtime = 300
bantime = 86400
action = custom-block
%(action_mwl)s # 同时发送邮件告警(可选)
注意:logpath请根据你的系统日志位置调整(CentOS用/var/log/secure,Ubuntu用/var/log/auth.log)。
如果还要监控其他服务(如nginx),可以加多条logpath。
第四步:重启fail2ban并验证效果
sudo systemctl restart fail2ban
sudo fail2ban-client status custom-scan
如果看到“Status for the jail: custom-scan”且“Currently banned: X”不为0,说明已生效。
你也可以手动模拟扫描:
# 从境外服务器尝试ssh连接失败几次
ssh invalid@your-server
# 然后查看fail2ban日志
sudo tail -f /var/log/fail2ban.log
当匹配到filter中的规则时,会看到类似“Ban <境外IP>”的行。
注意:这里的封禁是由自定义action执行的,只有境外IP才会被加入ipset。
避坑指南
- GeoIP数据库不完整:系统自带的geoiplookup可能不准确,特别是IPv6。建议使用MaxMind的GeoLite2库,但操作更复杂。初学者先用系统包。
- 误封国内IP:如果filter匹配过于宽松,国内IP也可能被触发规则。建议先小范围测试,先用
findtime和maxretry调松一点,比如5次/5分钟。 - iptables规则重启丢失:ipset在重启后会被清除,但fail2ban重启时会重新加载actionstart。确保actionstart中的ipset创建失败不会影响。
- 日志路径不对:自定义filter要匹配的日志,必须写在logpath里。如果监控nginx日志,要额外加logpath。
常见问题FAQ
Q:执行geoiplookup报“No such file or directory”?
A:没安装geoip-bin,或数据库文件缺失。运行sudo apt install geoip-bin或sudo yum install geoip。
Q:如何只拦截非中国IP,同时允许国内IP?
A:上面的action中grep -c 'Country: CN'就是判断中国;要想只拦截非中国,就把if条件改为等于0时封禁。
Q:filter的正则一直匹配不到怎么办?
A:先用fail2ban-regex /var/log/auth.log "你的正则"测试。也可以直接用系统自带的sshd filter,然后单独用action做地域判断。
总结
通过自定义fail2ban filter和action,你可以精确控制封禁策略:只对境外扫描IP执行封锁,避免误伤正常用户。
这套方案适用于所有Linux服务器,且完全开源免费。
如果你发现仍有大量国内IP扫描,可以进一步调整filter或考虑云端WAF。
建议先用一段时间,观察封禁日志,再微调参数。
如果你还想了解fail2ban的更多高级用法(如白名单、ipset持久化),欢迎继续翻阅本站相关文章。