modem7/crowdsec-troubleshooter
GitHub: modem7/crowdsec-troubleshooter
一个无特权的一次性 Docker 诊断工具,专门用于排查 CrowdSec + Traefik 部署中封禁机制是否正常工作。
Stars: 5 | Forks: 2
# crowdsec-troubleshooter
[](https://github.com/modem7/crowdsec-troubleshooter/actions/workflows/ci.yml)
[](https://woodpecker.modem7.com/repos/3)
[](https://github.com/modem7/crowdsec-troubleshooter/commits/master)
[](https://hub.docker.com/r/modem7/crowdsec-troubleshooter)
[](https://hub.docker.com/r/modem7/crowdsec-troubleshooter)
一个轻量级、无特权、运行一次的 Docker 工具,用于诊断 CrowdSec + Traefik
设置 — 提供健康检查、IP 封禁检查器,以及(可选的)实时的
“封禁是否真正生效”测试。没有守护进程,没有 `docker.sock`,没有主机
网络连接,除了出站 HTTP
调用之外不需要任何 capabilities。基本健康检查之外的所有功能都是可选的,并且该工具可以使用的每个凭据都附带了各自的添加/移除说明,
直接打印在工具自身的输出中。
## 为什么会有这个工具
CrowdSec 的决策仅存在于其 Local API (LAPI) 中 — Traefik 的 bouncer
和 Cloudflare Worker bouncer 都只是执行点,它们轮询
LAPI 并执行其指令。因此,“为什么这个没有被封禁”从
根本上来说是一个 LAPI 问题,而不是一个 Traefik/CrowdSec/Cloudflare 三方的
诊断。这个工具正是围绕这一点构建的。
## 快速开始
```
docker run --rm -e CROWDSEC_LAPI_URL=http://crowdsec:8080 modem7/crowdsec-troubleshooter
```
仅仅这样做就会运行 0 级检查 — LAPI 活跃度、日志解析活动、
bouncer 类型指纹识别,以及对 LAPI URL 本身的启发式检测 — 而且具有
**完全不需要任何凭据**。此工具中的所有其他内容都是在此基础上的附加功能;
在未配置任何内容的情况下运行一次,它会准确地告诉你
每个额外检查需要什么,为什么需要,以及如何添加(或移除)它。
```
# 检查特定 IP 的封禁状态和原因
docker run --rm -e CROWDSEC_LAPI_URL=... -e CROWDSEC_LAPI_KEY=... \
modem7/crowdsec-troubleshooter check-ip 198.51.100.23
# 验证 blocking 是否真正实现了 end-to-end 工作(添加/移除一个真实的测试 ban)
# CROWDSEC_MACHINE_CREDENTIALS_FILE 是从 container 内部读取的,因此
# host 文件必须 bind-mounted 进去 — 参见 setup/register_machine.sh 了解
# 如何 provision 凭据本身(它是一个 login+password,而不是 token)。
docker run --rm -e CROWDSEC_LAPI_URL=... \
-e CROWDSEC_MACHINE_CREDENTIALS_FILE=/creds/machine.json \
-v /path/on/host/machine.json:/creds/machine.json:ro \
modem7/crowdsec-troubleshooter live-test --target-url https://your-service.example.com
```
### 或者直接跳过 `-e` 标志:`wizard.sh`
每次运行都手动输入 `CROWDSEC_LAPI_URL`/`CROWDSEC_LAPI_KEY`/`CROWDSEC_MACHINE_CREDENTIALS_FILE`
很快就会让人厌烦,特别是最后一个还需要 `-v` 挂载。
`wizard.sh`(仅限 Linux — 在主机上运行,而不是在
容器内运行)会根据特定操作的实际需求提示输入,并在 `./.crowdsec-troubleshooter.env` 中记住你输入的内容供下次使用,然后
为你启动真正的 `docker run`。
无需克隆 — 直接从仓库运行:
```
curl -fsSL https://raw.githubusercontent.com/modem7/crowdsec-troubleshooter/master/wizard.sh | bash -s -- wellness
```
`-s --` 非常重要:如果没有它,`bash` 会尝试打开一个字面名为
`wellness` 的文件,而不是将其作为参数传递给通过管道传输的脚本。每个
提示都会明确地从 `/dev/tty` 读取,而不是从脚本自身的
stdin(上面的管道正是使用它来传递脚本本身)— 这正是使得提示能够通过管道正常工作,而不是每个回答都
默默返回为空的原因。
或者克隆仓库并在本地运行,两种方式的命令是一样的:
```
./wizard.sh # interactive menu
./wizard.sh --compose ./docker-compose.yml wellness # auto-suggest from your compose file
./wizard.sh check-ip 198.51.100.23
./wizard.sh live-test https://your-service.example.com
```
它解析的每个值都遵循相同的优先级:你已经导出的 shell 环境变量优先,
然后是之前保存的值,接着是
从 `docker-compose.yml` 派生的建议(尽力而为 — 它会寻找
`crowdsecurity/crowdsec`、`*traefik-crowdsec-bouncer` 和
`crowdsecurity/cloudflare-worker-bouncer` 镜像,并读取它们的 `ports:`/
`networks:`/`environment:` 块),最后为空。任何内容都不会被默默
覆盖 — 每个提示都会显示其默认值,按 Enter 键即保留。保存的
文件已被 `chmod 600`;像对待任何其他凭据文件一样对待它,如果这个目录是一个 git 仓库,请将其添加到 `.gitignore` 中。
## 级别模型
| 级别 | 解锁条件 | 增加的功能 |
|---|---|---|
| 0 | 除了 `CROWDSEC_LAPI_URL` 之外无需任何内容 | LAPI 活跃度、日志解析活动、bouncer 类型指纹、LAPI-URL 范围启发式检测、可选的身份验证绕过对比 |
| 1 | 专用的**只读 bouncer 密钥** | `check-ip` — 封禁检查器 |
| 2 | **机器凭据** (read-write) | 实时封禁/解封测试,AppSec 探测 |
| 3 | **只读主机文件挂载** | `DOCKER-USER` 链证据、重复获取检测、syslog 提示、compose 文件强化审计 |
在任何级别下,都不需要 `docker.sock`、`--privileged`、`NET_ADMIN`/`NET_RAW` 或主机
网络连接。请参阅 [`DESIGN.md`](./DESIGN.md) 了解每个
边界背后的原因 — 它们中的大多数之所以存在,是因为该工具的早期
草稿假设了比它实际需要的更多的访问权限。
有关此工具读取的每个 `-e` 变量、`-v` 挂载和 CLI 标志的完整列表 — 每个解锁的内容、需要与之搭配的要求,以及每个级别的精确
`docker run` 调用 — 请参阅
[`FLAGS.md`](./FLAGS.md)。
## 为什么没有守护进程模式
考虑过并刻意拒绝了。支持它的唯一真正理由是
持续监控,而这只不过是“按计划运行级别 0” — 一个 cron
条目或一个调度器调用 `docker run --rm`,通过管道导入 Healthchecks.io
或被 Uptime Kuma 轮询,就能获得相同的结果,且没有任何缺点。
守护进程意味着机器凭据(创建/删除真实的封禁)会无限期地存在于
正在运行的进程中 — 唯一的一项架构选择将完全破坏该工具的整个凭据卫生设计,而功能上却毫无益处。
## 镜像
每次成功构建后都会发布到两个镜像仓库 — 选择适合你设置的那一个:
```
docker pull modem7/crowdsec-troubleshooter:latest # Docker Hub — via Woodpecker
docker pull ghcr.io/modem7/crowdsec-troubleshooter:latest # GHCR — via GitHub Actions
```
每次推送到 `master` 且触及镜像实际
源码(`Dockerfile`、`lib/`、`checks/`、`setup/`、`versioncheck/`、
`capability_check.sh`、`troubleshoot.sh`)时,Docker Hub 都会构建 — 参见 `.woodpecker.yml`。GHCR
仅在该提交的 `CI` GitHub Actions 工作流实际成功
后才会发布 — 参见 `.github/workflows/publish-ghcr.yml` — 因此如果每个 lint/test/build 检查没有全部通过,镜像就
无法进入 GHCR。两者都是多架构(`linux/amd64` + `linux/arm64`)。
## 测试 / CI
每次推送和 PR 都会运行:`bash -n` 语法检查、ShellCheck、特定于此仓库的一组
约定检查(每个脚本必须是可执行的并且
以 `set -uo pipefail` 开头;每个 `setup/add_*`/`register_*` 都有一个
对应的 `remove_*`/`unregister_*`),针对 Dockerfile 的 Hadolint,
示例 compose 文件的 YAML/schema 验证,Gitleaks 密钥
扫描,一套 `bats` 回归/行为测试套件,最后是 Docker 构建
以及针对模拟 LAPI 的冒烟测试。
在开发期间,通过实际针对模拟服务器运行代码而不是仅仅阅读代码,发现了两个真实的 bug — 参见 `tests/lib_common.bats`
和 `tests/check_metrics_liveness.bats`,了解现在涵盖这两个 bug 的回归测试,以及 `DESIGN.md` 了解它们的具体情况。第三个更具结构性
的 bug(脆弱的基于 `$0` 的路径解析,当脚本被执行而不是被引用时,这仅仅是出于巧合才能工作)是在测试套件本身在测试合理地直接 `source` 脚本时失败而
发现的 — 通过切换到 `${BASH_SOURCE[0]}` 在整个项目中进行了修复。
在本地运行与 CI 相同的运行方式:
```
shellcheck --severity=warning $(find . -name "*.sh")
bats tests/*.bats
docker build -t crowdsec-troubleshooter:local .
```
## 状态
早期框架。级别 0 和封禁检查器(级别 1)已实现,
应该可以针对真实的 CrowdSec 实例直接使用 — 尚未针对
真实的实例进行过测试,因此请将其视为需要验证的起点,而不是盲目信任。
级别 2(实时封禁测试)在带有相同警告的情况下已实现,另外还有一个硬性
安全要求:必须验证移除测试封禁的清理陷阱在真实的故障条件下是否确实会触发,然后才能将其信任地用于生产实例。
已知尚未解决的悬而未决的问题:
- `cscli capi status` 的等效数据(`check_capi.sh`)是否完全可以通过 LAPI 的 HTTP API 访问,
或者像 bouncer 列表最终证明的那样,它是仅限数据库访问的。在脚本本身中已作为占位符标记。
- 确切的 AppSec 探测路径格式(`test_appsec_probe.sh`)— CrowdSec 的
文档显示了一个示例 token,但没有确认它是固定的
还是每次安装时生成的。
- 版本/CVE 检查(`versioncheck/cve.sh`)目前还没有可靠的纯网络
机制 — 目前接受手动提示,而不是自行
检测任何内容。
## 许可证
MIT
标签:CrowdSec, Docker, PB级数据处理, Traefik, 安全运维, 安全防御评估, 应用安全, 开发运维, 诊断工具, 请求拦截