CrowdSec Cloudflare 配置实战:让 Nginx 获取真实访客 IP,并通过 Cloudflare API 自动封禁暴力攻击

如果网站套上 Cloudflare 后,你在 Nginx 日志里看到的全是 172.70.x.x、104.16.x.x 等 Cloudflare 节点地址,那么后端安全系统实际上已经“失明”了。

攻击者可以不断尝试登录、扫描漏洞,CrowdSec 却可能把所有请求都归到 Cloudflare 节点名下。更危险的是,如果直接信任客户端提交的 X-Forwarded-For,攻击者还可以伪造 IP,让封禁机制误伤其他用户。

本文将搭建一条完整、可执行的防护链路:

Cloudflare 代理流量 → Nginx 恢复真实访客 IP → CrowdSec 分析日志 → Cloudflare API 在边缘封禁攻击者

最终效果是:攻击 IP 在 Cloudflare 边缘就会被拦截,请求不再进入源站,从而降低服务器连接数、带宽和应用层压力。

本文以 2026 年 9 月的常见部署方式为背景。CrowdSec、Cloudflare API 权限名称及套餐配额可能变化,生产部署前请同时复核官方文档和当前版本生成的示例配置。

一、为什么仅在源站封 IP 还不够

传统 Fail2ban 或本机防火墙通常在源站执行封禁:

攻击者
   ↓
Cloudflare
   ↓
源站防火墙拦截

这种方式虽然能保护应用,但攻击流量已经经过 Cloudflare 到达服务器,依然会占用:

  • 源站网络带宽;
  • TCP/TLS 连接资源;
  • Nginx 工作连接;
  • Cloudflare 到源站的回源额度;
  • 日志和安全系统处理能力。

把 CrowdSec 的封禁决定同步到 Cloudflare 后,链路会变成:

攻击者
   ↓
Cloudflare 边缘节点直接拦截
   ✕
不再回源

这就是 Cloudflare API 自动封禁的核心价值:CrowdSec 负责检测和决策,Cloudflare 负责在全球边缘执行拦截。

二、整个方案的工作原理

完整数据流如下:

访客真实 IP:198.51.100.23
          │
          ▼
Cloudflare 边缘节点
          │
          │ CF-Connecting-IP: 198.51.100.23
          ▼
Nginx Real IP 模块
          │
          │ 将 $remote_addr 恢复为 198.51.100.23
          ▼
Nginx access.log
          │
          ▼
CrowdSec 解析器与检测场景
          │
          │ 生成封禁 decision
          ▼
Cloudflare Bouncer
          │
          │ 调用 Cloudflare API
          ▼
Cloudflare IP List / WAF 规则

这里有三个必须同时成立的安全条件:

  1. Nginx 只信任 Cloudflare 官方 IP 段传来的真实 IP 请求头。
  2. 源站 80/443 端口应尽量只允许 Cloudflare 节点访问。
  3. Nginx 日志必须把恢复后的 $remote_addr 写进去。

缺少任何一步,最终封禁都有可能失效或被伪造。

三、准备工作与环境依赖

本文假设使用以下环境:

  • 一台 Linux 服务器,示例以 Debian/Ubuntu 为主;
  • 域名已经接入 Cloudflare;
  • DNS 记录开启了橙色云朵代理;
  • Nginx 位于源站或宿主机;
  • Docker Engine 和 Docker Compose Plugin 已安装;
  • 拥有 Cloudflare Zone ID、Account ID;
  • 能创建 Cloudflare API Token;
  • 服务器时间和时区正常。

确认 Docker 环境:

docker --version
docker compose version

确认 Nginx 编译时包含 Real IP 模块:

nginx -V 2>&1 | grep -o 'http_realip_module'

如果输出:

http_realip_module

说明可以继续。

大多数发行版官方 Nginx 包都已包含该模块。如果使用自行编译的 Nginx,需要添加:

--with-http_realip_module

建议先备份配置

sudo cp -a /etc/nginx /etc/nginx.backup-$(date +%F-%H%M%S)

同时确认当前日志位置:

sudo nginx -T 2>/dev/null | grep -E 'access_log|log_format'

本文默认日志为:

/var/log/nginx/access.log

如果你的 Nginx 运行在容器中,则需要把同一个日志卷以只读方式挂载给 CrowdSec。

四、让 Nginx 安全获取 Cloudflare 后的真实 IP

