# P1-04.9.2 实施与验收报告:双云 KMS、旧密文重包裹与故障演练
日期:2026-08-25
状态:功能、迁移、接口、管理端、自动化与超级管理员 Chrome UAT 已完成;真实企业云凭据验收属于发布门禁,不以模拟结果代替。
## 1. 阶段目标
1. 把 P1-04.9.1 的通用 KMS Gateway 契约落实为阿里云 KMS 与腾讯云 KMS 的真实官方 SDK 适配器。
2. 为旧本地 Fernet 密文建立租户级清单、异步批量重包裹、回读校验、暂停/恢复/取消/失败重试及字段级证据。
3. 实施托管服务不可用故障注入,证明超时、鉴权失败或提供方不可用时不会改写凭据。
4. 实施 SLO 外部升级通知的预检和显式入队演练,覆盖真实通知派发、重试与死信状态回写。
5. 保持合同 API 不持有云厂商 SDK 或 AccessKey,并避免阿里云 SDK 的加密库版本约束污染主服务。
## 2. 架构结果
### 2.1 隔离的双云 KMS 数据面
新增独立 `yuqei-kms-gateway` 服务。合同 API 仍只调用统一的 `/v1/encrypt` 与 `/v1/decrypt`,网关内部按部署选择:
- 阿里云:KMS 实例 SDK、ClientKey、ClientKey 密码、KMS 实例 CA、专属 endpoint、`AES_GCM`、固定 AAD。
- 腾讯云:KMS API 3.0 SDK、广州等国内 region、文件注入的临时或长期 API 凭据、固定 `EncryptionContext`。
- 公共门禁:文件注入服务 Bearer token、key allowlist、4 KiB 明文上限、请求超时、错误脱敏、无重定向、生产 TLS。
- SDK 依赖隔离:独立镜像或 `.venv`;合同 API 继续使用自己的 `cryptography>=44`,不加载云 SDK。
生产 Compose 提供阿里云、腾讯云两套互斥 overlay;网关容器只读、非 root、`no-new-privileges`,凭据与 TLS 材料均为只读挂载。
### 2.2 批量重包裹
重包裹对象覆盖企业日历源中的企业应用 secret、访问令牌、刷新令牌、增量游标,以及未过期 OAuth state 的 PKCE verifier。流程为:
1. 按当前租户扫描带密文的允许字段,只保存表名、记录 ID、字段名、来源后端和 SHA-256 指纹。
2. `inventory` 模式只生成清单与证据,不改写数据。
3. `execute` 模式创建异步任务,Worker 按批次取得租约并逐字段处理。
4. 每个字段先解开旧 envelope,再调用当前 KMS/Vault 加密,然后立即调用当前托管解密回读。
5. 回读明文使用常量时间比较;只有摘要一致且数据库旧指纹未变化时才写入新 envelope。
6. 并发更新、无法恢复、外部托管错误均转为 `skipped` 或 `failed`,不静默覆盖;任务支持暂停、恢复、取消和只重试失败项。
数据库和审计不保存重包裹明文。云厂商 CMK 的原生版本轮换由云 KMS 保持历史解密能力;本批处理专门用于把 P1-04.9.1 以前的本地 Fernet 密文迁移到当前外部托管后端,不支持在未同时配置来源适配器时跨两个外部托管后端直接搬迁。
### 2.3 故障演练
- 托管不可用:在事务内注入 `timeout`、`authorization`、`provider_unavailable` 三种隔离故障,比较演练前后租户密文字段数量与聚合摘要;只有“写入被阻止且摘要不变”才成功。
- SLO 外部升级:`dry_run` 检查通知通道已启用、握手通过且订阅 SLO 升级事件,不产生外部副作用。
- 真实入队:需要精确确认短语 `SLO_EXTERNAL_DRILL`,只向显式选择的通道创建一条带 `drill=true` 的通知投递。
- 投递 Worker 的 `delivered`、`retry_scheduled`、`dead_letter` 会回写演练账本,演练证据与普通 SLO 事件可区分。
## 3. 数据、接口与界面
迁移:`20260825_0063`。
新增表:
- `playbook_secret_rewrap_jobs`
- `playbook_secret_rewrap_items`
- `playbook_operational_drills`
新增接口:
- `GET|POST /api/v1/enterprise-playbook/governance/secret-custody/rewrap-jobs`
- `POST /api/v1/enterprise-playbook/governance/secret-custody/rewrap-jobs/{job_id}/actions`
- `GET|POST /api/v1/enterprise-playbook/governance/operational-drills`
超级管理员 Playbook 页面新增“旧密文盘点与批量重包裹”和“托管不可用与 SLO 外部升级故障演练”。危险操作有确认门禁,列表显示任务计数、字段状态、零写入证据与外部投递状态。
## 4. 发布门禁
正式切换前必须在隔离预发布环境完成:
1. 选择一家云厂商 overlay,配置企业自己的 endpoint、CMK、ClientKey/API 凭据、CA、服务 token 和网络白名单。
2. 网关 `/health` 返回 `configured=true`;合同 API 托管状态返回 `production_ready=true`。
3. 执行一次 `round_trip` 和一次持久化 `recovery_canary`,摘要均匹配。
4. 先创建 `inventory` 并导出任务计数;抽查目标记录当前指纹与业务可用性。
5. 使用小批次 `execute`,确认成功、失败、跳过之和等于总数;失败项不得通过手工数据库覆盖。
6. 运行三种托管不可用模拟,全部应为零写入。
7. 对测试通知通道先 `dry_run`,再显式 `enqueue`;确认外部接收与系统投递证据一致。
8. 完成全量重包裹并至少保留一个回滚窗口后,撤除旧 Fernet recovery key 文件。
## 5. 明确边界
- 当前开发机没有用户企业的阿里云 ClientKey、腾讯云 SecretId/SecretKey、CMK 或私网白名单,因此没有发起任何真实云端加解密请求。
- 自动化已在独立环境实际导入两家固定版本官方 SDK,并以假客户端验证精确请求模型、AAD/EncryptionContext、返回结构、key 校验与错误分类。
- “SDK 已接入”不等于“客户云账号已验收”;真实云往返是不可跳过的发布门禁。
- 本机没有 Docker,Compose overlay 已完成 YAML 与安全字段静态校验,镜像构建及容器健康检查需在部署节点执行。
## 6. 验收记录
- KMS Gateway:阿里云与腾讯云官方 SDK 均可在隔离 `.venv` 导入;4/4 网关契约与错误脱敏测试通过。
- 合同后端:358 collected,354 passed,4 skipped;无失败。新增测试覆盖远程重包裹回读、租户清单、乐观锁写入、管理员取消、三类零写入故障注入和 SLO 投递死信。
- 前端:typecheck、全部 Node 测试与生产 build 通过;生成路由包含三组 P1-04.9.2 代理接口。
- OpenAPI:120 paths / 180 schemas,YAML 与所有 schema 引用可解析。
- 数据库:PostgreSQL 已升级到 `20260825_0063 (head)`。
- 服务:Contract API `8100` health=ok,Web `3000` HTTP 200,AI API `8101` 保持运行;Review 与 Playbook Governance Worker 均运行且错误日志为空。Schema check 只有本地环境既有的 anti-abuse disabled warning,其余数据库、迁移和治理检查均为 ok。
- 超级管理员 Chrome UAT:账号 `uat.legal.20260823@example.test`、工作区“Playbook 隔离 UAT”;阶段标识为 P1-04.9.2,治理数据加载失败为 0,浏览器控制台错误为 0。
- UAT 盘点:创建 `playbook-secret-rewrap-3886d259f4fb`,状态 `inventory_complete`,当前隔离租户没有需要迁移的密文字段,业务数据未改写。
- UAT 托管故障:创建 timeout 模拟,状态 `succeeded`、`write_blocked=true`、`external_side_effect=false`,页面显示“零写入”。
- UAT SLO 门禁:对已握手但停用的测试邮件通道执行 dry-run,结果按预期失败且 `external_side_effect=false`;真实发送按钮保持禁用并展示启用/握手/订阅要求,未发送外部消息。
## 7. 官方实现依据
- 阿里云 KMS 实例 SDK for Python:
- 阿里云 KMS SDK 用户指南:
- 阿里云官方 Python SDK 示例:
- 腾讯云 KMS Python SDK:
- 腾讯云 KMS Encrypt:
- 腾讯云 KMS Decrypt:
详细发布、故障演练和回退步骤见 `project-docs/playbook-cloud-kms-rewrap-fault-drill-runbook-2026-08-25.md`。