Anubis 1.28 常用命令与运维实战(核心篇)

anubis 1.28 命令与运维核心手册:server 启动参数分组速查、systemd 实例运维与资源实测、策略前台校验与错误字典、bbolt 持久化与锁、Prometheus 指标族、JSON 日志分析、升级回滚演练与五则排障实录,Ubuntu 24.04 原生部署实测。

Anubis安全爬虫WAFGolang2026/10/28

Anubis 1.28 常用命令与运维实战(核心篇)

Anubis(TecharoHQ 项目)是部署在反向代理位置、以 Proof-of-Work 质询拦截 AI 爬虫的防护网关。本篇是 Anubis 专栏的命令与运维核心手册:以原生 systemd 部署形态为基准,覆盖 anubis 二进制的全部启动参数、systemd 实例化运维、策略前台校验与错误字典、Prometheus 指标、JSON 结构化日志分析,以及一次完整的版本升级与回滚演练。前置阅读:《Anubis 介绍与选型指南》、《Anubis v1.28.2 配置参数详解》;容器化部署形态见 《Anubis 1.28 Docker Compose 部署实战》。

文中所有命令输入与输出均来自 2026-10-28 的实测,回显逐行照抄(涉及密钥、时间戳的字段按样例保留)。策略与配置语法细节不在本篇重复展开,请配合配置参数详解篇使用。

一、环境信息

项值
主机系统Ubuntu 24.04 LTS(原生 .deb 安装,systemd 托管)
Anubis 版本v1.28.2(官方 GitHub Releases 的 anubis_1.28.2_amd64.deb)
运行实例anubis@gitea:保护本机 http://localhost:3000 上的演示上游站点
监听服务端口 127.0.0.1:8239,指标端口 127.0.0.1:8240
配置/etc/anubis/gitea.env(环境变量)+ /etc/anubis/gitea.botPolicies.yaml(策略)
状态存储bbolt 单文件 /var/lib/anubis/gitea/cache.db(systemd 实际映射至 /var/lib/private/anubis/gitea/)
发布渠道https://github.com/TecharoHQ/anubis/releases

二、命令速查总览

2.1 顶层命令与附属工具

命令用途
anubis主进程(无子命令设计,server 即默认行为),读取环境变量或等效 flag 启动反向代理
anubis --version打印版本号
anubis --healthcheck对本实例指标端口执行健康探测,退出码 0/1
anubis --extract-resources <目录>导出内置静态资源与出厂规则片段
anubis-robots2policy附属工具:把 robots.txt 规则转换为 Anubis 策略片段

实测回显:

$ anubis --version
Anubis v1.28.2

$ mkdir -p /root/res && anubis --extract-resources /root/res
Extracted embedded static files to /root/res

$ find /root/res -maxdepth 1
/root/res
/root/res/data
/root/res/static

导出的 data/ 下就是内置的出厂策略(botPolicies.yaml)与规则片段库(bots/、meta/),static/ 是质询页面静态资源。运维价值:不确定某个 (data)/... 片段名是否存在时,先导出盘点再写策略,详见排障实录第 2 条。

2.2 anubis 启动参数分组速查

anubis --help 列出全部 flag(v1.28.2 共 50 余项)。与同名大写环境变量一一等价(如 -bind ↔ BIND、-policy-fname ↔ POLICY_FNAME),systemd 形态优先用环境变量文件,容器/一次性调试用 flag。核心项按用途分组如下(默认值照抄 --help):

监听与上游

flag(默认值)说明
-bind(":8923")服务监听地址
-bind-network("tcp")监听网络族:tcp 或 unix
-target("http://localhost:3923")反向代理目标;置空则只做 auth request 应答不代理
-target-host覆写转发到上游的 Host 头
-public-url对外可达 URL,用于 auth request 场景的重定向构造
-base-prefix("")应用挂载的子路径前缀,如 /myapp
-strip-base-prefix转发上游前剥离子路径前缀
-custom-real-ip-header用指定头替代 X-Real-IP 读取客户端 IP
-use-remote-address直接从网络连接读客户端 IP(裸金属/调试用)

策略与质询

flag(默认值)说明
-policy-fname("")策略文件路径;缺省用编译内置的出厂策略
-difficulty(4)质询难度(前导 0 的十六进制位数)
-difficulty-in-jwt在 JWT claims 中写入难度字段
-ed25519-private-key-hex / -ed25519-private-key-hex-fileJWT 签名私钥(hex / 文件二选一)
-hs512-secret改用 HS512 对称密钥签名 JWT
-jwt-restriction-header("X-Real-IP")JWT 仅在该头取值与签发时一致时有效