4.1 为什么不能直接信任 X-Forwarded-For

下面这种配置非常危险:

real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0;

它等于告诉 Nginx:

无论请求来自哪里,都无条件相信对方声明的客户端 IP。

攻击者可以绕过 Cloudflare,直接访问源站并发送:

X-Forwarded-For: 8.8.8.8

这样日志和 CrowdSec 看到的来源就可能变成伪造地址。

正确做法是:

  • 只信任 Cloudflare 官方公布的 IPv4/IPv6 网段;
  • 使用 Cloudflare 提供的 CF-Connecting-IP;
  • 最好同时在网络层禁止外部绕过 Cloudflare 访问源站。

4.2 自动生成 Cloudflare可信代理配置

创建配置目录:

sudo install -d -m 0755 /etc/nginx/snippets

下载 Cloudflare 官方 IP 段:

curl -fsSL https://www.cloudflare.com/ips-v4 -o /tmp/cloudflare-ips-v4.txt
curl -fsSL https://www.cloudflare.com/ips-v6 -o /tmp/cloudflare-ips-v6.txt

先确认文件不是空的:

test -s /tmp/cloudflare-ips-v4.txt
test -s /tmp/cloudflare-ips-v6.txt

生成 Nginx 配置:

{
  echo "# Generated from Cloudflare official IP ranges"
  echo "# Updated at: $(date -u +'%Y-%m-%dT%H:%M:%SZ')"

  awk 'NF {print "set_real_ip_from " $1 ";"}' /tmp/cloudflare-ips-v4.txt
  awk 'NF {print "set_real_ip_from " $1 ";"}' /tmp/cloudflare-ips-v6.txt

  echo "real_ip_header CF-Connecting-IP;"
  echo "real_ip_recursive on;"
} | sudo tee /etc/nginx/snippets/cloudflare-real-ip.conf >/dev/null

查看结果:

sudo cat /etc/nginx/snippets/cloudflare-real-ip.conf

内容应该类似:

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;

# 此处还会有其他 IPv4 和 IPv6 网段

real_ip_header CF-Connecting-IP;
real_ip_recursive on;

不要只复制上面两个示例网段。 Cloudflare 会维护和调整完整列表,应以官方地址为准:

  • https://www.cloudflare.com/ips-v4
  • https://www.cloudflare.com/ips-v6

4.3 在 Nginx 的 http 块中加载配置

编辑 /etc/nginx/nginx.conf:

sudo nano /etc/nginx/nginx.conf

在 http {} 内添加:

http {
    include /etc/nginx/snippets/cloudflare-real-ip.conf;

    # 其他配置
}

不要把它放进一个只服务于其他域名的无关 server {} 中。如果整台服务器的所有网站都通过 Cloudflare,放在 http {} 最直观。

4.4 配置包含真实访客 IP 的 Nginx 日志

在 http {} 中定义日志格式:

log_format crowdsec_combined
    '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" '
    'host="$host" '
    'cf_ray="$http_cf_ray" '
    'proxy_peer="$realip_remote_addr"';

然后在对应的网站 server {} 中使用:

server {
    listen 443 ssl http2;
    server_name example.com;

    access_log /var/log/nginx/access.log crowdsec_combined;

    # 其他站点配置
}

几个变量的含义如下:

变量 含义
$remote_addr Real IP 模块处理后的真实访客 IP
$realip_remote_addr 修改前的连接来源,通常是 Cloudflare 节点 IP
$http_cf_ray Cloudflare 为请求生成的 Ray ID,可用于排错
$http_cf_connecting_ip 原始请求头内容,不建议直接作为日志首字段

CrowdSec 的 Nginx 解析器通常从日志开头读取来源 IP,因此这里最关键的是:

'$remote_addr ...'

4.5 检查并重载 Nginx

sudo nginx -t
sudo systemctl reload nginx

如果使用容器化 Nginx,则执行对应容器内的测试和重载命令,例如:

docker exec nginx nginx -t
docker exec nginx nginx -s reload

访问网站后查看日志:

sudo tail -f /var/log/nginx/access.log

正确结果应类似:

198.51.100.23 - - [10/Sep/2026:12:30:21 +0800] "GET / HTTP/2.0" 200 ...

并且末尾的代理节点字段类似:

proxy_peer="172.70.92.10"

