我维护着一台 "Homebox",在我这里就是一台云服务器,托管着我各种个人应用。因为其中一些应用暴露在公网上,加上我经常把这台服务器当作新项目的快速部署容器,安全就是头等大事。
为了保护这套设置,我最近用 CrowdSec 加了一个 Web Application Firewall (WAF)。CrowdSec 是一个开源、协作的安全引擎。WAF 之所以必要,是因为它能提供"虚拟补丁":拦下恶意 payload,挡掉自动扫描器,即使底层代码还没更新,也能保护应用免受已知 CVE 的攻击。
我的 Homebox 用 Dokploy 管理,这是一个 PaaS 工具,底层用 Traefik 作为反向代理。在这篇教程里,我会讲清楚我是如何通过一个插件把 CrowdSec 的 AppSec 能力直接集成进 Traefik,搭出一个健壮、免费、保护我所有部署的 WAF。
(注:本教程用 Dokploy 来管理配置,但这些概念和 Docker Compose 文件同样可以用来保护任何标准的 Traefik 环境。)
CrowdSec 情报是怎么工作的
一个常见的误解是,CrowdSec 把你的流量日志发到云端去分析。这完全是错的。
- 本地优先: CrowdSec 在本地构建它的情报。引擎跑在你的服务器上,本地检测攻击,本地封禁。它甚至在完全没有网络的情况下也能工作。
- 保护隐私的社区(可选加入): 把情报分享给 CrowdSec 社区是严格可选的。如果你通过 CrowdSec Console 加入,它只分享匿名化的信号(比如"IP X 尝试了一个已知漏洞利用"),绝不分享原始日志、请求 payload 或敏感数据。你既能从社区黑名单中受益,又不会牺牲数据隐私。
标准 CrowdSec(4 层)vs. AppSec(7 层)
在进入配置之前,理解标准 CrowdSec 行为和较新的 AppSec 功能之间的区别很重要。它们工作在 OSI 模型完全不同的层上:
- 标准 CrowdSec(4 层): 在网络层面工作。它分析日志(比如 SSH 失败或 HTTP 404 洪水),通过与防火墙(比如 iptables)对话来完全封禁坏 IP。如果一个 IP 被封了,它连 TCP 连接到你的服务器都打不开。
- CrowdSec AppSec(7 层): 在应用层面作为真正的 WAF 工作。它不只看 IP,而是主动检查实际的 HTTP payload、headers 和 query 参数(比如查找特定的恶意
multipart/form-data),实时捕捉零日漏洞和具体的应用层 CVE 利用。
这里有一个简单的架构差异可视化:
┌────────────────────────────────────────────────────────┐
│ STANDARD CROWDSEC (Layer 4 - Network IP Blocking) │
└────────────────────────────────────────────────────────┘
Attacker IP ──> [ Firewall / iptables ] ──X (Blocked instantly)
▲
│ (Updates ban list)
[ CrowdSec Engine ] <── (Reads background logs)
-
-
┌────────────────────────────────────────────────────────┐
│ CROWDSEC APPSEC (Layer 7 - Payload Inspection WAF) │
└────────────────────────────────────────────────────────┘
Attacker HTTP Request ──> [ Traefik + Bouncer Plugin ]
│ ▲
(Sends payload) │ │ (Returns Allow/Block)
▼ │
[ CrowdSec AppSec Engine ]
(Inspects HTTP body)Traefik 插件和 CrowdSec Bouncer
Traefik 是一个现代反向代理,把流量路由到我的 Docker 容器。它最好的功能之一是插件,让你不需要重新编译二进制文件就能扩展 Traefik 的核心功能。
这套设置里,我们用的是 CrowdSec Traefik Bouncer 插件。它充当 Traefik 和 CrowdSec AppSec 引擎之间的桥梁。它在边缘拦截流量,在请求被允许到达应用之前,先问 CrowdSec 这个请求 payload 是安全的还是恶意的。
iWhat is a CrowdSec bouncer?+
Bouncer 是 CrowdSec 的执行组件。CrowdSec 引擎检测威胁并生成决策;Bouncer 在流量实际通过的地方执行这些决策。在这个例子里,Traefik 插件就是 Bouncer,因为它在 Traefik 内部,能在 HTTP 请求到达我的应用之前阻止或允许它。
分步实施指南
下面就是我在 Dokploy/Traefik 环境里的具体配置。
1. 部署 CrowdSec 容器
首先,我部署了 CrowdSec 引擎。我用了官方 Docker 镜像,并用 COLLECTIONS 环境变量在启动时自动安装 AppSec 规则。
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
environment:
GID: "${GID-1000}"
BOUNCER_KEY_TRAEFIK: "${BOUNCER_KEY_TRAEFIK}"
COLLECTIONS: "crowdsecurity/linux crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules"
volumes:
- ../files/acquis.yaml:/etc/crowdsec/acquis.yaml
- crowdsec-db:/var/lib/crowdsec/data/
- crowdsec-config:/etc/crowdsec/
security_opt:
- no-new-privileges:true
labels:
- traefik.enable=false
restart: always
networks:
- dokploy-network2. 关键的 LAPI 密钥
注意上面的 BOUNCER_KEY_TRAEFIK 变量。这就是 LAPI 密钥(Local API Key),它用来在 Traefik 插件和你的 CrowdSec 引擎之间做安全认证。没有它,Traefik 就无法和 CrowdSec 通信。
通过这个环境变量配置时,CrowdSec 容器不强制严格的密钥格式。你设置的任何字符串都成为有效密钥。你只需要生成一个强随机 32 字符串,在 Docker Compose 的 .env 文件里把它赋给 BOUNCER_KEY_TRAEFIK,之后再把同一个字符串填到 Traefik 中间件配置里。
3. 配置采集 (acquis.yaml)
只安装 AppSec collection 不够;你必须显式告诉 CrowdSec 开始监听 AppSec 流量。我把这个 acquis.yaml 文件挂载到容器里:
---
source: appsec
listen_addr: 0.0.0.0:7422
path: /
appsec_configs:
- crowdsecurity/appsec-default
labels:
type: appseciWhat is happening in this file?+
如果你刚接触 CrowdSec,可能会纳闷这个 acquis.yaml(采集)文件到底做什么。默认情况下,CrowdSec 通过读取日志文件(比如你的 Nginx 或 SSH 日志)在事后查找威胁。
但 WAF 需要实时捕捉东西,在它们打到你的应用之前。这个文件告诉 CrowdSec 别再看静态文件,而是启动一个活跃的 HTTP 监听器:
source: appsec:告诉引擎我们期待实时进入的 HTTP 流量。listen_addr: 0.0.0.0:7422:打开端口7422。这就是我们的 Traefik 插件要把每个 HTTP 请求送过来检查的地方。appsec_configs:加载我们刚才让容器安装的那些具体 AppSec 规则。
没有这个文件,监听器起不来,Traefik 没地方发请求,WAF 规则永远也执行不了。
4. Traefik 中间件 (middlewares.yaml)
接下来,我用 Dokploy 内置的文件系统编辑器配置了 Traefik 中间件。这告诉 Traefik 使用 Bouncer 插件,并把请求转发给 CrowdSec AppSec 引擎。
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: true
crowdsec:
plugin:
bouncer:
enabled: true
logLevel: DEBUG
crowdsecMode: appsec
crowdsecLapiKey: keystring # Replace with your LAPI key
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec:8080
crowdsecAppsecEnabled: true
crowdsecAppsecHost: crowdsec:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: true故障关闭保护
特别注意 crowdsecAppsecFailureBlock: true 和 crowdsecAppsecUnreachableBlock: true。这两项对安全至关重要。它们保证"故障关闭"架构。如果 CrowdSec 容器崩溃,或者 AppSec 引擎返回 500 错误,Traefik 会直接阻止进入的请求,而不是让它绕过 WAF 打到你的应用。
5. 全局生效
最后,为了确保每一个打到我的服务器的请求都被检查,我把 crowdsec 中间件直接挂到了 Traefik 的主入口点(web 和 websecure):
entryPoints:
web:
address: :80
http:
middlewares:
- crowdsec@file
websecure:
address: :443
http3:
advertisedPort: 443
http:
tls:
certResolver: letsencrypt
middlewares:
- crowdsec@file保持 WAF 更新(自动化)
装一个 WAF 只是完成了一半。漏洞每天都被发现,所以你的规则需要不断更新来捕捉新的 CVE。
要更新 CrowdSec,你需要两条命令:
cscli hub update(下载可用规则的最新索引)cscli hub upgrade(将更新应用到你安装的 collection)
我用 Dokploy Schedules 自动化了这个过程,配置成每天跑一次。每日更新是最佳节奏:它保护你不被新发布的 CVE 击中,又不会造成过度、过细的资源消耗。
我如何处理必需的重启
一个重要的注意事项:更新规则后,CrowdSec 引擎需要重启才能让新的 AppSec 规则生效。
在我的设置里,常规的 Dokploy 容器备份在更新计划之后不久运行。因为备份过程会停止容器来备份配置,然后再启动它,这自然就成了应用更新规则的重启步骤。
真实测试:拦截 CVE-2025-55182
为了验证这套设置,我决定拿一个真实的、很危险的漏洞来测试:CVE-2025-55182(一个 Next.js 原型污染漏洞,会导致远程代码执行)。
我用了一个开源利用工具(rix4uni/CVE-2025-55182),直接对我受保护 Homebox 上托管的一个域名发起攻击。
1. 利用尝试失败
运行命令行工具时,每一次 payload 尝试都没能在服务器上执行。工具绕不过边缘。
2. CrowdSec 日志
查看 CrowdSec 容器日志,我能清楚地看到发生了什么。CrowdSec 主动拒绝了利用尝试,实时识别出恶意 payload。
3. 日志分析
为了确认,我把原始日志跑了一遍 AI 摘要,它证实 CrowdSec 的 AppSec 规则在 Next.js RCE payload 触碰我的应用容器之前,就成功捕捉并丢弃了它。
结论
通过把 Traefik、Dokploy 和 CrowdSec AppSec 组合起来,我在云 Homebox 上部署了一个强大、免费的 Web Application Firewall。我的个人应用和快速部署,从打到边缘的那一刻起,就被屏蔽于主动、自动化的利用之外。
如果你自托管或维护个人服务器,我建议采用类似的边缘级 WAF 架构。