Cookie

flag(默认值)说明
-cookie-prefix("techaro.lol-anubis")Cookie 名前缀
-cookie-domain限定 Cookie 顶级域(多域名站需要)
-cookie-expiration-time(168h)通过质询后的通行有效期(7 天)
-cookie-secure(true)Secure 标记
-cookie-same-site("None")同站策略;cookie-secure=false 时自动降级为 Lax
-cookie-partitioned(true)启用 CHIPS 分区 Cookie
-cookie-http-onlyHttpOnly 标记
-dynamic-cookie-suffix(true)按站点设置动态生成 Cookie 名后缀

指标、日志与杂项

flag(默认值)说明
-metrics-bind(":9090")指标/健康检查端口
-metrics-bind-network("tcp")指标端口网络族
-slog-level("INFO")结构化日志级别
-serve-robots-txt直接对外提供"全站禁止抓取"的 robots.txt
-webmaster-email拒绝页展示的站长邮箱(误杀申诉入口)
-healthcheck执行健康探测后立即退出(见 §5.3)
-extract-resources("")导出内置资源(见 §2.1)
-debug-benchmark-js每个请求都返回质询页用于算力基准测试(见 §4.4)

另有 -thoth-* 三个 flag 已标记 deprecated(Thoth GeoIP 能力已并入策略文件的 geoip 配置),新配置不应再使用。

三、systemd 实例运维

3.1 模板单元解剖

官方 .deb 自带模板单元 anubis@.service,%i 为实例名:

[Unit]
Description="Anubis HTTP defense proxy (instance %i)"

[Service]
ExecStart=/usr/bin/anubis
Restart=always
RestartSec=30s
EnvironmentFile=/etc/anubis/%i.env
LimitNOFILE=infinity
DynamicUser=yes
CacheDirectory=anubis/%i
CacheDirectoryMode=0755
StateDirectory=anubis/%i
StateDirectoryMode=0755
RuntimeDirectory=anubis/%i
RuntimeDirectoryMode=0755
ReadWritePaths=/run

[Install]
WantedBy=multi-user.target

要点:

  • 实例模型:systemctl enable --now anubis@<名字> 即拉起一个独立实例,各自持有 /etc/anubis/<名字>.env 与专属状态目录。一台机器可并行防护多个上游。
  • DynamicUser=yes:进程以临时动态 UID 运行,StateDirectory 声明的目录实际落在 /var/lib/private/anubis/<实例>/(透过符号链接 /var/lib/anubis/<实例> 访问),直接 ls /var/lib/anubis 看不到文件不是故障。
  • Restart=always + RestartSec=30s:进程崩溃后 30 秒重启,策略解析失败这类确定性错误会表现为 30 秒一次的 crash loop,systemctl status 能直接看到节奏。

3.2 实例启停与巡检

$ systemctl enable --now anubis@gitea
$ systemctl is-active anubis@gitea
active
$ systemctl status anubis@gitea --no-pager -l | head -6
● anubis@gitea.service - "Anubis HTTP defense proxy (instance gitea)"
     Loaded: loaded (/usr/lib/systemd/system/anubis@.service; enabled; preset: enabled)
     Active: active (running) since Wed 2026-10-28 02:12:37 UTC; 11min ago
   Main PID: 1075 (anubis)
      Tasks: 11 (limit: 3172)
     Memory: 12.0M (peak: 19.3M)

日常巡检一条龙(活跃性、健康检查、指标端点、版本四连验,全部通过无输出差错):

$ systemctl is-active anubis@gitea && METRICS_BIND=:8240 anubis --healthcheck && curl -s -o /dev/null -w "healthz: HTTP %{http_code}\n" http://127.0.0.1:8240/healthz && anubis --version
active
2026/10/28 02:26:47 running healthcheck
healthz: HTTP 200
Anubis v1.28.2

启动成功的日志锚点(JSON 结构化):

$ journalctl -u anubis@gitea -o cat --no-pager | grep '"msg":"listening"'
{"time":"2026-10-28T02:11:52.812839753Z","level":"INFO","msg":"listening","subsystem":"anubis","bind":"127.0.0.1:8239","bind_network":"tcp","version":"v1.28.2","use_remote_address":false,"difficulty":4,"serve_robots_txt":false}