这说明:

  • $remote_addr 已经是访客 IP;
  • $realip_remote_addr 仍保留 Cloudflare 节点 IP;
  • CrowdSec 可以根据真实访客 IP 进行聚合和封禁。

五、必须阻止攻击者绕过 Cloudflare 直连源站

恢复真实 IP 并不代表源站已经安全。

如果攻击者知道服务器公网 IP,仍可能直接访问源站。此时他可以:

  • 绕过 Cloudflare WAF;
  • 绕过 Cloudflare Rate Limiting;
  • 绕过边缘封禁;
  • 直接消耗源站带宽;
  • 测试是否存在未代理的虚拟主机。

5.1 使用云防火墙或安全组限制 80/443

最推荐在云厂商安全组中配置:

  • SSH 端口只允许管理员固定 IP;
  • 80/443 只允许 Cloudflare 官方 IPv4 和 IPv6 网段;
  • 拒绝其他互联网来源访问 Web 端口。

如果使用 UFW,必须先保留 SSH,防止把自己锁在服务器外:

sudo ufw allow from YOUR_ADMIN_IP to any port 22 proto tcp

然后根据 Cloudflare 官方列表逐条允许 80/443。可以使用下面的脚本生成规则:

while read -r cidr; do
  sudo ufw allow from "$cidr" to any port 80 proto tcp
  sudo ufw allow from "$cidr" to any port 443 proto tcp
done < /tmp/cloudflare-ips-v4.txt

while read -r cidr; do
  sudo ufw allow from "$cidr" to any port 80 proto tcp
  sudo ufw allow from "$cidr" to any port 443 proto tcp
done < /tmp/cloudflare-ips-v6.txt

确认规则无误后,再拒绝其他来源:

sudo ufw deny 80/tcp
sudo ufw deny 443/tcp
sudo ufw enable
sudo ufw status numbered

如果 Nginx 通过 Docker 的 ports: 暴露端口,Docker 的 iptables/nftables 转发规则可能绕过部分 UFW 行为。此时优先使用云安全组,或者在 Docker 的 DOCKER-USER 链中实施限制。

5.2 进一步启用 Authenticated Origin Pulls

Cloudflare Authenticated Origin Pulls 可以让源站验证连接确实来自持有对应客户端证书的 Cloudflare 节点。

它不能替代网络层访问控制,但可以作为第二道验证。启用前应确认:

  • Nginx 客户端证书验证配置正确;
  • Cloudflare 控制台对应功能已开启;
  • 已准备回滚通道;
  • 不会影响健康检查或其他合法回源服务。

5.3 避免源站 IP 泄漏

常见泄漏入口包括:

  • 同一台服务器上的未代理子域名;
  • 邮件服务器 DNS 记录;
  • 历史 DNS 数据;
  • Git 仓库中的配置;
  • 错误页面或监控平台;
  • 直接暴露的对象存储、面板和 API。

如果源站 IP 已长期暴露且频繁被绕过,最彻底的办法通常是更换源站公网 IP,并在上线前完成 Cloudflare-only 防火墙限制。

六、使用 Docker 部署 CrowdSec

6.1 创建项目目录

sudo mkdir -p /opt/crowdsec
sudo chown -R "$USER":"$USER" /opt/crowdsec
cd /opt/crowdsec

创建 acquis.yaml:

cat > acquis.yaml <<'EOF'
filenames:
  - /var/log/nginx/access.log
labels:
  type: nginx
EOF

这里的 type: nginx 非常重要,它告诉 CrowdSec 使用 Nginx 相关解析链。

6.2 编写 Docker Compose 文件

创建 compose.yaml:

services:
  crowdsec:
    image: crowdsecurity/crowdsec:${CROWDSEC_VERSION:-latest}
    container_name: crowdsec
    restart: unless-stopped

    environment:
      COLLECTIONS: "crowdsecurity/nginx"
      TZ: "Asia/Shanghai"

    volumes:
      # CrowdSec 持久化数据库
      - crowdsec-db:/var/lib/crowdsec/data

      # CrowdSec 配置与 Hub 数据
      - crowdsec-config:/etc/crowdsec

      # 日志采集配置
      - ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro

      # 只读挂载宿主机 Nginx 日志
      - /var/log/nginx:/var/log/nginx:ro

    ports:
      # 仅供本机 Cloudflare Bouncer 连接 LAPI
      - "127.0.0.1:8080:8080"

    security_opt:
      - no-new-privileges:true

