Files
tamabox/docs/cli.md
T
tamakyi db97ac13ed docs: 蜜罐文档补家族伪装与资源开销
honeypot.md:
- 开篇从「一个假 WordPress 后台」改为「按扫描目标伪装成对应后台」,列出 9 个家族
  与各自触发路径;
- §1 补静态资源的处理(回占位内容、不落库、插件 .php 回 404 的理由);
- §3 封禁路径表补一行「Tomcat Basic 认证」的归属,说明 401 也走凭据通道;
- 补资源开销的实际数字,便于运维判断这功能值不值。

cli.md:paths 说明补家族汇总。
2026-10-23 12:38:42 +08:00

18 KiB
Raw Blame History

TamaBox 命令行操作手册(站方运维)

本项目没有管理后台:站方的全部运营动作只落在三类操作上 —— CLI 命令 / 邮件告警 / 改配置后重启。 这份文档只讲 CLI,覆盖处置、举报取证、批量隐藏与账号管理。


0. 前置

二进制从哪里来

# 仓库根目录构建(与 Dockerfile 是两条不同入口,命令列表必须双向同步)
go build -o tamabox .

# Docker 用的是这一条
go build -o NekoBox ./cmd/

两处都注册在同一份 app.Commands 列表中。新增命令时必须同时改 tamabox.go 和 cmd/tamabox.go, 否则会出现「本机二进制有、线上没有」(或反之),典型报错是 No help topic for '<cmd>'。

⚠️ 替换二进制不等于生效:go build -o tamabox . 只改磁盘文件,正在运行的进程仍是旧映像。 换完之后必须重启(systemd / supervisor / 手动拉起),否则改了代码也看不到变化。 另外 ./tamabox web 在前台立即返回,通常是端口被旧进程占着:

ps -eo pid,lstart,cmd | grep '[t]amabox'   # 看进程启动时间,与二进制 mtime 比对

确认手头二进制包含全部命令:

./tamabox --help | sed -n '/COMMANDS/,/GLOBAL/p'
# web / censor / uid / ban / restore / reports / restrict / unrestrict / hide-public / honeypot

想确认某个字符串有没有编进二进制,不要用 strings —— 它默认只输出可打印 ASCII, UTF-8 中文整段被丢弃,strings x | grep 中文 恒为 0。 用 grep -ac "中文字符串" x。

配置文件

CLI 与 web 读同一份配置,路径由环境变量指定:

NEKOBOX_CONFIG_PATH=/path/to/app.ini ./tamabox reports --domain xxx

必要条件(缺失会直接启动失败):

  • app.external_url
  • server.salt、server.xsrf_key
  • [database] 段的 type / host / port / user / password / name

⚠️ CLI 启动时会跑 AutoMigrate(与 web 相同)。这是特性也是风险:迁移有问题时连只读的 reports 都跑不起来。做重要操作前先 pg_dump。

操作前先备份

pg_dump -Fc <dbname> > before-$(date +%F-%H%M).dump

1. 命令总览

命令 作用 是否改数据 典型用途
reports 举报取证 / 待处理列表 / 全站统计 否 处置前看证据
restrict 施加处置(L1–L4) 是 停止分发、冻结发布、停用页面
unrestrict 撤销全部生效中处置并发解除通知 是 解封、误操作回滚
hide-public 批量把公开回答转为私密 是 清理存量挂人内容
ban 永久封禁(删用户+内容) 是(不可逆) 严重违规账号
restore 恢复已停用账号 是 用户申诉后恢复
censor 重新审核历史内容 是 审核规则变更后复检
uid 为用户生成/补齐 uid 是 数据修复
honeypot 扫描器诱捕取证(假登录页) 部分是(prune / purge-creds) 看谁在扫、拿到了什么字典
web 启动 Web 服务 — 生产入口

除 reports、honeypot hits/stats/paths/size 外,所有写操作都要求交互式输入大写 YES 确认; 脚本化时加 --yes。注意 honeypot prune / purge-creds 的确认词是 yes(小写)。


2. reports —— 处置前的取证

三种互斥用法(--stats / --pending 优先于 --email/--domain)。

2.1 单个箱主的证据包

./tamabox reports --domain 52mxtx1314
./tamabox reports --email [email protected] --days 90 --limit 50

输出包含:

  • 当前处置:级别、到期时间、原因
  • 近 N 天:被举报 X 条 / 公开回答 Y 条(累计,Z%);举报 W 次,其中待处理 V 次
  • 被举报条目:按举报数降序,带题号、内容前 48 字与状态(待处理 N / 已处理)
  • 历史处置:每条的时间、级别、原因、状态(生效中 / 已撤销 / 已到期)

