feat(ql3): resolve secret action recovery manually

This commit is contained in:
whyour
2026-08-14 12:11:50 +08:00
parent d8489d8a0b
commit 3ca9901055
29 changed files with 3143 additions and 50 deletions
+1
View File
@@ -33,6 +33,7 @@
- 2026-08-14 收口更新(取代上一句关于 controller 尚未启用的描述):生产 controller 已在既有 `cluster-admin/plugin-package/executor` 内接入 create/get-only Kubernetes Job adapter。它先按确定性名称 GET;仅当 durable execution 仍可创建且 approval 未过期时使用 Strict CREATECREATE 的 409 或响应丢失均回到 exact GET 收敛,`executing` 但 Job 缺失、过期 plan、终态 Job 与 renderer contract 漂移全部返回 `recoveryRequired`,不会盲目重建。Controller ServiceAccount 的 RBAC 仅允许 Job `create|get`action ServiceAccount 无 API token`admissionregistration.k8s.io/v1` ValidatingAdmissionPolicy 以固定参数 ConfigMap 和请求者身份锁死 digest 镜像、command、两个 ServiceAccount、source Secret、exact SHA-256 item/path、PostgreSQL SecretRef、安全上下文、资源额度和 volume/mount 形状,参数缺失与策略错误均失败关闭。基础 NetworkPolicy 继续只允许 DNS,集群 overlay 必须显式提供 API Server 的精确 CIDR/TCP 443 出口补丁。
- 真实 K3s `v1.34.3+k3s1` 已完成 admission 编译与现场门:合规 Job 的 server dry-run 通过,篡改镜像被策略拒绝,删除参数 ConfigMap 后创建被拒绝;controller SA 的 `list|watch|delete jobs`、Pod 创建和 Secret 读取均被拒绝,action SA 的 Job/Pod 创建与 Secret 读取也均被拒绝。实现仍保持 18 个 package,未新增 workspace package、Edge daemon/timer/watcher 或低配设备常驻负担;短生命周期 controller 与按需 action Job 仅属于 Cluster profile。完整 18-package clean build/test 退出 0backend 1196 项为 1194 pass、2 条条件 skip、0 failcluster-admin 339 pass/3 skip、cluster-postgres 328 pass/2 skippackage boundary、cluster dependency、edge import 和 cluster deployment 审计均无 finding。PostgreSQL `18.4` arm64 physical HA 125 项、timeline `1→2` 通过,报告 SHA-256 为 `a3d34e61ea2064e1cde574e533137186e09fdce9048455da64f582906037fa0d`,临时 Docker 资源已清理。ADR 仍为 Proposed:升级失败自动回滚、终态 Job 的 durable 恢复决议和固定物理低配设备证据尚未完成。
- 2026-08-14 终态恢复更新:Secret Action controller 不再把所有终态 Job 仅计为瞬时 `recoveryRequired`。Job 到达 Complete/Failed 后已停止执行,controller 会用 started execution 的原 lease fence 复验不可变业务结果:首次 binding 必须与 approval plan、`startedAtMs` 推导出的 binding 完全一致;transition 必须与 plan、authority evidence、commit time 推导出的 receipt 完全一致。精确 durable result 存在时补写 `succeeded`,即使 Job 已被 TTL 清理也能收敛;Failed 且无 durable mutation 时写 `failed`Complete 但无 receipt 时以 `indeterminate``blocked`。Job 在 start barrier 前终态或审批过期且尚未创建时,controller 复用既有 claim→release fence 写 `blocked`,不让坏 Job 永久占据 reconciler 页首。任何 stored result 漂移继续抛出 conflict`executing + Job 缺失 + receipt 缺失` 继续要求人工处理,绝不自动重建可能已产生副作用的动作。该切片不修改共享 execution schema、PostgreSQL migration 或角色权限,不新增 package、连接与常驻进程;Cluster controller 复用现有 package-executor PoolEdge 零变化。controller/process 定向 21/21cluster-admin 全包 348 项为 345 pass/3 条件 skip/0 fail;完整 18-package 串行 build/test 退出 0backend 1196 项为 1194 pass/2 条件 skip/0 failpackage boundary、cluster dependency、edge import、cluster deployment 均无 finding,部署/包边界聚焦测试 61/61。PostgreSQL `18.4` arm64 physical HA 125 项、timeline `1→2` 通过,报告 SHA-256 为 `bec512767fbbd7774baa9366698f60c25c8b017ed66f459b154d143fe86293bc`,临时 Docker 资源已清理。
- 2026-08-14 人工恢复更新(ADR-0397,已接受):上述唯一保留的 `executing + Job/receipt 均缺失` 不确定窗口现在具有显式 Cluster 产品处置路径。既有 Approval management mTLS/OIDC endpoint 新增 `approval.recover.inspect|resolve`,只接受五分钟内 `multi_factor|hardware` User、独立 `approval.recover` 权限、二次认证、exact execution version/digest 和外部 evidence SHA-256。只允许 Secret binding/transition action`confirm_failed` 写 failed`abandon_unknown` 写 blocked,永远禁止人工 succeeded、Job 重建或 execution 重置。PostgreSQL `pg-0065`/capability v64 新增不可变 resolution ledger 与单个 SECURITY DEFINER resolver,在同一事务内锁 Policy/execution fence、写 allowed audit、推进终态并写 receiptApproval manager 只有 dispatch/execution/resolution SELECT 与函数 EXECUTE,没有 execution UPDATE。通用 execution repository 与 Worker Credential 调用链保持不变。真实 PostgreSQL 18.4 已从空库完成 65 migration,证明原子提交、exact replay 不重复审计和 direct UPDATE `42501`;实现不新增 package、依赖、Pod、Pool、daemon、timer、watcher 或 Edge/Standalone 负担。18-package clean build/test 退出 0backend 1,194 pass/2 skip/0 failpackage/dependency/edge/deployment 审计零 finding;新 migration 与 repository 内聚到 `approved-action` 领域,migration ledger 直属源码保持审定上限 65。PostgreSQL 18.4 arm64 physical HA 125 项 gate、timeline `1→2` 通过,报告 SHA-256 为 `6d4921cba74475d15722a13c6a8034793c0ee25681bc7dcaf91024927c5752fe`,临时 Docker 资源已清理。
- D-302/ADR-0390(已接受)
Cluster operator context 增加无网络、无 mutation 的内建 `ql3-cluster-admin context validate` 预检。它先复用 owner-private context
reader,再让每个 entry 经过与真实请求相同的 production HTTPS/Kubernetes configuration preparation,验证精确 route、hostname、CA、
@@ -0,0 +1,45 @@
# ADR-0397Cluster Secret Action 显式人工恢复
- 状态:Accepted(实现、真实 PostgreSQL 单节点门、完整 workspace/后端门、边界审计与 physical HA 门均完成)
- 日期:2026-08-14
- 关联 RFCQL-RFC-0001 D-306B2
- 关联 ADRADR-0035、ADR-0359、ADR-0395、ADR-0396
## 问题
Secret Action controller 已能从终态 Kubernetes Job 与不可变 binding/transition receipt 自动恢复,也能在 Job Failed 且数据库无业务 mutation 时安全写入 failed。但 `approved_action_executions` 已进入 `executing`、Job 已不存在且 durable binding/receipt 也不存在时,系统无法证明外部效果从未发生。自动重建可能重复写 Secret;自动标记 succeeded 会伪造不存在的业务 receipt;长期保持 executing 又缺少可审计的产品处置入口。
通用 execution repository 同时服务 Worker Credential 等高影响调用链。为人工恢复放宽它的 UPDATE 权限或复用 package-executor,会扩大爆炸半径,也破坏 Approval manager 与执行器的职责分离。
## 决策
1. 只为 `plugin_package.secret_binding.bind``plugin_package.secret_binding.transition` 开放人工恢复。目标必须仍是 `executing`,原 lease 已过期,effective status 为 `recovery_required`pending、live lease、已有可自动验证 receipt、其他 action type 和任何终态均拒绝。
2. 人工决议只有 `confirm_failed``abandon_unknown`。前者表示外部证据已证明没有业务效果,execution 进入 `failed`;后者表示结果仍不可判定但操作者决定停止自动恢复,execution 进入 `blocked`。入口永远不能写 `succeeded`,因为人类陈述不能替代 immutable binding/transition receipt。
3. 调用者必须提交 exact execution version、execution digest、唯一 mutation ID、稳定低敏 reason code 和外部证据 SHA-256。服务端重新读取 dispatch/execution 并绑定 action、dispatch、Project 与原 execution digest;相同事实 exact replay 返回 `existing`,任一字段漂移冲突。
4. 复用 Cluster Approval management 的 mTLS/OIDC endpoint、短生命周期 client 与双重认证确认。只接受五分钟内的 `multi_factor|hardware` User,并独立请求 `approval.recover`;不复用 `approval.decide`、package.manage、ServiceAccount、Agent 或 System authority。
5. PostgreSQL `pg-0065`/control capability v64 新增不可变 resolution ledger 和单个 `SECURITY DEFINER resolve_approved_action_manual_recovery(jsonb,jsonb,jsonb)`。函数在同一事务内锁定当前 Policy fence 与 exact executing row,插入 allowed security audit、推进 execution 终态并插入 resolution。任一步失败全部回滚。
6. `ql3_approval_manager` 只新增 dispatch/execution/resolution 的 SELECT 与该函数的 EXECUTE;不取得 execution UPDATE,也不取得 resolution INSERT/UPDATE/DELETE。通用 `PostgresApprovedActionExecutionRepository` 保持不变,人工路径使用独立 repository,避免影响 Worker Credential execution flow。
7. 返回只包含低敏 action binding、execution 状态/版本/摘要/时间和 resolution receipt。lease owner/token、authentication ID、assurance、认证时间与 Policy 内部原因不进入响应。
8. 能力只属于 Cluster profile,复用现有 Approval manager Pool、Pod、listener 和 client。Edge/Standalone 不加载 PostgreSQL migration、repository 或新协议;不新增 workspace package、第三方依赖、daemon、timer、watcher、连接池或 Kubernetes workload。
## 接受条件
- Runtime Core 覆盖授权 inspect、两种终态、live lease/unsupported action/weak User/权限拒绝、围栏漂移和 exact replay。
- transport/client 覆盖 exact command/result、二次认证、终态 version fence,并证明 lease 与认证事实不泄露。
- PostgreSQL migration/readiness 明确证明 Approval manager 没有 execution UPDATE,只能执行受限函数。
- 真实 PostgreSQL 从空库执行全部 migration,证明 resolution、execution 与 audit 原子提交,响应重放不重复审计,直接 UPDATE 返回 `42501`
- 完整 workspace build/test、package/dependency/edge/deployment 审计与 PostgreSQL physical HA 门通过后,ADR 才能转为 Accepted。
## 影响与替代方案
- 每次人工处置增加一行有界 resolution 和一行既有 security audit;正常执行路径没有额外查询、常驻内存或 cadence。
- evidence digest 只证明操作者审查的外部材料,不把材料本身写入 QingLong;证据保存、访问控制与 retention 由部署者负责。
- 不提供“重置为 pending”“重新创建 Job”或“人工确认 succeeded”。这些方案都可能复制或伪造外部副作用,拒绝。
- 不给 Approval manager 通用 UPDATE。即使应用层能够校验,数据库 credential 仍是独立 authority boundary,必须由函数约束精确转换。
## 当前验证
- Runtime Core、Cluster PostgreSQL 与 Cluster Admin 包级测试已通过;新增领域、repository、transport/client 与 migration/readiness 测试均进入默认 test 集合。18 个 QL3 package clean build/test 退出 0;后端全门 1,194 pass、2 条件 skip、0 fail。
- PostgreSQL 18.4 单节点真实门从空库完成 65 个 migration,验证 `resolved → existing`、resolution/audit 各一条,以及 Approval manager 直接 UPDATE 被 `42501` 拒绝。该门同时发现并修复 `audit_event_id` 外键与 JSON-to-UUID 写入的真实类型错误。
- package boundary、cluster dependency、edge import 与 cluster deployment 四项审计全部 compatible 且零 findingworkspace 保持 18 package、无 single-source/shallow-source package。`pg-0065` 与 recovery repository 按 `approved-action` 领域内聚,既有有序 migration ledger 直属源码仍保持审定上限 65,没有以放宽阈值掩盖目录增长。
- PostgreSQL 18.4 arm64 physical HA 125 项 gate 全绿,timeline `1→2`,报告 SHA-256 为 `6d4921cba74475d15722a13c6a8034793c0ee25681bc7dcaf91024927c5752fe`;临时 Docker 资源已清理。因此本 ADR 转为 Accepted。
+2
View File
@@ -399,6 +399,8 @@
| [ADR-0393](./ADR-0393-generation-bound-plugin-package-secret-binding-ledger.md) | 按 Generation 固定的 Plugin Package Secret 绑定账本 | Accepted |
| [ADR-0394](./ADR-0394-generation-bound-plugin-package-secret-materialization.md) | 按 Generation 固定的 Plugin Package Secret Materialization | Accepted |
| [ADR-0395](./ADR-0395-owner-confirmed-plugin-package-secret-binding.md) | Owner 确认的 Plugin Package Secret 首次绑定 | Proposed |
| [ADR-0396](./ADR-0396-generation-transition-plugin-package-secret-binding.md) | 按 Package Generation 切换 Plugin Package Secret Binding | Proposed |
| [ADR-0397](./ADR-0397-explicit-cluster-secret-action-manual-recovery.md) | Cluster Secret Action 显式人工恢复 | Accepted(实现、单节点 PostgreSQL、完整 workspace/后端/边界与 physical HA 门完成) |
## 规则
@@ -6,7 +6,7 @@ PostgreSQL Pool。
## 部署前置
1. PostgreSQL 已完成 54 条 control-core migration、capability v53 和正常 readinessCloudNativePG 已创建
1. PostgreSQL 已完成 65 条 control-core migration、capability v64 和正常 readinessCloudNativePG 已创建
`ql3_approval_manager``ql3-postgres-approval-manager-auth`
2. 从
`deploy/kubernetes/ql3-cluster/operations/approval-management/config.example.yaml`
@@ -95,6 +95,48 @@ assertion。服务会在进入领域服务前和提交前重新认证;身份
`decision` 改为 `rejected` 可拒绝。`reasonCode` 只允许稳定、低敏的 snake_case 分类,不写自由文本、Secret 或个人信息。
成功返回 `decided` 与 version 2;同语义精确重放返回 `existing`
## Secret Action 人工恢复
只有 controller 已报告 `executing + Job missing + durable binding/transition receipt missing` 时才使用本入口。先检查,不要从日志手工拼接 execution digest
```json
{
"schemaVersion": 1,
"operation": "approval.recover.inspect",
"request": {
"projectId": "default",
"dispatchId": "dispatch-secret-action-1",
"requestId": "cluster-recovery-inspect-1",
"auditEventId": "30000000-0000-4000-8000-000000000001",
"failureAuditEventId": "30000000-0000-4000-8000-000000000002"
}
}
```
结果必须是受支持的 Secret binding/transition actionexecution effective status 必须是 `recovery_required`resolution 必须为空。完成外部取证后,用 inspect 返回的 exact version/digest 和证据文件的 SHA-256 创建新命令:
```json
{
"schemaVersion": 1,
"operation": "approval.recover.resolve",
"request": {
"projectId": "default",
"dispatchId": "dispatch-secret-action-1",
"requestId": "cluster-recovery-resolve-1",
"auditEventId": "40000000-0000-4000-8000-000000000001",
"failureAuditEventId": "40000000-0000-4000-8000-000000000002",
"expectedExecutionVersion": 3,
"expectedExecutionDigest": "<64-lowercase-hex-from-inspect>",
"mutationId": "manual-recovery-1",
"decision": "abandon_unknown",
"evidenceDigest": "<sha256-of-external-evidence>",
"reasonCode": "orphan_absence_unverifiable"
}
}
```
`confirm_failed` 只用于外部证据明确证明业务 mutation 未发生,终态为 failed;仍无法判定时使用 `abandon_unknown`,终态为 blocked。不存在人工 succeeded,也不得借此重建 Job 或重置 execution。响应丢失时只能用相同 User、mutation、decision、evidence、reason 和 execution fence 精确重放,返回 `existing` 表示原事务已提交。
## 使用一次性 Kubernetes Client Job
复制