volumes:
  crowdsec-db:
  crowdsec-config:

启动服务:

docker compose up -d

查看启动日志:

docker compose logs -f crowdsec

生产环境不建议长期使用漂移的 latest 标签。确认部署稳定后,应将 CROWDSEC_VERSION 固定为已经测试过的版本或镜像摘要。

例如创建 .env:

cat > .env <<'EOF'
CROWDSEC_VERSION=请替换为你已验证的版本号
EOF

具体版本应从 CrowdSec 官方镜像仓库或发行说明中确认,不要盲目照抄过时教程里的版本号。

6.3 检查 Nginx Collection

docker compose exec crowdsec cscli collections list

确认 crowdsecurity/nginx 已启用。

如未安装,可执行:

docker compose exec crowdsec cscli collections install crowdsecurity/nginx
docker compose restart crowdsec

检查解析器和检测场景:

docker compose exec crowdsec cscli parsers list
docker compose exec crowdsec cscli scenarios list

6.4 检查 CrowdSec 是否正在读取日志

docker compose exec crowdsec cscli metrics

重点观察:

  • Acquisition Metrics;
  • Parser Metrics;
  • Nginx 日志读取行数;
  • Parsed 和 Unparsed 数量;
  • Scenario Metrics。

也可以查看 CrowdSec 日志:

docker compose logs --tail=200 crowdsec

如果访问网站后 Acquisition 的读取数量仍为零,通常是日志路径、挂载路径或权限配置错误。

七、理解“网站暴力破解防护”的检测边界

安装 Nginx Collection 并不等于 CrowdSec 能自动理解所有网站的业务登录逻辑。

CrowdSec 最容易识别的是:

  • 大量 401、403、404;
  • 常见漏洞探测;
  • 恶意 User-Agent;
  • 敏感路径扫描;
  • 高频访问异常;
  • 已知攻击行为模式。

但许多网站登录失败时仍返回:

HTTP/1.1 200 OK

仅在 HTML 或 JSON 中提示“密码错误”。对 Nginx 日志来说,成功登录和失败登录可能都是:

POST /login 200

CrowdSec 无法仅凭默认 access log 判断哪个请求失败。

更可靠的处理方式

按照优先级,可选择:

  1. 让应用把登录失败写入结构化安全日志;
  2. 使用对应应用的 CrowdSec Collection;
  3. 让失败登录返回明确的 401 或 403;
  4. 针对固定登录路径编写本地 Scenario;
  5. 同时使用 Cloudflare Rate Limiting 和 Turnstile。

WordPress、Nextcloud、面板程序、SSH、邮件服务等应优先查找 CrowdSec Hub 中是否存在适配的 Collection:

docker compose exec crowdsec cscli hub list

搜索相关组件:

docker compose exec crowdsec cscli hub list | grep -i wordpress
docker compose exec crowdsec cscli hub list | grep -i ssh

安装前请使用当前版本的 cscli hub list 确认真实存在的组件名称,不要根据旧博客凭空安装。

八、创建 Cloudflare API Token

Cloudflare Bouncer 需要调用 API,把 CrowdSec 决策同步为 Cloudflare 边缘规则或 IP 列表。

8.1 获取 Account ID 和 Zone ID

登录 Cloudflare 控制台,进入对应域名。通常可以在域名概览页找到:

  • Account ID
  • Zone ID

不要混淆两者:

  • Account ID 标识 Cloudflare 账户;
  • Zone ID 标识具体域名区域。

8.2 创建最小权限 Token

在 Cloudflare API Token 页面创建自定义 Token。

官方 CrowdSec Cloudflare Bouncer 不同版本可能使用不同的 Cloudflare API 能力,例如:

  • 账户级 IP/Rules Lists;
  • Zone WAF 或 Firewall 规则;
  • 旧版 Firewall Access Rules。

因此,应以当前安装版本自带的配置模板和官方 README 所列权限为准。不要为了省事使用 Global API Key。

常见所需权限类别可能包括:

  • Account Rules Lists / Account Filter Lists:Edit;
  • Zone Firewall Services 或 Zone WAF:Edit;
  • Zone:Read。

资源范围应尽量限制为:

  • 指定 Cloudflare Account;
  • 指定 Zone;
  • 不要授权所有账户和所有域名,除非确有需要。

不同 Cloudflare 套餐对列表数量、规则数量和列表容量可能有不同限制。请以当前控制台和 Cloudflare Limits 文档为准。