口径(容易误读,务必看清):

  • 分子 = 窗口内被举报的条目数(不是举报次数)。同一条被 5 个人举报只算 1 条。
  • 分母 = 该箱主当前累计的公开回答数,不是窗口内产出。 所以 L2 收窄公开之后,分母会变小,比例可能超过 100% —— 这不是 bug,是分母口径问题。 判断「组织化挂人」时优先看绝对条数与指涉对象分布,比例只作参考。
  • 已删除的提问不计入(deleted_at IS NULL)。

2.2 全站待处理举报

./tamabox reports --pending --limit 50

按箱主分组列出所有 status = pending 的举报(不受 30 天窗口限制,方便清理积压)。

2.3 全站维度统计(识别「举报被武器化」)

./tamabox reports --stats --days 7

输出两个 Top 10:

  • 被举报最多的箱主(含 domain / 昵称 / 待处理举报数)
  • 举报来源 IP 最多(同一 IP 反复举报同一批内容)

怎么读:

现象 更可能是
多个 IP 打同一个箱主,且内容指向多个不同对象 组织化挂人
少量 IP 打多个不同箱主 举报被武器化(报复性举报)
同一 IP 单点爆发 脚本刷量(已由验证码 + 唯一索引限制)

⚠️ 统计只覆盖待处理举报,且最长回溯 30 天(超期未处理的举报会自动退出统计池)。

2.4 举报计数的前提:客户端 IP

举报去重键是 (question_id, reporter_ip),计数与统计全部依赖真实客户端 IP。

  • 站点在反向代理 / CDN 之后时,必须在 [security] 配置 trust_proxy = true, 并按需配置 trusted_proxies。
  • 若 trust_proxy = false 而站点实际在代理后,所有访客会被识别成同一个代理 IP → 每条内容最多只有 1 条举报,阈值告警永远不会触发。
  • 验证方法:从两个不同网络各举报一次,然后 reports --pending 看是否出现两个不同 IP。

3. restrict —— 分级处置

./tamabox restrict --domain <domain> --level L1 --days 7 --reason "组织挂人投稿"
./tamabox restrict --email <email> --level L3 --days 14 --reason "复发" --yes

参数:

参数 说明
--domain / --email 二选一;都填时按 email 查
--level L1 / L2 / L3 / L4,默认 L1,大小写敏感
--days 生效天数,从当前时间起算,必须 > 0
--reason 必填,写入审计记录(不会展示给用户,只用于内部留痕)
--yes 跳过 YES 确认
--force --days 超过 90 天时必须加,否则直接拒绝执行
--no-notify 不给箱主发处置通知邮件(默认发)

异常时长与二次确认

--days 不设硬上限(长期封禁是真实需求),但超过 90 天时:

  • 不加 --force → 直接报错退出(多打一个 0 不会被执行)
  • 交互模式下还要再输入一次级别名(如 L3)才继续
  • 脚本里 --force --yes 两个显式开关即构成两次确认

处置通知

处置生效后会自动给箱主发一封邮件(按箱主自己的界面语言,四语言齐全),内容只有:

  • 级别与级别名称(如 L3 冻结发布)
  • 到期时间
  • 社区规则链接
  • 申诉渠道(页脚「找站长」)

邮件里不包含 --reason 原文,也不引用被举报内容 —— 沿用公告模板的口径:只写级别、期限、依据。

需要「静默止血」(不惊动对方、避免其转移阵地)时加 --no-notify。

邮件发送失败不会回滚处置:只打一条 warn 并在终端提示,处置仍然生效。

级别语义

级别 名称 实际效果 生效点
L1 停止分发 单题/整箱分享卡不可生成;从首页、探索、标签检索、随机位摘除;热门标签不再出现 share.go、users.go
L2 收窄公开 L1 全部 + 新回答强制私密(单条 UPDATE 同时写回答与 is_private)+ 禁止「设为公开」+ 禁止回答追问 question.go、follow_up.go
L3 冻结发布 L2 全部 + 禁止回答(仍可收信) question.go
L4 页面停用 整个 /_/{domain} 全部 302 到 /restricted(单题、私聊、分享卡、JSON 列表接口一并挡住) page.go Pager + ListAPI

设计要点:

  • L3 不禁收信。伤害来自「发布」,剥夺收信能力没有治理收益,还会给对方「平台封口」的说法。
  • 到期自动解除:判定是 expires_at > now(),没有定时任务,也不需要手工解封。
  • 替换语义:同一用户再次 restrict 会先撤销旧的生效记录再写新的,任何时刻只有一条生效。 升级(L1→L3)与降级(L3→L1)都走这条路径。
  • 处置期间审核严格度锁定为最高档(strict),箱主无法自行改档;解禁后自动恢复他原本的设置 (users.moderation_strictness 全程未被改写)。