3.3 资源占用实测

空载常驻内存约 12 MiB,质询求解期间峰值约 19–20 MiB:

$ systemctl show anubis@gitea -p MainPID,MemoryCurrent
MainPID=3211
MemoryCurrent=20770816
$ ps -o rss=,comm= -p 3211
20772 anubis

(上述取值为质询活跃时刻的峰值;§3.2 的 status 输出则显示常态 Memory: 12.0M (peak: 19.3M)。)Go 单二进制的典型水平。生产容量规划可按单实例 20–40 MiB 估列,内存不是该组件的瓶颈项。

3.4 状态目录与持久化

bbolt 存储由策略文件 store 段指定路径(v1.28.2 的正确写法是 backend: bbolt + parameters.path,写错会被静默回落为进程内缓存,详见排障实录第 4 条)。落盘验证:

$ grep -A3 '^store:' /etc/anubis/gitea.botPolicies.yaml
store:
  backend: bbolt
  parameters:
    path: /var/lib/anubis/gitea/cache.db
$ ls -l /var/lib/private/anubis/gitea/cache.db
-rw-r--r-- 1 root root 16384 Oct 28 10:05 /var/lib/private/anubis/gitea/cache.db

bbolt 是单文件嵌入式存储,持久化内容为已通过质询的通行凭证(签名 JWT 状态),重启实例后已放行用户的 Cookie 依然有效(验证方法见 §7.2)。

四、策略校验与调试

4.1 标准校验法:前台一次性启动

v1.28.2 没有独立的 anubis validate 子命令,官方校验手段就是前台把进程拉起来看能否走到 listening。标准三步:

$ timeout 6 anubis -policy-fname /root/policy-validate.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000 -slog-level INFO | grep -oE '"msg":"[^"]{0,40}' | tail -4
"msg":"generating random key, Anubis will have 
"msg":"REDIRECT_DOMAINS is not set, Anubis will
"msg":"USE_SIMPLIFIED_EXPLANATION is deprecated
"msg":"listening

命令 6 秒后被 timeout 杀掉属预期(退出码 124),成功锚点就是末尾的 listening;消息链从 starting up Anubis、loading policy file 起。注意:待校验的策略若带 store 段且生产实例正占用同一个 bbolt 文件,校验进程会挂在文件锁上(见 §4.3),校验副本应临时去掉 store 段。

4.2 校验错误字典(实测回显)

四类典型错误,回显逐行照抄:

① YAML 语法错误

$ printf 'bots:\n  - name: broken\n    user_agent_regex: [Mozilla\n' > /root/policy-broken.yaml
$ timeout 6 anubis -policy-fname /root/policy-broken.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000
2026/10/28 02:21:47 can't parse policy file: can't parse policy file /root/policy-broken.yaml: can't parse policy config YAML /root/policy-broken.yaml: error converting YAML to JSON: yaml: line 3: did not find expected ',' or ']'

(解析失败时进程打印该行后立即退出,无 listening。)

② 动作字段拼错(action: DENYD,非法值)

$ timeout 6 anubis -policy-fname /root/p-action.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000
2026/10/28 02:21:36 can't parse policy file: can't parse policy file /root/p-action.yaml: config is not valid:
bot 0: config: bot entry for "badaction" is not valid:
config.Bot: unknown action: "DENYD"

③ 正则编译失败(user_agent_regex: "[Mozilla")

$ timeout 6 anubis -policy-fname /root/p-regex.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000
2026/10/28 02:21:36 can't parse policy file: can't parse policy file /root/p-regex.yaml: config is not valid:
bot 0: config: bot entry for "badregex" is not valid:
config.Bot: invalid user agent regex
error parsing regexp: missing closing ]: `[Mozilla`

④ import 规则混入其他键

$ # - name: ai-block-aggressive 与 import: 写进同一条规则
2026/10/28 01:41:39 failed to import policy file "/etc/anubis/gitea.botPolicies.yaml": error running policy file validation: yaml: unmarshal errors:
  line 12: import rules may not contain other keys

import 规则的正确写法是该条规则只有 import: 一个键:- import: (data)/meta/ai-block-aggressive.yaml。(data)/ 前缀指向内置片段库,可先 anubis --extract-resources 导出盘点(§2.1)。

