Files
patbond-doc/docs/development/iterations/iteration-3.5/07-security-incident-20260911.md
T
lixi f9b1358b37
CI / docs-build (push) Successful in 1m59s
docs: 2026-09-11 安全事件复盘 + 新建服务器暴露面清单 + M3.5 报告 03/04/05 入档
安全事件(已闭环,服务恢复):
- 根因链:Gitea 3000 对公网开放 → 外部调用 /api/internal/manager/add-logger
  注入 gitconfig 的 uploadpack.packObjectsHook → 指向不存在的脚本 →
  upload-pack 发 NAK 后无法产出 pack → 全仓 HTTPS clone 失败(CI 全挂)
- 攻击未达成代码执行(hook 目标脚本不存在);三仓 ref 与本地逐一核对未被篡改;
  无系统层入侵(无陌生 key/crontab/挖矿进程/陌生登录)
- 新建常设「服务器暴露面清单」:补上服务器侧「决策变了环境没跟上」的核对机制
  (Nacos 在 ADR-002 移除后仍暴露公网近两个月)
- CI Runner 手册排障表增三条:CI 秒失败先在本机复现 checkout、跨仓比 CI
  须核对时间戳、clone 坏而 push 正常时查 packObjectsHook 注入

M3.5 交付报告:03 后端资料与头像(api 334→379)、04 契约冻结 v1.4.0
(31→32 路径、矩阵 173→181 格、11 格红转绿)、05 资料页与头像 UI(flutter 526→597)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 17:45:35 +08:00

6.1 KiB
Raw Blame History

