Files
zhangxiang 4ab2f3cd8e chore(infra): 新增 Gitea+Jenkins CI/CD 部署与凭据整改
- infra/cicd:docker-compose(gitea/jenkins)、JCasC(凭据统一 ${ENV} 注入,无硬编码)、
  备份/恢复、健康检查、凭据轮换与 git 历史清除脚本
- docs/deployment/cicd:安装、高可用备份监控、凭据事故复盘
- .env.example 仅为占位模板;真实 .env 由 .gitignore 排除
2026-09-20 10:37:57 +08:00

4.1 KiB
Raw Permalink Blame History

03 · P0 凭据入库事故(待处置)

状态:待执行。 本文档是处置手册,不是已完成的整改记录。 2026-09-14 由会话内审计发现,张翔已确认「出方案、由他执行」。

1. 事故定性

真实生产凭据已提交进 git 并推送到公网可达的远程仓库。

  • 远程:https://git.f.novalon.cn/novalon/novalon-website.git(公网可达,实测 HTTP 200
  • 泄露物:Gitea 登录密码、Jenkins admin 密码、Gitea OAuth2 应用 secret、CI webhook token

任何能访问该仓库的人都可以直接登录生产 Gitea 与 Jenkins。 即使是私有仓库,也等效于把密码写进了版本历史 —— 无法撤销,只能轮换。

2. 泄露位置(实测)

文件 泄露内容 首次入库
scripts/setup-cicd/cicd.config GITEA_PASSWORDJENKINS_ADMIN_PASSWORDWEBHOOK_TOKEN b8ab1fd
scripts/setup-registry-auth.sh Gitea OAuth2 应用 secretgto_…,共 48 字符) 同期
novalon-cicd/docker-compose.yml PostgreSQL 密码(仅服务器,未入库)

确认命令(可复现):

git ls-files --error-unmatch scripts/setup-cicd/cicd.config   # → 已跟踪
git log --oneline -- scripts/setup-cicd/cicd.config            # → af57504, b8ab1fd
grep -rn "PASSWORD\s*=" scripts/setup-cicd/ scripts/setup-registry-auth.sh

3. 处置步骤(按序执行)

工具已备好:infra/cicd/security/

Step 1 — 轮换凭据(先做,别等清史)

ssh root@139.155.109.62 \
  'cd /home/novalon/docker-app/infra/cicd/security && ./rotate-credentials.sh --dry-run'
# 确认无误后去掉 --dry-run 实际执行

脚本会:

  1. 生成强随机新凭据(openssl rand
  2. 轮换 Gitea 用户密码(gitea admin user change-password
  3. 吊销泄露的 OAuth2 应用
  4. 签发新的只读read:repositoryGitea 令牌
  5. Jenkins admin 密码经 init.groovy.d 在下次重启生效(无需已有管理员凭据)
  6. 写回服务器 .env600 权限)

⚠️ 执行后旧凭据立即失效,请在无人使用 CI/CD 的时间窗操作。

Step 2 — 清除 git 历史

pip install git-filter-repo   # 或 brew install git-filter-repo
cd /path/to/novalon-website
./infra/cicd/security/purge-git-history.sh --dry-run   # 先扫描确认命中数
./infra/cicd/security/purge-git-history.sh             # 会先做 mirror 备份

脚本做了三重保护:

  • 改写前自动 git clone --mirror 备份
  • 二次确认(输入 PURGE
  • 清史后重新扫描,仍有残留则拒绝推送并中止

强制推送的连带影响(必须提前通知)

  • 所有协作者必须 rm -rf 本地仓库后重新 clone —— 旧仓库 push 会把泄露内容带回
  • 未合入的 PR / fork 可能失效

Step 3 — 删除远程残留对象

清史后远程的旧 blob 仍可通过 commit sha 直接访问:

Gitea → 仓库 → 设置 → 维护 → 运行垃圾回收

Step 4 — 防复发

  • 删除 scripts/setup-cicd/cicd.config,改用 .env + .env.example 模式
  • 装秘密扫描钩子(gitleaks / trufflehog),pre-commit + CI 双层
  • 把「凭据只进 .env600)或 Jenkins Credentials」写进 code review 检查项

4. 验收标准(执行完必须逐条核对)

  • git grep 全历史无明文密码(git log -p | grep -c 为 0
  • 旧密码登录 Gitea/Jenkins 均失败
  • 新令牌权限为 read:repositoryJenkins 能正常拉代码
  • 一次 push 能触发流水线(验证凭据替换没破坏链路)
  • Gitea OAuth2 应用列表中无泄露的 gto_… 应用
  • 服务器 .env 权限为 600,且已加入 .gitignore

5. 为什么「清史」不能替代「轮换」

历史可能已被 clone / fork / 缓存 / 搜索引擎抓取,清除是尽力而为。 密码一旦进过公网仓库,唯一可靠的补救是让它失效。 所以顺序是:先轮换(让泄露的密码立即变成废纸),再清史(降低持续暴露面)。