症状一行修复
did not find expected node contentYAML 缩进/引号错误,用 python3 -c "import yaml,sys;yaml.safe_load(open(sys.argv[1]))" 文件名 定位行号
invalid actionaction 只能是 ALLOW / CHALLENGE / DENY / WEIGH
missing closing ]正则先过一遍本地编译再入库
import rules may not contain other keysimport 规则去掉 name: 等其他键,只留 import:

4.3 隐性故障:bbolt 独占锁

对生产实例正在使用的策略文件(含同一路径的 store 段)执行前台校验,进程会静默挂起直到 timeout 杀掉。实测:输出停在 loading policy file 后再无下文,且不打印任何错误:

$ timeout 10 anubis -policy-fname /etc/anubis/gitea.botPolicies.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000 > /tmp/hang.log 2>&1
$ cat /tmp/hang.log
{"time":"2026-10-28T02:24:00.659278291Z","level":"INFO","source":{"function":"main.run","file":"github.com/TecharoHQ/anubis/cmd/anubis/main.go","line":286},"msg":"starting up Anubis"}
{"time":"2026-10-28T02:24:00.659394965Z","level":"INFO","source":{"function":"main.run","file":"github.com/TecharoHQ/anubis/cmd/anubis/main.go","line":308},"msg":"loading policy file","fname":"/etc/anubis/gitea.botPolicies.yaml"}

根因:bbolt 打开文件时取独占锁,store 段路径与生产实例相同即挂死在锁等待上(卡点就在装载策略、打开存储这一步)。ps 视角下两个 anubis 进程都在等锁(Go 运行时等待的表象相同),判定依据是新进程迟迟打不出 listening:

$ ps -o pid,stat,wchan:26,comm -C anubis
    PID STAT WCHAN                      COMMAND
   1075 Ssl  futex_do_wait              anubis
   1375 Sl   futex_do_wait              anubis

规避:校验副本临时删掉 store 段(§4.1),或停生产实例后再校验。

4.4 -debug-benchmark-js:质询算力基准模式

$ anubis -debug-benchmark-js -policy-fname /root/policy-validate.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000 &
$ curl -s -A 'Mozilla/5.0' http://127.0.0.1:18923/ | grep -oE '<title>[^<]*</title>'
<title>Making sure you're not a bot</title>
$ curl -s -A 'Mozilla/5.0' http://127.0.0.1:18923/ | grep -oE 'Getting average benchmark time[^<]*'
Getting average benchmark time

该模式下每个请求都返回基准质询页,浏览器端反复求解并上报算力,用于评估不同 -difficulty 对客户端的耗时影响。调难度参数前建议先在自己目标用户的主流设备上跑一遍基准,再定策略档位。

4.5 anubis-robots2policy:robots.txt 转策略

$ printf 'User-agent: GPTBot\nDisallow: /\n\nUser-agent: Googlebot\nCrawl-delay: 2\nDisallow: /private/\n' > /root/robots.txt
$ anubis-robots2policy -input /root/robots.txt
policy:
  bots:
    - name: gptbot
      user_agent_regex: \bGPTBot\b
      action: DENY
    - name: googlebot
      user_agent_regex: \bGooglebot\b
      path_regex: ^/private/.*$
      action: DENY

输出可直接粘进 bots: 列表。注意它把 Disallow 全部译成 DENY:若只想给合规爬虫设质询门槛而非硬拒,把 action 手工改为 CHALLENGE 即可。

4.6 内置策略盘点

出厂默认策略(未配置 POLICY_FNAME 时生效的那份)同样可以导出审阅:

$ grep -n 'import:' /root/res/data/botPolicies.yaml | head -8
  - import: (data)/bots/_deny-pathological.yaml
  - import: (data)/bots/_denied-networks.yaml
  - import: (data)/crawlers/_all-bots.yaml
  - import: (data)/meta/ai-block-aggressive.yaml
  ...

两个实用结论:其一,内置片段名带 _ 前缀(如 _deny-pathological.yaml),凭直觉写 default-deny.yaml 会得到 unknown snippet 错误(排障实录第 2 条);其二,meta/ai-block-aggressive.yaml 展开后是一组 AI 爬虫 UA 聚合规则(如 bot/ai-catchall),是生产策略里"AI 爬虫一刀切"模块的实体。

五、Prometheus 指标与健康检查

5.1 指标端点与指标族