8.3 验证 Token 是否有效

将 Token 放入临时环境变量:

read -rsp "Cloudflare API Token: " CF_API_TOKEN
echo
export CF_API_TOKEN

调用官方验证接口:

curl -fsS \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  https://api.cloudflare.com/client/v4/user/tokens/verify

正常响应中应包含:

{
  "success": true
}

验证结束后,可以清除当前 Shell 中的变量:

unset CF_API_TOKEN

不要把 Token 直接写入 Shell 历史、公开 Git 仓库、截图或工单。

九、配置 CrowdSec Cloudflare Bouncer

CrowdSec 本身负责分析和生成决定,真正执行封禁的是 Remediation Component,也就是 Bouncer。

9.1 生成 Bouncer API Key

在 CrowdSec 容器中注册一个 Bouncer:

cd /opt/crowdsec

docker compose exec crowdsec \
  cscli bouncers add cloudflare-bouncer

命令会输出一串 API Key,例如:

API key for 'cloudflare-bouncer':

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这串 Key 通常只完整显示一次,立即保存到密码管理器。

查看已注册 Bouncer:

docker compose exec crowdsec cscli bouncers list

9.2 安装官方 Cloudflare Bouncer

在 Debian/Ubuntu 宿主机上,可以先添加 CrowdSec 官方软件源:

curl -s https://install.crowdsec.net | sudo sh
sudo apt update

然后安装:

sudo apt install crowdsec-cloudflare-bouncer

安装脚本和仓库地址具有时效性。如果命令返回 404、签名错误或包不存在,请停止操作并查看 CrowdSec 官方 Cloudflare Remediation Component 文档,不要从不明第三方站点下载二进制文件。

9.3 找到实际配置文件

常见路径为:

/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml

先通过软件包确认:

dpkg -L crowdsec-cloudflare-bouncer | grep -E '\.ya?ml$'

备份原始文件:

sudo cp \
  /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml \
  /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml.backup

9.4 填写 Bouncer 配置

当前常见配置结构如下,但字段应以安装包生成的样例为准:

crowdsec_lapi_url: http://127.0.0.1:8080/
crowdsec_lapi_key: "替换为刚才生成的_BOUNCER_API_KEY"
crowdsec_update_frequency: 10s

cloudflare_config:
  accounts:
    - id: "替换为_CLOUDFLARE_ACCOUNT_ID"
      token: "替换为_CLOUDFLARE_API_TOKEN"
      ip_list_prefix: "crowdsec"
      default_action: "block"

      zones:
        - zone_id: "替换为_CLOUDFLARE_ZONE_ID"
          actions:
            - block

注意 YAML 缩进必须使用空格,不能混入 Tab。

设置严格权限:

sudo chown root:root \
  /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml

sudo chmod 600 \
  /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml

CrowdSec Cloudflare Bouncer 的配置结构可能随版本调整。如果安装包中的默认文件与上面不同,应保留默认结构,只替换对应的 LAPI 地址、Bouncer Key、Account ID、Zone ID 和 Token,而不是强行覆盖为旧格式。

9.5 启动并检查 Bouncer

sudo systemctl enable --now crowdsec-cloudflare-bouncer
sudo systemctl status crowdsec-cloudflare-bouncer

查看实时日志:

sudo journalctl \
  -u crowdsec-cloudflare-bouncer \
  -f

回到 CrowdSec 检查 Bouncer 是否连接:

docker compose exec crowdsec cscli bouncers list

如果显示最近拉取时间,说明 Cloudflare Bouncer 已成功连接 CrowdSec LAPI。

十、验证 Cloudflare API 自动封禁是否真正生效

10.1 添加一个短期测试决定

不要直接封禁自己当前使用的公网 IP,除非你准备了备用网络、控制台或救援通道。

可以先使用一个你可控的测试 IP:

docker compose exec crowdsec \
  cscli decisions add \
  --ip TEST_PUBLIC_IP \
  --duration 2m \
  --reason "cloudflare-bouncer-test"

查看决策:

docker compose exec crowdsec cscli decisions list

等待 Bouncer 的同步周期,然后查看日志:

sudo journalctl \
  -u crowdsec-cloudflare-bouncer \
  --since "5 minutes ago"

同时进入 Cloudflare 控制台,检查 Bouncer 创建或维护的:

  • IP List;
  • WAF/Firewall 规则;
  • 安全事件;
  • 对应列表条目。