副作用(需要提前知道)

  • 处置期间箱主在自己后台能看到橙色条幅(级别 + 到期日 + 规则页链接)。
  • L1 就强制 strict:L1 只停分发、内容仍能发布,但审核已按最严处理, 正常的吐槽式提问也可能被拦。若线上发现大面积误拒,把 moderation.EffectiveStrictness 改成「L1 沿用原设置」即可(一行)。
  • 处置会给箱主发通知邮件(见上)。若用 --no-notify 跳过,箱主只剩登录后的橙色条幅。
  • 邮件发送依赖 [mail] 段的 SMTP 配置;未配置时处置仍会生效,但终端会提示发送失败。

4. unrestrict —— 撤销

./tamabox unrestrict --domain <domain>
./tamabox unrestrict --email <email> --no-notify
  • 把该用户所有生效中的处置写 revoked_at(保留记录,不删除,审计可追溯)。
  • 幂等:没有生效中处置时输出「没有生效中的处置,无需操作」。
  • 撤销后:页面/发布/分享卡立即恢复;hide-public 已经转为私密的内容不会自动变回公开, 需要箱主自己逐条「设为公开」。
  • 会给箱主发一封「限制已解除」通知(四语言,与处置通知同一模板,无申诉段), 正文里明确写了「私密内容不会自动恢复」。--no-notify 可跳过。

⚠️ 到期自动解除不会发信:那个判定是读表时比较 expires_at,没有定时任务。 如果希望「到期也通知」,只能靠 unrestrict 手工解封,或将来加个调度器。


5. hide-public —— 清理存量内容

# 最推荐:只隐藏「有未处理举报」的公开回答
./tamabox hide-public --domain <domain> --only-reported

# 全部公开回答(激进的兜底)
./tamabox hide-public --domain <domain>

# 只处理最近 30 天
./tamabox hide-public --domain <domain> --only-reported --since 30
参数 说明
--only-reported 只处理有 pending 举报的题目
--since N 只处理最近 N 天创建的题目;0 = 不限
--yes 跳过确认

行为:

  • 只动「公开且已回答」的题(answer != '' AND is_private = false),不碰私密与未回答。
  • 执行前打印总数 + 最新 10 条预览。
  • 可逆:只改可见性,箱主可逐条改回公开(但该箱主处于 L2 及以上时改不回去)。
  • 隐藏成功后自动把相关 pending 举报标记为 accepted,让它们退出告警统计池。
  • 单独用它不足以「停止分发」,建议配合 restrict --level L1。

6. 账号管理(ban / restore)

# 永久封禁:物理删除用户与其内容,邮箱释放。不可恢复。
./tamabox ban --email [email protected] --yes
./tamabox ban --domain xxx --yes

# 恢复「自助停用」的账号(软删除行),可同时重置密码
./tamabox restore --email [email protected]
./tamabox restore --email [email protected] --password 'NewPassw0rd' --yes

两者的区别(涉及唯一索引,容易踩):

  • 停用(可恢复):内容物理删除 + 用户行软删除。邮箱/域名继续被占用是有意为之, 恢复靠 restore。
  • 封禁(不可恢复):内容物理删除 + 用户行物理删除,邮箱立即可重新注册。
  • restore --password 必须在恢复之后执行,否则改不到(普通作用域查不到软删除行)。

7. honeypot —— 扫描器取证

设计取舍、凭据存储格式、部署要点见 honeypot.md。这里只列命令。

子命令 作用 改数据
hits 列出捕获到的凭据(时间倒序,密码默认脱敏) 否
stats 来源排行(IPv6 按 /64 聚合)+ 路径排行(库)+ 凭据排行(文件) 否
paths 列出当前生效的诱饵路径(按伪装家族汇总) 否
size 表行数、磁盘占用、凭据文件体积 否
prune 按保留期清元数据 + 凭据文件 是
purge-creds 删除旧版遗留的明文凭据列(不可逆) 是
./tamabox honeypot hits --since 7d --limit 50
./tamabox honeypot hits --ip 203.0.113.9 --show-password
./tamabox honeypot stats --days 7
./tamabox honeypot paths
./tamabox honeypot size
./tamabox honeypot prune --days 30
./tamabox honeypot purge-creds