$ curl -s http://127.0.0.1:8240/metrics | grep '^# HELP' | grep anubis
# HELP anubis_policy_results The results of each policy rule
# HELP anubis_proxied_requests_total Number of requests proxied through Anubis to upstream targets

v1.28.2 的 Anubis 业务指标只有两个指标族(端点同时暴露 Prometheus 客户端库自带的标准指标族,此处只看 anubis_ 前缀):

  • anubis_policy_results:按命中规则与动作分桶的计数器。一个请求命中策略即计数一次,是"谁被拦、为什么"的核心观测面。
  • anubis_proxied_requests_total:按上游 host 分桶的成功代理计数器,即"放行后真实到达上游的流量"。

实测样本(与 §6 日志、§4 场景对应):

$ curl -s http://127.0.0.1:8240/metrics | grep '^anubis_'
anubis_policy_results{action="CHALLENGE",asn="",asn_description="",rule="bot/generic-browser"} 2
anubis_policy_results{action="DENY",asn="",asn_description="",rule="bot/ai-catchall"} 1
anubis_proxied_requests_total{asn="",asn_description="",host="127.0.0.1:8239"} 2

判读:两条 CHALLENGE 记录来自普通浏览器 UA(规则 bot/generic-browser,v1.28 的 policy_results 维度里 DENY 动作即硬拒绝,CHALLENGE 动作含"已通过"与"待通过"两种状态);DENY 记录来自 Amazonbot UA,命中 AI 爬虫聚合规则 bot/ai-catchall。注意:该版本没有独立的"质询通过数"业务指标,"通过量"用 anubis_proxied_requests_total 近似,"发放质询数"从日志统计(§6.3)。

5.2 --healthcheck:监控探针

anubis --healthcheck 探测的是本进程指标端口的 /healthz,适合直接做 systemd ExecStartPost 或外部拨测。三种取值的实测对照(这一坑必须记牢):

$ METRICS_BIND=":8240" anubis --healthcheck
2026/10/28 02:17:21 running healthcheck
exit=0

$ METRICS_BIND="127.0.0.1:8240" anubis --healthcheck
2026/10/28 02:17:21 running healthcheck
2026/10/28 02:17:21 failed to fetch metrics: Get "http://localhost127.0.0.1:8240/healthz": dial tcp: lookup localhost127.0.0.1 on 127.0.0.11:53: no such host
exit=1

$ METRICS_BIND="localhost:8240" anubis --healthcheck
2026/10/28 02:17:21 running healthcheck
2026/10/28 02:17:21 failed to fetch metrics: Get "http://localhostlocalhost:8240/healthz": dial tcp: lookup localhostlocalhost on 127.0.0.11:53: server misbehaving
exit=1

实现把探测 URL 拼成 http://localhost + METRICS_BIND,只有 :端口 形式取值能拼出合法地址。健康检查命令里写 METRICS_BIND=:8240(与实例 env 文件中的值保持同形式),错误端口则报 connection refused 并以退出码 1 结束。

六、日志分析与审计

6.1 JSON 日志字典

日志为 JSON 行,journalctl -o cat 去前缀后即原文。实测出现的消息类型:

msg含义关键字段
listening实例就绪bind、version、difficulty
loading policy file / configuration imported策略装载过程fname
explicit deny命中 DENY 规则拒绝请求check_result.name(规则名)、user_agent、x_real_ip
new challenge issued向请求方发放质询challenge(质询 ID)、weight
challenge accepted质询求解通过、发通行凭证challenge

两条完整实测样本(升级演练期间,字段照抄):

{"time":"2026-10-28T02:16:09.53144794Z","level":"INFO","source":{"function":"github.com/TecharoHQ/anubis/lib.(*Server).issueChallenge","file":"github.com/TecharoHQ/anubis/lib/anubis.go","line":226},"msg":"new challenge issued","subsystem":"anubis","host":"127.0.0.1:8239","method":"GET","path":"/","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0","accept_language":"","priority":"","x-forwarded-for":"","x-real-ip":"203.0.113.10","challenge":"01a1804b-203a-7e4c-a8d5-3ac271b73352","weight":0}
{"time":"2026-10-28T02:15:22.551808285Z","level":"INFO","source":{"function":"github.com/TecharoHQ/anubis/lib.(*Server).checkRules","file":"github.com/TecharoHQ/anubis/lib/anubis.go","line":464},"msg":"explicit deny","subsystem":"anubis","host":"127.0.0.1:8239","method":"GET","path":"/","user_agent":"Amazonbot/0.1 (+https://developer.amazon.com/support/amazonbot)","accept_language":"","priority":"","x-forwarded-for":"","x-real-ip":"203.0.113.77","check_result":{"name":"bot/ai-catchall","rule":"DENY","weight":0}}