不同 Bouncer 版本采用的 Cloudflare 对象可能不同,因此具体入口应以日志和当前官方文档为准。

10.2 删除测试决定

docker compose exec crowdsec \
  cscli decisions delete \
  --ip TEST_PUBLIC_IP

确认已经删除:

docker compose exec crowdsec cscli decisions list

Bouncer 通常会在后续同步周期中清除对应 Cloudflare 条目,而不是在执行删除命令的同一毫秒立即消失。

10.3 观察真实检测结果

查看告警:

docker compose exec crowdsec cscli alerts list

查看封禁决定:

docker compose exec crowdsec cscli decisions list

查看整体指标:

docker compose exec crowdsec cscli metrics

如果有告警但没有封禁决定,需要检查对应 Scenario 是否带有:

remediation: true

某些场景可能只产生观察性告警,不自动生成可供 Bouncer 执行的封禁决定。

十一、生产环境强化建议

11.1 不要无限期封禁所有攻击者

动态住宅 IP、移动网络和运营商 NAT 可能被多人共享。永久封禁很容易误伤后来的正常用户。

比较合理的策略是:

  • 低强度扫描:数分钟到数小时;
  • 明确暴力破解:数小时到数天;
  • 高置信恶意 IP:更长时间;
  • 误报风险高的业务:优先 Challenge,而不是 Block。

具体时长应结合网站类型、用户分布和攻击强度调整。

11.2 管理员 IP 不应只靠静态白名单

管理员 IP 可能变化,尤其是在移动网络和家庭宽带中。更稳妥的做法包括:

  • 管理后台只允许 VPN;
  • 使用 Cloudflare Access;
  • 开启多因素认证;
  • 使用硬件安全密钥;
  • 后台路径启用 Turnstile;
  • 登录接口设置独立限速;
  • CrowdSec 白名单只作为辅助措施。

11.3 同时保留 IPv4 和 IPv6

如果只配置 Cloudflare IPv4 网段,IPv6 回源可能出现:

  • 无法恢复真实 IP;
  • 被防火墙拒绝;
  • CrowdSec 记录到 Cloudflare IPv6 节点;
  • 攻击者通过 IPv6 绕开部分策略。

因此必须同时维护:

https://www.cloudflare.com/ips-v4
https://www.cloudflare.com/ips-v6

11.4 定期更新 Cloudflare IP 段

Cloudflare IP 段不会频繁变化,但生产环境仍应定期同步。

可以编写受控脚本,流程应包括:

  1. 下载到临时文件;
  2. 验证文件非空;
  3. 生成临时 Nginx 配置;
  4. 执行 nginx -t;
  5. 测试通过后原子替换;
  6. 重载 Nginx;
  7. 同步更新云防火墙或安全组。

不要让 Cron 直接覆盖正式配置后无条件 reload,否则一次下载异常就可能导致全站故障。

11.5 关注 Cloudflare 列表与规则配额

大量长时间封禁可能触及:

  • 单列表最大条目数;
  • 每账户列表数量;
  • WAF 自定义规则数量;
  • API 请求频率;
  • 当前套餐限制。

CrowdSec Bouncer 的日志中如果出现配额错误,应优先:

  • 缩短决策持续时间;
  • 减少低置信度封禁;
  • 检查过期条目是否被及时清理;
  • 使用 CrowdSec 社区信誉数据提高判断质量;
  • 根据当前 Cloudflare 套餐调整策略。

十二、常见排错指南(FAQ / Troubleshooting)

1. Nginx 日志里仍然是 Cloudflare IP

先查看完整生效配置:

sudo nginx -T 2>/dev/null | grep -A5 -B5 'real_ip_header'

应看到:

real_ip_header CF-Connecting-IP;

并且存在完整的 Cloudflare:

set_real_ip_from ...

然后确认 DNS 记录确实开启橙色云朵。如果是灰色云朵,Cloudflare 不会代理请求,也不会添加可信的 CF-Connecting-IP。

还要确认日志使用的是:

$remote_addr

而不是:

$realip_remote_addr

后者在启用 Real IP 模块后通常保存的是修改前的 Cloudflare 节点地址。

2. 能否直接把 $http_cf_connecting_ip 写进日志

技术上可以,但不推荐把它作为唯一可信来源。

$http_cf_connecting_ip

