5.8 KiB
5.8 KiB
生产密钥恢复与环境文件外置:设计
1. 设计目标
本任务解决两个相互关联但必须分阶段交付的问题:
- 主机或环境文件丢失时,人知道每个配置键如何恢复、代价是什么、由谁完成。
- 生产环境文件不再位于源码同步树内,任何
rsync --delete都无法物理触及它。
Phase 1 只建立恢复合同;Phase 2 才改变生产运行路径。数据库/附件备份继续保持现有边界,不吸收明文 Secret。
2. Phase 1:机器可校验的恢复分类
2.1 分类清单
新增一个位于 deploy/trialrun/ 的 JSON 合同文件,按类别保存 .env.example 键名数组:
{
"version": 1,
"categories": {
"template_rebuild": [],
"external_reissue": [],
"direct_rotate": [],
"downtime_reset": [],
"preserve_original": []
}
}
- 文件只允许大写环境键名和固定类别,不含值、默认值副本、长度、哈希、账号或外部平台地址。
validate-env-contract.js解析.env.example和分类文件,断言全集相等、无重复、无未知类别、无多余键。- 普通非敏感配置也必须进入
template_rebuild,避免清单只覆盖看起来像 Secret 的少数键。 FIELD_ENCRYPTION_KEY固定归入preserve_original;POSTGRES_PASSWORD/DATABASE_URL的关系在恢复卡中说明,数据库角色口令本身归入downtime_reset。
2.2 恢复卡
更新现有 deploy/trialrun/runbook.md 紧急恢复卡,不新建第二份 runbook:
- 每类给出丢失影响、责任角色、恢复来源、停止条件和恢复后验证。
- 数据库流程只描述受控“重置”:停止应用写入与 DB 容器、备份认证配置、用无公网暴露的隔离启动临时调整认证、修改角色口令、恢复原认证配置、重启并执行 migration status/ready/runtime smoke。任何一步失败都保持入口关闭。
preserve_original明确原值丢失会导致既有密文永久不可读;不得用新随机值冒充恢复成功。- 共享密码库由系统管理员和公司负责人双人可取。仓库只记录角色与季度取用演练结果,不记录产品地址、账号或恢复码。
2.3 Phase 1 验证
- 单元测试覆盖完整分类、重复键、未知键、漏键、非法类别和分类文件出现疑似值。
- 环境合同继续检查 config/example/Compose;分类变化纳入同一个失败出口。
- 敏感扫描证明新增文件只有键名与类别。
- Phase 1 不通过 SSH、数据库、Compose 或 systemd 访问生产。
3. Phase 2:生产环境文件外置
3.1 运行合同
REMOTE_ENV_FILE成为唯一运行事实源,必须是同步树外的安全绝对路径、普通文件、root 所有、0600。- Compose 的四个
env_file从字面量./.env改为必须显式提供的${REMOTE_ENV_FILE};同一变量也继续传给 Compose--env-file负责插值。 deploy.sh、deploy-existing.sh、远端 preflight、自动备份/WARCH3 安装器、备份 runner、巡检和收口脚本统一接收该变量,不保留${REMOTE_DIR}/deploy/trialrun/.env的生产默认值。- 本地开发可显式使用仓库外的测试 env;validator 的否定合同针对生产 Compose/部署默认与路径嵌套,不因为开发者工作区存在 gitignored 测试文件而误报。
3.2 负向防护
deploy.sh在 provenance、mkdir、rsync 之前拒绝REMOTE_ENV_FILE == REMOTE_DIR或任一方向嵌套。preflight-remote.sh验证 env owner/mode/非 symlink,并确认路径不在REMOTE_DIR下。validate-env-contract.js拒绝 Compose 再出现env_file: ./.env、部署脚本重新默认到同步树或 runbook 把生产事实源写回仓库目录。rsync的.envexclude 可以作为纵深防御保留,但不再承担安全正确性。
3.3 受控迁移状态机
- Prepare:只读冻结活动 Compose、env 路径、4 个 service、PGDATA/storage 和构建身份;检查共享密钥封存完成状态,不读取值。
- Copy:在主机上以 root 原子复制当前 env 到外置临时文件,核对键集合、owner/mode/非 symlink 后原子改名;不在日志输出 diff 或值。
- Switch:重装 4 个 units,使用新 env 完成 Compose config、新 init 配置预检并切换应用。
- Verify:migration status、
/ready、external/runtime smoke、普通登录、企微登录/入口、automatic backup daily/watchdog 和 timer 身份均通过。 - Retire:在同一批准窗口内按精确旧路径删除旧副本;随后验证旧路径不存在、容器与 units 只引用新路径。
任何 Verify 失败都在旧副本尚存时切回旧合同并重新安装 units;禁止在验证完成前删除旧副本。Retire 后若需回滚,只能使用外置事实源,不能把 Secret 搬回同步树。
4. 密钥版本化的延期边界
当前 v1:<iv>:<tag>:<ciphertext> 没有 key id,单一 FIELD_ENCRYPTION_KEY 无法支持安全轮换。本任务只保证原值可恢复,不修改加密服务、数据库密文或敏感字段 API。
后续独立安全任务至少需要:新格式版本与 key id、keyring 配置合同、当前写入键、旧键只读、惰性与批量重加密、覆盖银行账号/身份证号/通知渠道密文的枚举与计数、轮换回滚和旧键删除门禁。
5. 风险与回滚
- 最大风险是两份 env 共存期间扩大 Secret 暴露面,因此窗口必须短、两份均 root-only,完成后有否定删除验证。
- Compose 插值与 service
env_file是两个用途,必须同时验证,不能只改命令行--env-file。 - 数据库口令重置步骤属于事故恢复,不在常规部署或自动测试中执行;文档不得弱化停机和隔离要求。
- 具体密码管理器和成员授权是仓库外 EXTERNAL_GATED 动作,缺少双人取用确认时 Phase 2 不得开始。