6.2 实用分析管道

# 被拒 UA 排行(直接定位该拉黑/该降档的爬虫)
$ journalctl -u anubis@gitea -o cat --no-pager | grep 'explicit deny' \
    | grep -o '"user_agent":"[^"]*"' | sort | uniq -c | sort -rn | head -3
      1 "user_agent":"Amazonbot/1.0"
      1 "user_agent":"Amazonbot/0.1 (+https://developer.amazon.com/support/amazonbot)"

# 观测期内发放的质询总数
$ journalctl -u anubis@gitea -o cat --no-pager | grep -c 'new challenge issued'
6

# 按来源 IP 汇总被拒请求
$ journalctl -u anubis@gitea -o cat --no-pager | grep 'explicit deny' \
    | grep -o '"x-real-ip":"[^"]*"' | sort | uniq -c | sort -rn | head

采集侧注意:x-forwarded-for 在接入层不透传该头时为空串,客户端 IP 一律取 x_real_ip 字段做归并,避免把反代自身 IP 当成攻击源。

七、版本升级与回滚

7.1 升级演练:1.28.1 → 1.28.2

$ anubis --version
Anubis v1.28.1
$ dpkg -i /root/anubis_1.28.2_amd64.deb
(正在读取数据库 ... 系统当前共安装有 15244 个文件和目录。)
准备解包 .../anubis_1.28.2_amd64.deb  ...
正在解包 anubis (1.28.2) 并覆盖 (1.28.1) ...
正在设置 anubis (1.28.2) ...
$ anubis --version
Anubis v1.28.2

**关键发现:.deb 升级只换文件,不会重启正在运行的实例。**升级后立刻查日志,进程仍是旧版本:

$ journalctl -u anubis@gitea -o cat --no-pager --since '1 minute ago' | grep -o '"version":"[^"]*"' | tail -1
"version":"v1.28.1"

必须显式滚动重启:

$ systemctl restart anubis@gitea
$ sleep 2 && systemctl is-active anubis@gitea
active
$ journalctl -u anubis@gitea -o cat --no-pager --since '30 seconds ago' | grep -o '"version":"[^"]*"' | tail -1
"version":"v1.28.2"

多实例机器上升级后的标准动作是逐实例 systemctl restart anubis@<名字>,全部重启完成后再进入验证。

7.2 升级后状态连续性验证

三件事验证:配置未被动过、持久化文件延续、旧用户凭证依然有效(用已通过质询的浏览器 Cookie 重放请求):

$ md5sum /etc/anubis/gitea.env /etc/anubis/gitea.botPolicies.yaml
5df8b403be0b4b7a6f9d28b22a44d3f0  /etc/anubis/gitea.env
7699b9550746c6b665f92c78a8e0e475  /etc/anubis/gitea.botPolicies.yaml
$ ls -l /var/lib/private/anubis/gitea/cache.db
-rw-r--r-- 1 root root 16384 Oct 28 10:05 /var/lib/private/anubis/gitea/cache.db
$ # 用升级前留下的通行 Cookie 直接回访
$ python3 /root/pow_client.py replay
step4 revisit -> 200 | upstream: demo upstream for anubis ops

Cookie 跨升级版本有效:JWT 签名密钥来自 env 文件(未变)、通行状态在 bbolt(未变),两把钥匙都在,票据自然有效——用户无感知,这就是把签名私钥持久化进配置的价值。

7.3 回滚演练

apt 体系下用 dpkg -i 装低版本 .deb 即完成回滚(同款包管理路径,无需卸载):

$ dpkg -i /root/anubis_1.28.1_amd64.deb
正在解包 anubis (1.28.1) 并覆盖 (1.28.2) ...
$ anubis --version
Anubis v1.28.1
$ systemctl restart anubis@gitea && journalctl -u anubis@gitea -o cat --no-pager --since '30 seconds ago' | grep -o '"version":"[^"]*"' | tail -1
"version":"v1.28.1"
$ python3 /root/pow_client.py replay
step4 revisit -> 200 | upstream: demo upstream for anubis ops