安全事件复盘:Gitea gitconfig 注入(2026-09-11

级别:中(服务中断,无数据损失,攻击未达成代码执行) 发现方式CI 连续失败排查 处置结果:已闭环,服务恢复 记录人:主会话(AI 辅助排查,用户执行服务器侧操作)


1. 时间线(UTC+8

时间 事件
2026-07-13 Nacos 部署于服务器并对公网暴露 8848/9848(此前 ADR-002 已将 Nacos 从项目移除,属遗留服务)
2026-09-10 18:04 flutter CI 最后一次成功(task 68
2026-09-10 夜 ~ 09-11 晨 攻击发生:外部 IP 调用 Gitea 内部管理 API 写入 gitconfig
2026-09-11 09:27 doc 仓 CI 首次失败(1 秒,无 step)
2026-09-11 10:03 / 10:33 api 仓 CI 失败;期间另有 4 个提交的 job 完全未上报状态
2026-09-11 下午 定位、处置、验证恢复

攻击者来源 IP(gitconfig 注入串中残留):20.212.233.41Azure 段)、187.15.89.220(巴西)。

2. 根因链

  1. Gitea 的 3000 端口对公网开放HTTP_ADDR 未限制为 127.0.0.1,且轻量服务器防火墙放行 3000),使 nginx 之外存在一条直达通道。
  2. /api/internal/** 可被外部调用:攻击者 POST /api/internal/manager/add-logger,利用日志路径参数把内容写进 Gitea 的 gitconfig(注入串以 ;# 收尾,用于把 Gitea 追加的日志行注释掉)。
  3. 注入项为 uploadpack.packObjectsHook,指向 /var/lib/gitea/data/home/p0_*.sh 等文件。该 hook 是 git upload-pack 生成 pack 时调用的外部程序。
  4. 脚本并不存在 → hook 执行失败 → upload-pack 在发出 NAK 后无法产出任何 pack 数据 → 所有 HTTPS clone/fetch 失败,而 Gitea 仍记为 200 OK in 3ms

被污染的两份文件:/var/lib/gitea/data/home/.gitconfig/var/lib/gitea/home/.gitconfig

3. 影响评估

结论 依据
代码完整性 未被篡改 三仓 git ls-remote 的 dev/main/tag 与本地权威值逐一核对一致(api dev 3cd8005、main 8089c06、flutter dev 6945436、main 0e87413、doc main 5f02909,三个 v0.3.0 tag 全一致)
攻击是否达成代码执行 未达成 hook 指向的 p0_*.sh 经确认不存在;正因执行失败才暴露事件
系统是否被入侵 未被入侵 authorized_keys 仅 2 个已知 key(腾讯云 skey + 维护者本人);ubuntu 无 crontab、root crontab 仅腾讯云 agent;无挖矿进程(最高 CPU 1.0% 为云监控 agent);登录记录全部来自维护者常用 IP 段;Failed password 仅 1 次
开发是否受影响 未受影响 push 走 SSH 且 receive-pack(写入方向)不经 packObjectsHook,故两日内所有提交正常落地
CI 中断约 1 天 checkout 走 HTTPS,全仓失效
凭证泄露风险 ⚠️ 存在 攻击者能调用内部 APIINTERNAL_TOKEN 须视为可能泄露并轮换

4. 处置动作

# 动作 状态
1 Gitea HTTP_ADDR = 127.0.0.1(不再对公网监听),重启
2 防火墙删除 3000、8848、9848、2222 放行规则
3 停止 Nacos 并确认无监听(nacos.service 需一并 disable 防重启自启) 停止;⚠️ disable 待确认
4 清理两份 gitconfig 的 uploadpack.packObjectsHook(原文件已备份至 /root/gitconfig*.evidence.*.bak
5 重启 Gitea 并验证恢复:三仓 HTTPS 浅克隆全成功;upload-pack 响应从 12 字节恢复至 723 KB
6 轮换 INTERNAL_TOKEN(及可选 SECRET_KEY 待做
7 nginx 增加 location ^~ /api/internal/ { deny all; return 404; } 作为纵深防御 待做
8 清理 gitconfig 中残留的注入垃圾注释行 待做(不影响功能)

5. 诊断过程的教训(比结论更值得记)

判断走过两次弯路,都源于采信推断而非实测:

  1. 第一次误判「act_runner 故障」:CI 日志显示 job 1~2 秒失败、steps: null,据此推断 runner 挂了。实际 runner 容器 Up 6 天、job 镜像在本地、job 容器正常启动。
  2. 第二次误判「flutter CI 正常所以 runner 活着」:查到 flutter 最新提交 CI 为 success,却没核对时间戳——那是前一天 18:04 的旧记录。跨仓比较必须带时间戳。
  3. 第三次误判「nginx 代理层」:绕过 nginx 直连 3000 后同样失败,才排除。

真正的转折点是两个动作

  • 在本机复现同样的 git 操作git clone --depth 1 https://... 立刻复现 EOF)→ 一举把问题从「CI 领域」移到「Gitea 服务端领域」;
  • 手动执行 Gitea 内部实际调用的命令git upload-pack --stateless-rpc)→ 手动成功、进程内失败,把差异锁定到执行环境,于是查 gitconfig 时一眼看到注入。

沉淀为纪律(已写入 CI Runner 手册 排障表)CI 失败时,第一动作是在本机复现 CI 的第一个 step(通常是 checkout),而不是先去查 runner。

6. 更深层的问题:环境漂移无人核对

本次真正的隐患不是「Gitea 有个洞」,而是 Nacos 在项目已用 ADR-002 明确移除后,其进程与防火墙规则仍在服务器上暴露公网近两个月

代码侧我们有 ADR + 契约冻结 + 契约一致性测试来防止「决策变了、实现没跟上」,服务器侧却没有任何对应机制。为此新建 服务器暴露面清单:逐项登记开放端口与常驻服务的用途、归属决策、最后确认日期,作为常设文档定期核对。

7. 待办

  • 轮换 INTERNAL_TOKEN
  • nginx 拒绝 /api/internal/
  • systemctl disable nacos.service(当前仅停止,仍为 enabled,重启会自启)
  • 清理 gitconfig 残留注释垃圾行
  • Gitea 升级评估(当前 1.26.4/api/internal 可被外部调用是否属已知漏洞待核,无论如何应保持不对公网监听)