要点:

  • 凭据不入库。用户名/密码只写在 data/honeypot-creds/creds-YYYY-MM-DD.jsonl (目录 0700、文件 0600),数据库里只有 IP / 路径 / UA / 是否封禁这些元数据。 所以 hits / paths 不连数据库,DB 挂了也能查。
  • 密码默认脱敏(保留首尾各 1 位)。终端输出常被贴进工单或截图,--show-password 只在确有取证需要时开。
  • hits / stats 里的 ip 是精确客户端地址;封禁用的是 shared.LimitKey 折叠后的键 (IPv6 为 /64 前缀)。两者在 IPv6 下不一致是正常的,别拿邮件里的精确地址去对封禁键。
  • purge-creds 用于清掉早期版本存在 honeypot_hits 上的明文列(AutoMigrate 不删既有列)。 执行前先 pg_dump;它只处理当前库,历史备份与从库里的明文需要自行清理。
  • 封禁语义:GET 探测走常规阈值(默认 10 次 / 60 秒),提交凭据才一次即封。 所以「扫了几十次怎么没封」不是 bug —— 见 honeypot.md 的「两条封禁路径」。

8. 典型处置流程

  1. 发现:收到告警邮件(同一箱主 24h ≥ 8 条 或 7d ≥ 20 条)→ 或箱主后台出现「被举报 N 次」。
  2. 取证:reports --domain <domain> 看条目、占比、历史处置。
  3. 冷静期:举报后 24 小时内不作判定(避免被集中举报推着走,也避免情绪化处置)。
  4. 止血:restrict --level L1 --days 7 --reason "..."。默认会通知箱主; 若判断通知会导致其转移阵地、需要先静默收口,加 --no-notify。
  5. 清存量:hide-public --domain <domain> --only-reported。
  6. 观察:7 天内复发 → 升 L2/L3;双方互挂 → 两边同级别处理,不选边。
  7. 收尾:到期自动解除(不发信);需要提前解封用 unrestrict(会给箱主发「已解除」通知)。
  8. 留痕:reports --domain <domain> 的历史处置段落就是处置记录(谁、何时、级别、原因)。

判定「挂人」的三要件(同时成立才处理):

  • 可识别性:能让圈内人定位到具体账号/作品/主页(昵称、代称、@、外链、作品标签)
  • 贬损或号召规避:带「避雷/挂/别信/辱骂」这类定性,而不是中性描述
  • 无公共利益:纯私人纠纷、粉籍立场之争

只成立一两条 → 不处理或仅提示。


9. 排错

现象 原因 处理
No help topic for 'xxx' 构建入口不同步(根 tamabox.go vs cmd/tamabox.go) 两处都注册后重新编译
find user: record not found --domain 填了昵称/登录名 domain 是 URL 里的那一段,/_/{domain}
处置后页面无变化 二进制是旧的 / 服务没重启 --help 看命令在不在;抓线上 HTML 比对标记
待处理列表是空的 举报已被箱主处理(自动 accepted)或超过 30 天 正常;必要时直接查库
告警邮件一直没收到 [security] security_alert_email 为空(静默跳过,无日志) 配好并重启
举报数始终是 1 trust_proxy 未按部署环境配置,全部识别为代理 IP 见 §2.4
箱主说「我改了但没生效」 处置期内严格度被锁定 / L2 禁止设为公开 看后台条幅;reports 查当前处置
--days 超过 90 天 被拒绝 防手误设计,需要 --force 确认时长后重试,或改用常规天数
处置成功但箱主说没收到邮件 SMTP 未配 / 发送失败 / 用了 --no-notify 看终端 warn;[mail] 配好后可 restrict 重发(同级别重设会替换记录)
蜜罐记录的 ip 是 127.0.0.1 trust_proxy 未配,源站把所有人看成同一个代理 IP 配 trust_proxy;此时一次封禁会封掉整站(见 honeypot.md 的「部署」一节)
整站都返回 403 蜜罐把反代 IP 封了(上一条的后果) 重启进程(封禁是内存态)或等 honeypot_ban_minutes 到期
换了二进制但行为没变 进程没重启,跑的还是旧映像 ps -eo lstart,cmd | grep tamabox 比对启动时间与文件 mtime
strings 二进制 | grep 中文 恒为 0 strings 只输出可打印 ASCII,中文 UTF-8 被丢弃 用 grep -ac "中文字符串" 二进制
honeypot 命令不存在 二进制旧 / 两个构建入口未同步 重新编译后重启进程

改完配置必须重启才生效(配置只在启动时读取一次)。


10. 明确不做的事

  • 不做自动处置:举报数达标只发邮件,不自动封禁。这个场景里举报本身就是攻击武器。
  • 不做申诉后台:申诉走邮件,处置结果由 CLI 写入。
  • 不用配置文件当黑名单:无法表达「N 天后自动解除」,最终会变成手工维护灾难。
  • 不要在 CLI 之外直接改库:user_restrictions 的写入必须带 revoked_at 语义, 手工 UPDATE 容易留下多条同时生效的记录(读取端会取最高级别,但不便于审计)。