回滚后旧 Cookie 依然有效(策略文件与签名密钥未动)。演练后按 §7.1 流程升回 1.28.2 收尾。运维纪律:升级前把上一个大版本的 .deb 留在本地包目录,回滚窗口就在手边。

容器化部署形态的升级与回滚操作见 《Anubis 1.28 Docker Compose 部署实战》的对应章节。

八、排障实录

以下五则均为本次实测中真实发生、逐行留痕的故障,按"现象 → 定位 → 处置"沉淀。

8.1 启动失败:import rules may not contain other keys

  • 现象:systemctl start anubis@gitea 后服务 crash loop,systemctl status 显示 activating (auto-restart),30 秒一轮。
  • 日志:journalctl -u anubis@gitea -o cat | tail -3 给出 error running policy file validation: yaml: unmarshal errors: line 12: import rules may not contain other keys。
  • 定位:策略里把 import 写成了 - name: ai-block-aggressive + import: ... 两键同条。import 是"整段引入"语义,不允许再挂其他键。
  • 处置:改成单键规则 - import: (data)/meta/ai-block-aggressive.yaml;systemctl restart 后 is-active 转绿。
  • 预防:import 一律独立成条;上线前按 §4.1 前台校验。

8.2 启动失败:unknown snippet

  • 现象:换用 - import: (data)/bots/default-deny.yaml 后依然 crash loop,报 unknown snippet: (data)/bots/default-deny.yaml。
  • 定位:凭记忆写片段名。内置片段库实际名为 _deny-pathological.yaml 等,带 _ 前缀,且并非所有直觉命名都存在。
  • 处置:anubis --extract-resources /root/res 导出,ls /root/res/data/bots/ /root/res/data/meta/ 盘点真实名字后引用 (data)/bots/_deny-pathological.yaml。
  • 预防:引用内置片段前必盘点(§2.1),不要手写印象中的片段名。

8.3 策略校验命令静默挂起

  • 现象:对生产策略文件做前台校验(§4.1),进程 30 秒无任何输出,被 timeout 杀掉,既没有报错也没有 listening。
  • 定位:ps -o pid,stat,comm -C anubis 见新进程 D+ 状态——卡在 bbolt 文件锁;生产实例持锁,校验副本 store 段指向同一 cache.db,无限等待。
  • 处置:校验副本临时去掉 store 段(或停生产实例再校验);校验秒过。
  • 预防:校验用"去 store 副本"是标准姿势;把这条写进团队 checklist,否则每次校验都要白等一个 timeout 周期。

8.4 配置了 bbolt 持久化,但 cache.db 不生成

  • 现象:策略写了 store: 段,服务正常跑,/var/lib/anubis/gitea/ 下始终没有 cache.db,重启后通行凭证全丢。
  • 定位:写法错误——store: 下直接嵌 bbolt: 键。v1.28 的正确 schema 是 backend: bbolt + parameters.path: ...。错误 schema 不报错,静默回落为进程内缓存,这正是"配置看似生效、实则没生效"的典型。
  • 处置:改为正确 schema(§3.4),重启后 cache.db 落盘(16384 字节)。
  • 预防:store 改动后除了 is-active,必须加"文件是否落盘"这步验证——服务活着不代表配置生效。

8.5 升级后版本没变

  • 现象:dpkg -i 装完 1.28.2,anubis --version 已是新版本,日志里的运行实例仍是 "version":"v1.28.1"。
  • 定位:.deb 升级只替换文件,不触碰运行中进程(单元未声明 ExecReload,也没有升级钩子)。
  • 处置:逐实例 systemctl restart anubis@<名字>,日志确认新版本号(§7.1)。
  • 预防:把"升级 ≠ 生效,重启 + 日志验版本"固化为升级 SOP 的固定两步。

九、例行维护清单

周期动作判据
每日systemctl is-active anubis@<实例> + METRICS_BIND=:8240 anubis --healthcheck双 active/退出码 0
每日curl -s http://127.0.0.1:8240/metrics | grep '^anubis_' 存档样本量连续、无异常清零
每周§6.2 拒绝 UA 排行复盘新增高频爬虫 UA → 评估策略条目
每月bbolt 文件与 /etc/anubis/ 配置备份备份前 systemctl stop 实例,拷贝后拉起
每季对比 GitHub Releases 最新 tag,走 §7 升级演练日志验证新版本号 + Cookie 连续性
事件驱动策略变更§4.1 前台校验 + 变更后指标/日志双确认

十、参考链接