只是原始 HTTP 请求头。只有请求确定来自 Cloudflare 时,它才可信。

更安全的方式是:

  1. 使用 set_real_ip_from 限制可信代理;
  2. 让 Real IP 模块验证来源;
  3. 最终记录 $remote_addr。

3. CrowdSec 的 Acquisition Metrics 一直是零

检查宿主机日志是否存在:

sudo ls -lah /var/log/nginx/access.log

检查容器内是否可见:

docker compose exec crowdsec \
  ls -lah /var/log/nginx/access.log

读取最后几行:

docker compose exec crowdsec \
  tail -n 5 /var/log/nginx/access.log

如果容器内不存在,说明 Docker 卷映射错误。

如果 Nginx 也运行在 Docker 中,应使用共享命名卷,而不是假设日志一定存在于宿主机 /var/log/nginx。

4. CrowdSec 读取了日志,但 Parsed 数量很低

检查当前 Nginx 日志格式是否过度自定义。

CrowdSec 的 Nginx Parser 通常能识别常见 Combined Log Format。如果把 JSON、字段顺序或请求行改得过于特殊,就可能需要专门的 parser。

查看 CrowdSec 日志:

docker compose logs --tail=300 crowdsec

查看指标:

docker compose exec crowdsec cscli metrics

建议先使用接近标准 Combined 格式的日志,再逐步增加附加字段。

5. Bouncer 报 connection refused 127.0.0.1:8080

检查 CrowdSec LAPI 是否映射到宿主机:

sudo ss -lntp | grep ':8080'

应看到类似:

127.0.0.1:8080

检查容器:

docker compose ps
docker compose logs --tail=100 crowdsec

从宿主机测试端口:

curl -v http://127.0.0.1:8080/

即使根路径返回 404,也说明 TCP 和 HTTP 服务可达;连接拒绝则意味着端口没有监听。

6. Bouncer 报 401 或未认证

通常原因是:

  • Bouncer API Key 填错;
  • Key 前后包含空格;
  • 修改配置后未重启;
  • Key 属于另一个 CrowdSec LAPI;
  • 删除并重建了 CrowdSec 数据卷。

重新注册一个 Key:

docker compose exec crowdsec \
  cscli bouncers add cloudflare-bouncer-new

更新配置后重启:

sudo systemctl restart crowdsec-cloudflare-bouncer

7. Cloudflare API 返回 403

403 通常不是网络问题,而是权限或资源范围问题。

重点检查:

  • Token 是否具有当前 Bouncer 所需的列表/WAF权限;
  • Token 是否绑定了正确 Account;
  • Zone 是否在同一个 Account 下;
  • Zone ID 和 Account ID 是否填反;
  • 当前套餐是否支持 Bouncer 使用的规则或列表能力;
  • Token 是否已被撤销或过期。

使用验证接口只能证明 Token 存在,不能证明它拥有修改指定 Zone 或列表的权限。

8. CrowdSec 有 Decision,但 Cloudflare 没有对应封禁

依次检查:

docker compose exec crowdsec cscli decisions list
docker compose exec crowdsec cscli bouncers list
sudo systemctl status crowdsec-cloudflare-bouncer
sudo journalctl -u crowdsec-cloudflare-bouncer --since "15 minutes ago"

常见原因包括:

  • Bouncer 没有连接 LAPI;
  • Decision 已经过期;
  • 场景被 Bouncer 的过滤条件排除;
  • IP 属于 Bouncer 不处理的范围;
  • Cloudflare 列表达到配额;
  • API Token 权限不足;
  • 配置文件缩进错误;
  • Bouncer 尚未到下一次同步周期。

9. 为什么登录爆破没有触发 CrowdSec

首先检查登录失败时 Nginx 的状态码:

sudo tail -f /var/log/nginx/access.log

如果每次失败登录都是:

POST /login HTTP/2.0" 200

默认 Nginx 日志并没有足够信息判断它是失败登录。

解决办法是:

  • 接入应用自身的认证失败日志;
  • 使用对应应用的 CrowdSec Collection;
  • 让应用输出结构化安全事件;
  • 编写与业务语义匹配的 Parser 和 Scenario;
  • 配合 Cloudflare Rate Limiting 或 Turnstile。

不要为了“让规则触发”而简单封禁所有高频 /login 请求,否则企业 NAT、密码管理器和移动网络用户可能被误伤。

