Fail2ban + Cloudflare 双层防护方案

问题背景

服务器使用 Cloudflare CDN,Fail2ban 能正确检测到原始攻击 IP,但封禁无效。

原因

解决方案

使用 iptables + Cloudflare API 双层封禁:


实施步骤

1. 获取 Cloudflare Global API Key

  1. 访问:https://dash.cloudflare.com/profile/api-tokens

  2. 滚动到底部 API Keys 区域

  3. 找到 Global API Key → 点击 View → 输入密码

  4. 复制 API Key(妥善保存)

2. 修复 Cloudflare Action 配置

Fail2ban 自带的 cloudflare.conf 存在 bug,需要修复:

# 备份原文件
cp /etc/fail2ban/action.d/cloudflare.conf /etc/fail2ban/action.d/cloudflare.conf.bak

# 编辑配置

vim /etc/fail2ban/action.d/cloudflare.conf

找到 actionunban 部分(约第 40-45 行),将:

actionunban = curl -s -o /dev/null -X DELETE -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
              https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$(curl -s -X GET -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
              'https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1' | cut -d'"' -f6)

替换为

actionunban = rule_id=$(curl -s -X GET \
              -H 'X-Auth-Email: <cfuser>' \
              -H 'X-Auth-Key: <cftoken>' \
              'https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1' \
              | jq -r '.result[0].id // empty'); \
              if [ -n "$rule_id" ]; then \
                curl -s -X DELETE \
                -H 'X-Auth-Email: <cfuser>' \
                -H 'X-Auth-Key: <cftoken>' \
                "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$rule_id"; \
              fi

修复说明

安装 jq(如果没有)

dnf install jq -y

3. 配置 Jail(启用双层封禁)

创建或编辑 /etc/fail2ban/jail.d/nginx-botsearch.local

vim /etc/fail2ban/jail.d/nginx-botsearch.local

内容:

[nginx-botsearch]
enabled  = true
port     = http,https
filter   = nginx-botsearch
logpath  = /var/log/nginx/access.log
maxretry = 2
findtime = 600
bantime  = 86400

# 双层封禁:iptables + Cloudflare
action = iptables-multiport[name=nginx-botsearch, port="http,https", protocol=tcp]
         cloudflare[cfuser="你的cloudflare邮箱", cftoken="你的Global API Key"]

参数说明

安全加固

chmod 640 /etc/fail2ban/jail.d/nginx-botsearch.local

4. 测试与验证

# 测试配置语法
fail2ban-client -t

# 重启 Fail2ban
systemctl restart fail2ban

# 确认 jail 状态
fail2ban-client status nginx-botsearch

功能测试

# 手动封禁测试 IP
fail2ban-client set nginx-botsearch banip 1.1.1.1

# 验证点 1:检查 iptables
iptables -L f2b-nginx-botsearch -v -n | grep 1.1.1.1
# 应该看到:1.1.1.1 DROP

# 验证点 2:检查 Cloudflare
# 访问:https://dash.cloudflare.com/[账号ID]/[域名]/security/waf/tools
# 应该看到:IP=1.1.1.1 的 Block 规则

# 测试解封
fail2ban-client set nginx-botsearch unbanip 1.1.1.1

# 验证点 3:双层都解封
iptables -L f2b-nginx-botsearch -v -n | grep 1.1.1.1  
# 无输出
# Cloudflare 后台规则也应消失

工作流程

  1. 攻击检测:Fail2ban 监控 Nginx 日志,检测到恶意行为

  2. 双层封禁

  3. 自动解封bantime 到期后,两层都自动解除封禁

效果


技术细节

为什么原版 actionunban 会失败?

原版实现(使用 cut 提取 ID):

... | cut -d'"' -f6

示例

# 正常情况
echo '{"result":[{"id":"abc123"}]}' | cut -d'"' -f6
# 输出:abc123 ✓

# API 返回增加字段
echo '{"result":[{"mode":"block","id":"abc123"}]}' | cut -d'"' -f6
# 输出:id (错误)✗

改良版本(使用 jq 解析):

... | jq -r '.result[0].id // empty'

API 端点说明

使用 User-level IP Access Rules

https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules

如果需要 Zone-level(仅影响单个域名),修改 API 端点为:

https://api.cloudflare.com/client/v4/zones/{zone_id}/firewall/access_rules/rules

成功标准


常见问题

Q1: Free 套餐 API rate limit 够用吗?
A: 1200 请求/5 分钟。每次封禁/解封各 1-2 个 API 请求,可应付正常攻击规模。

Q2: 如何查看当前封禁列表?

# 查看 Fail2ban 封禁状态
fail2ban-client status nginx-botsearch

# 查看 iptables 规则
iptables -L f2b-nginx-botsearch -v -n

# 查看 Cloudflare 规则
# 访问后台:Security > WAF > Tools

Q3: 如何清除所有封禁?

# 清除 Fail2ban 所有封禁(包括 iptables 和 Cloudflare)
fail2ban-client unban --all

Q4: 如果需要永久封禁某个 IP?
不建议用 Fail2ban 做永久封禁,应该:


维护建议

  1. 定期检查日志

    tail -f /var/log/fail2ban.log | grep -E "Ban|Unban|ERROR"
  2. 监控封禁数量

    fail2ban-client status nginx-botsearch | grep "Currently banned"
  3. Cloudflare 规则数量:Free 套餐接近 2 万条时考虑缩短 bantime

  4. 备份配置

    tar -czf fail2ban-backup-$(date +%Y%m%d).tar.gz \
        /etc/fail2ban/jail.d/ \
        /etc/fail2ban/action.d/cloudflare.conf

参考资料


发表评论

必填

选填

选填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。