Anubis 部署笔记:Docker + Nginx unix socket,用 PoW 挑战把爬虫成本抬起来
项目信息
- 仓库:
- 文档站:
- 容器镜像:
ghcr.io/techarohq/anubis:latest
Anubis 是 TecharoHQ 出的 Web AI Firewall,靠工作量证明(Proof of Work)挑战拦截爬虫和 AI 抓取。已在 FFmpeg、GNOME、Arch Linux、Codeberg 等项目上跑着。
它是怎么工作的
Anubis 坐在反向代理(Nginx/Caddy)和目标服务之间,每个被保护的服务需要一个 Anubis 实例。客户端浏览器要跑一段 JS 做数学计算,算出正确结果才放行。对真人用户透明且快,对自动化爬虫则把成本显著抬高。
官方自己的说法是:多数情况下你可能不需要它,直接用 Cloudflare 保护源站就行;只有当不能或不想用 Cloudflare 时才有必要上。它是一个「核选项」——能挡住小型爬虫,也可能误伤 Internet Archive 这类「好爬虫」,后者可以通过 bot policy 显式放行。
安装
官方目前唯一支持的方式就是 Docker 镜像 ghcr.io/techarohq/anubis。容器以 UID 1000 / GID 1000 运行,挂载的卷必须属于该用户或对其可写。
策略文件 policy.yaml
新版用 YAML 写策略:
bots:
# 拦截激进爬虫
- name: pathological-bots
user_agent_regex: (?i:bot|crawler|scraper)
action: DENY
# 对 AI 爬虫发起挑战
- name: ai-bots
user_agent_regex: (?i:gptbot|claude|anthropic|openai)
action: CHALLENGE
status_codes:
CHALLENGE: 200
DENY: 200
store:
backend: memory # 快速开始用内存;生产用 bbolt
logging:
sink: stdio
level: INFO
生产策略可以直接 import 内置策略库:
bots:
- import: (data)/bots/_deny-pathological.yaml
- import: (data)/bots/aggressive-brazilian-scrapers.yaml
- import: (data)/meta/ai-block-aggressive.yaml
- import: (data)/crawlers/_allow-good.yaml
- import: (data)/common/keep-internet-working.yaml
store:
backend: bbolt
parameters:
path: /data/anubis.bdb
docker run
docker run -d \
--name anubis \
-p 8923:8923 \
-p 9090:9090 \
-e BIND=":8923" \
-e TARGET="http://localhost:3000" \
-e DIFFICULTY="4" \
-e POLICY_FNAME="/config/policy.yaml" \
-v $(pwd)/policy.yaml:/config/policy.yaml:ro \
ghcr.io/techarohq/anubis:latest
docker-compose
services:
anubis:
image: ghcr.io/techarohq/anubis:latest
ports:
- "8923:8923"
- "9090:9090"
environment:
BIND: ":8923"
TARGET: "http://app:3000"
DIFFICULTY: "4"
POLICY_FNAME: "/config/policy.yaml"
METRICS_BIND: ":9090"
SLOG_LEVEL: "INFO"
volumes:
- ./policy.yaml:/config/policy.yaml:ro
restart: unless-stopped
app:
image: your-app:latest
expose:
- "3000"
常用环境变量
以下取自 cmd/anubis/main.go:
| 变量 | 默认值 | 说明 |
|---|---|---|
BIND | :8923 | 监听地址 |
TARGET | http://localhost:3923 | 后端服务 URL |
DIFFICULTY | 4(1–10) | 挑战难度,即结果前缀 0 的个数 |
POLICY_FNAME | 内置 | 策略 YAML 路径 |
METRICS_BIND | :9090 | Prometheus 指标与健康检查 |
COOKIE_EXPIRATION_TIME | 24h(另有文档写 168h) | 通行证有效期 |
SLOG_LEVEL | INFO | 日志级别 |
USE_REMOTE_ADDRESS | false | 是否从网络请求读客户端 IP |
XFF_STRIP_PRIVATE | true | 从 X-Forwarded-For 去掉私网 IP |
ED25519_PRIVATE_KEY_HEX_FILE | — | 持久化存储或多实例部署需要的签名密钥 |
BIND_NETWORK | — | 可选 tcp/unix;用 unix socket 时 BIND 形如 /run/anubis/anubis.sock |
健康检查
Docker 侧:
healthcheck:
test: ["CMD", "anubis", "--healthcheck"]
interval: 5s
timeout: 30s
retries: 5
start_period: 500ms
Nginx + Anubis 的 unix socket 模式
这么做的好处是 Anubis 不必额外暴露端口,TLS 和路由都交给 Nginx。
anubis 容器环境:
COOKIE_DOMAIN: "example.com"
TARGET: "unix:/tmp/nginx.sock"
BIND: "/run/anubis/anubis.sock"
BIND_NETWORK: "unix"
SOCKET_MODE: "0770"
nginx 与 anubis 共享 volume,nginx 的 server 段 include 一个 anubis.conf:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://anubis:8923;
}
443 的 server 里 include anubis.conf,其余指令移到监听 unix:/tmp/nginx.sock 的 server 段,并加上 set_real_ip_from unix;。如果漏掉这一句,依赖真实 IP 的应用(比如 MediaWiki)会拿不到客户端 IP——$REMOTE_ADDR 会始终是 unix:。
指标与 DNSBL
Prometheus 指标:访问 http://:/metrics。
Anubis 默认对访客 IP 做 DNSBL 查询,而很多 IPv6 并不在数据库里。可以在策略文件里写 dnsbl: false 关掉。
踩坑要点
- 卷权限:容器以 UID/GID 1000 跑,挂进去的目录和文件属主/权限要对,否则读写策略文件和 bbolt 数据库会失败。
- 签名密钥:用持久化存储(bbolt)或多实例部署,必须挂
ED25519_PRIVATE_KEY_HEX_FILE。不挂的话,重启或多副本之间签发的通行证互不认,用户会反复过挑战。 - 难度取舍:
DIFFICULTY越大,客户端算得越久。防护更强,但用户等待也更久。被爬虫打满时往上调,收到用户抱怨慢时往下调。 - 真实 IP:走 unix socket 时记得
set_real_ip_from unix;,否则后端的$REMOTE_ADDR只有一个unix:。
一个绕不过去的自指
文档站 anubis.techaro.lol 本身就跑着 Anubis。直接抓取会返回 Making sure you're not a bot! 挑战页——想读文档,得用浏览器先过一次 PoW。