10. Cloudflare 开启 Pseudo IPv4 后 IP 不一致

Cloudflare 的 Pseudo IPv4 模式可能影响部分请求头行为,尤其是 IPv6 访客。

如果选择覆盖请求头的模式,应同时关注:

CF-Connecting-IPv6

对于需要准确保留 IPv6 来源的 CrowdSec 部署,建议复核 Cloudflare 当前的 Pseudo IPv4 文档,避免使用会覆盖真实来源信息的模式。

11. 使用 Cloudflare Workers 后真实 IP 异常

如果请求经过 Worker,并由 Worker 再发起子请求,CF-Connecting-IP 的行为可能与普通代理链路不同。

应确认:

  • Worker 是否改写请求头;
  • 是否跨 Zone 发起请求;
  • Worker 路由是否覆盖当前域名;
  • 日志中的 CF-Ray 是否符合预期。

不要仅凭一个请求头推断链路,应同时记录:

$remote_addr
$realip_remote_addr
$http_cf_connecting_ip
$http_cf_ray

排错完成后,可以减少不必要的敏感日志字段。

12. 误封了自己的公网 IP 怎么办

如果还能进入服务器:

docker compose exec crowdsec \
  cscli decisions delete \
  --ip YOUR_PUBLIC_IP

然后观察 Bouncer 同步:

sudo journalctl -u crowdsec-cloudflare-bouncer -f

如果已经无法访问网站,可以:

  • 切换手机网络;
  • 使用云厂商控制台;
  • 暂时在 Cloudflare 控制台删除对应列表条目;
  • 暂停 Bouncer 创建的规则;
  • 使用服务器救援终端。

不要直接删除整个 CrowdSec 数据卷,这会同时丢失数据库和决策状态,且未必立即清除 Cloudflare 侧已有规则。

十三、最终检查清单

上线前逐项确认:

  • [ ] 域名已开启 Cloudflare 代理;
  • [ ] Nginx 已加载最新 Cloudflare IPv4 和 IPv6 网段;
  • [ ] real_ip_header 使用 CF-Connecting-IP;
  • [ ] 未信任 0.0.0.0/0 或所有代理来源;
  • [ ] Nginx 日志首字段是恢复后的 $remote_addr;
  • [ ] 日志中能够看到真实访客 IP;
  • [ ] 源站 80/443 只允许 Cloudflare 回源;
  • [ ] CrowdSec 容器可以只读访问 Nginx 日志;
  • [ ] crowdsecurity/nginx Collection 已安装;
  • [ ] cscli metrics 显示日志正在被解析;
  • [ ] Cloudflare Token 使用最小权限;
  • [ ] CrowdSec LAPI 仅监听 127.0.0.1:8080;
  • [ ] Cloudflare Bouncer 已成功连接 LAPI;
  • [ ] 短期测试 Decision 可以同步到 Cloudflare;
  • [ ] Decision 删除后,Cloudflare 条目能够被清理;
  • [ ] 已准备误封后的备用管理通道;
  • [ ] 已监控 Cloudflare 列表、规则和 API 配额。

结语

一套可靠的 CrowdSec Cloudflare 配置,重点并不只是“安装一个容器”或“填入一串 API Token”,而是建立可信的数据闭环:

  1. Cloudflare 把真实访客地址放入可信请求头;
  2. Nginx 只接受 Cloudflare 节点提供的该请求头;
  3. Nginx 把恢复后的真实 IP 写入访问日志;
  4. CrowdSec 根据真实 IP 分析攻击行为;
  5. Cloudflare Bouncer 把封禁决定同步到边缘;
  6. 防火墙阻止攻击者绕过 Cloudflare直连源站。

其中最关键的一条原则是:

真实 IP 不是“读取一个 Header”那么简单,而是建立一条只信任指定反向代理的安全边界。

完成这条链路后,网站暴力破解防护就不再局限于源站本机。扫描器、撞库工具和高频攻击者可以在 Cloudflare 边缘被提前拦截,而 CrowdSec 仍保留开放、可审计、可扩展的检测与决策能力。

⚡ 极客核心要点提炼 可供 AI 智能体与搜索引擎引用检索

本文主题:CrowdSec Cloudflare 配置实战:让 Nginx 获取真实访客 IP,并通过 Cloudflare API 自动封禁暴力攻击

引用出处:https://isoziyuan.com/p/100122/(作者:Isoziyuan · 发布于爱搜资源网)