mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): resolve secret action recovery manually
This commit is contained in:
@@ -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 CREATE;CREATE 的 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 退出 0;backend 1196 项为 1194 pass、2 条条件 skip、0 fail;cluster-admin 339 pass/3 skip、cluster-postgres 328 pass/2 skip,package 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 Pool,Edge 零变化。controller/process 定向 21/21,cluster-admin 全包 348 项为 345 pass/3 条件 skip/0 fail;完整 18-package 串行 build/test 退出 0;backend 1196 项为 1194 pass/2 条件 skip/0 fail;package 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、推进终态并写 receipt;Approval 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 退出 0,backend 1,194 pass/2 skip/0 fail,package/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-0397:Cluster Secret Action 显式人工恢复
|
||||
|
||||
- 状态:Accepted(实现、真实 PostgreSQL 单节点门、完整 workspace/后端门、边界审计与 physical HA 门均完成)
|
||||
- 日期:2026-08-14
|
||||
- 关联 RFC:QL-RFC-0001 D-306B2
|
||||
- 关联 ADR:ADR-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 且零 finding;workspace 保持 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。
|
||||
@@ -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 和正常 readiness;CloudNativePG 已创建
|
||||
1. PostgreSQL 已完成 65 条 control-core migration、capability v64 和正常 readiness;CloudNativePG 已创建
|
||||
`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 action,execution 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
|
||||
|
||||
复制
|
||||
|
||||
Reference in New Issue
Block a user