feat(ql3): prove mounted secret provider rotation

This commit is contained in:
whyour
2026-08-24 16:19:51 +08:00
parent fd10f72aba
commit b731b434f7
10 changed files with 956 additions and 48 deletions
+10 -1
View File
@@ -68,6 +68,14 @@
`manual_required/identity_custody_required`,本切片不声称旧 credential、session、token、Policy 或 Audit 已迁移。Secret/Config v52 fixture
现已用真实 `complete → replay → verify` 完成 v3 闭环,不再由测试伪造 receipt、直接推进 head 或手工回收 storage;真正未知表和 Legacy 身份回归
继续失败关闭。
第十切片以 D-399/ADR-0494 完成 Cluster `mounted-files` provider 的真实在线子门,而没有增加 Edge/Standalone 闭包。固定 K3s
`v1.34.3+k3s1` 三节点和 PostgreSQL 18.4 中,两个跨节点 management replica 通过正式 mTLS client 完成 plan、跨副本 replay、四眼审批与 inspect
direct exact-key executor 以无 ServiceAccount token、不可读取 Secret API、单一只读 `0440` projection 完成 binding exactly once,数据库 material
match 为 0。两个 production provider observer 以 required anti-affinity 位于不同节点,在无 Secret API 权限、无 token、deny-all network、非 root/
只读 root 下先观察 generation 1,再于 Secret resourceVersion-fenced replace 后无重启观察 generation 2;删除 Secret 后空 projection 以
`QL3_CLUSTER_MOUNTED_SECRET_UNAVAILABLE` 失败关闭。observer 将 content-free JSON 写入 termination log,审计不依赖偶发 EOF 的 kubelet logs。
`qinglong/plugin-package-secret-binding-kubernetes-live@v2` 私有 `0600` 报告 24/24 gates 为 true、离线 findings 为空,同时 verifier 继续接受 immutable
v1 shape。该门不声明 control-plane HA、PostgreSQL 物理 failover或直接 Vault/KMS/HSM;也没有实现 Cluster Legacy Env migration ledger。
全部 evidence 不含原 Env name/value、目标 ciphertext/key ID 或 row body。v52 Local SQLite 完整测试为 `247/247`publisher 定向回归 `6/6`fresh Edge
readiness 为 contract v52、104 migrations、89 required tables、SQLite 3.53.3、`DELETE` journal。Local Admin 为 `96/96`ADR-0493 后 Local Owner 有效结果为
`301 total / 294 pass / 7 conditional skip / 0 fail`;完整 backend 为
@@ -79,7 +87,8 @@
因而不重跑且不重新占有 PostgreSQL HA 证明;相邻已通过的 remote CI/HA 只作为基线。
D-385D-388 的 `config.sh`/Keyv/SSH data-directory lineage 与 SQLite `Envs` 保持分离;当前无稳定生产 schema 的历史 `Configs` 表继续 sealed+manual
不猜字段。后续切片必须完成真实 Edge 空间证据和 Cluster Secret provider live gate。D-397 apply
不猜字段。ADR-0494 已关闭基础 Cluster mounted-files provider live 子门;后续切片仍必须完成固定低性能设备的真实 Edge 空间/写放大/断电证据,
以及 Cluster Legacy Env migration 的专用 PostgreSQL SERIALIZABLE ledger、Task/Trigger revision mutation、直接外部 custody adapter 与 HA promotion 后 receipt replay。D-397 apply
只声明 sealed source retained 且 `physicalErasureGuaranteed=false`;明文销毁必须在 restart/
readiness、观察窗和 rollback retention 之后另行强认证。Cluster 必须使用 PostgreSQL SERIALIZABLE ledger、外部 KMS/Secret provider 与 HA evidence
不复用 Local SQLite/POSIX authority,也不得把明文写入 PostgreSQL、ConfigMap、Pod env 或 Job command。
@@ -144,6 +144,11 @@ Unicode、大小写和 Kubernetes key 兼容问题。canonical ref hash 更小
`24526848` bytes、零 OOM/oom_kill
6. workspace 仍为 20 个 QL3 package,没有新增 migration、表、生产依赖、timer、
watcher、listener、Pool、连接、sidecar 或 Kubernetes API 权限。
7. ADR-0494 已在真实三节点 K3s 中以两个跨节点 observer 证明 Kubernetes Secret
atomic-writer rotation:两副本无重启观察新 generation,均无 Secret API 权限、
ServiceAccount token 与网络权限,projection 为只读 `0440`;删除 projection 后以
`QL3_CLUSTER_MOUNTED_SECRET_UNAVAILABLE` 失败关闭。v2 私有报告 24/24 gates
为 true,且报告/termination message 不含值或 SecretRef。
## 尚未关闭
@@ -152,5 +157,6 @@ Unicode、大小写和 Kubernetes key 兼容问题。canonical ref hash 更小
2. 直接 Vault/KMS/HSM adapter 的可选供应链、认证、rate limit 和 outage contract
3. Worker materialization 后的 tmpfs/文件清除、Executor-specific injection 与真实
Pod/节点回收证据;
4. Kubernetes Secret/CSI/Vault Agent live rotation、并发 delivery、raw-wire
response loss 多副本故障证据
4. 直接 CSI/Vault Agent adapter 自身的 live rotation/credential outage、raw-wire
response loss、节点丢失与 Kubernetes control-plane 多副本故障证据;基础
Kubernetes Secret 双副本 atomic rotation 已由 ADR-0494 关闭。
@@ -1,6 +1,6 @@
# ADR-0491:有界 Secret/Config Reconciliation 与任务环境绑定
- 状态:ProposedD-397 已实现 Legacy Env inspection、私有有界 row plan、durable plan publication、独立 signed decision、逐项 Automation adoption provenance、Local SQLite 原子 application publisher、Owner prepared/apply/rollback 编排ADR-0492 completion v3;真实 Edge 空间证据Cluster Secret provider live gate 尚未完成)
- 状态:ProposedD-397 已实现 Legacy Env inspection、私有有界 row plan、durable plan publication、独立 signed decision、逐项 Automation adoption provenance、Local SQLite 原子 application publisher、Owner prepared/apply/rollback 编排ADR-0492 completion v3,并由 ADR-0494 完成 Cluster mounted-files provider live 子门;真实 Edge 空间证据Cluster migration ledger 与直接外部 custody gate 尚未完成)
- 日期:2026-08-23
- 决策:D-397
- 关联:ADR-0073、ADR-0074、ADR-0092、ADR-0094、ADR-0480、ADR-0482、ADR-0483、ADR-0484、ADR-0485、ADR-0486、ADR-0487、ADR-0488、ADR-0490
@@ -145,4 +145,6 @@ D-397 当前八切片已经实现:absent、unsupported、Edge over-budget、2.
本切片当前验证:Local SQLite `247/247`,其中 Secret/Config application publisher 定向回归 `6/6`fresh Edge readiness 为 contract v52、104 migrations、89 required tables、SQLite 3.53.3、`DELETE` journal。Local Admin 为 `96/96`ADR-0493 后 Local Owner 有效结果为 `301 total / 294 pass / 7 conditional skip / 0 fail`。完整 backend 为 `1567 total / 1565 pass / 2 conditional skip / 0 fail`。package boundary、精确 Cluster dependency/legacy boundary、122-module Edge import、service-manager bridge import、本地镜像与 `14/14` Local artifact audit 全部 compatibleLocal Admin 为 49 source / 48 nested / 1 root exportLocal Owner 为 188/187/1workspace 仍为 18 packages 且没有单文件或浅层 package。基础 Edge/Standalone 为 `2,635,529 / 2,635,607 bytes`、323 files、58 loaded modules,且没有 Cluster/PostgreSQL 闭包。本切片不改 PostgreSQL schema、连接、role、Pool、容器或 Kubernetes 拓扑,因此不重跑且不重新占有 PostgreSQL HA 证明。
转为 Accepted 前仍必须完成:真实 Edge 空间预算证据与 Cluster Secret provider live gateADR-0492 已完成本机 completion schema 演进和 completed-head 后 rollback material 回收,ADR-0493 又让没有 Legacy 身份输入的 fresh v52 目标身份经 signed `retain_target` 正确形成 no-effect,并精确消除六张已知目标表的 `unknown` 误判。Legacy `Auths/Users` 或真正未知表仍保持 manual;本切片的 Local Owner 编排或 PostgreSQL HA 证据不得冒充外部密钥托管
ADR-0494 已完成 Cluster `mounted-files` provider live 子门:真实三节点 K3s 中两个 management replica、direct exact-key executor 和两个跨节点 provider observer 完成 PostgreSQL durable approval/binding、Kubernetes atomic projection rotation、无 Secret API 权限/ServiceAccount token、只读 `0440`、内容脱敏及删除后 fail-closedv2 私有报告 24/24 gates 为 true,并保持 v1 verifier 兼容。该门不增加 Edge 闭包,也不等于直接 Vault/KMS/HSM custody
转为 Accepted 前仍必须完成:固定低性能 Edge 设备的真实空间/写放大/断电恢复证据,以及 Cluster Legacy Env migration 的专用 PostgreSQL SERIALIZABLE ledger、Task/Trigger revision mutation、外部 custody adapter 和 HA promotion 后 receipt replay。ADR-0492 已完成本机 completion schema 演进和 completed-head 后 rollback material 回收,ADR-0493 又让没有 Legacy 身份输入的 fresh v52 目标身份经 signed `retain_target` 正确形成 no-effect,并精确消除六张已知目标表的 `unknown` 误判。Legacy `Auths/Users` 或真正未知表仍保持 manual;本切片的 Local Owner 编排、ADR-0494 的 mounted-files gate 或 PostgreSQL HA 证据都不得冒充完整 Cluster migration 与外部密钥托管。
@@ -0,0 +1,130 @@
# ADR-0494PostgreSQL Secret Binding 与 Mounted Provider 在线轮换门
- 状态:Accepted
- 日期:2026-08-24
- 决策:D-399
- 关联:ADR-0129、ADR-0141、ADR-0233、ADR-0491、ADR-0492、ADR-0493
## 背景
ADR-0233 已提供 production `mounted-files` Secret provider,但原有证明主要来自
单元测试、静态部署审计和受限容器资源门。D-397 又要求 Cluster Secret migration
不能借用 Local SQLite/POSIX authority,必须证明 PostgreSQL durable approval、精确
Secret 投影和多节点运行时在同一真实 Kubernetes 门中仍然失败关闭。
旧的 Secret binding live contract 还落后于当前 executor basebase 已支持创建
action-scoped Job,而兼容门仍假设外层 executor 直接挂载值;同时成功 Pod 的 kubelet
logs 偶发 `EOF` 会让已经完成的证据在报告阶段丢失。门禁不能通过增加 Secret API
权限、ServiceAccount token、watcher、sidecar 或把值写入报告来规避这些问题。
## 决策
### 1. 复用现有生产边界,不增加新的运行时闭包
本门直接构建正式 `ql3-cluster-admin``ql3-cluster-control` 镜像,provider 使用
ADR-0233 的 `createClusterMountedSecretProvider`。不新增 workspace package、生产依赖、
PostgreSQL migration/table/role、daemon、timer、watcher、listener、sidecar 或 Secret
API clientEdge/Standalone import graph 不加载本门或 Cluster 依赖。
兼容 Secret binding executor 在 fixture 中显式进入 direct exact-key 模式:关闭
action controller、禁用 ServiceAccount token,只挂载当前批准计划所需的一个
projection key,并设置 `QL3_PLUGIN_PACKAGE_EXECUTOR_SECRET_ROOT`。这证明既有
PostgreSQL approval/binding 语义和 direct provider 消费,不改写 production base 的
action-scoped controller 决策;controller 的 digest-pinned Job/admission contract 继续由
其独立测试与部署审计证明。
### 2. 三节点拓扑与 durable approval 必须同时成立
live fixture 启动一台 K3s server 和两台 agent。两个 management Pod 使用
anti-affinity 分布到不同节点,通过正式 mTLS client 完成 plan、跨副本 replay、propose、
四眼 decide 和 inspect。PostgreSQL 18.4 保存 approval、execution 和 binding;数据库中
只能出现 SecretRef/plan/evidence digest,不能出现 Secret material。最小权限 manager
不能读取 binding 表,management Pod 不挂载业务 Secret。
外层 executor 只读挂载 exact-key `0440` projection,不能 get/list Kubernetes Secret
且没有 ServiceAccount token。它必须发布一次 binding、消费一次 approval 并成功完成;
任一持久化 fence 或 projection 漂移都失败关闭。
### 3. 两个 provider observer 必须无重启观察原子轮换
另建与 approval 中相同 SecretRef/projection key 对应的可变 Kubernetes Secret,并启动
两个 observer Job。required pod anti-affinity 要求它们位于不同节点;二者都运行正式
Cluster provider、非 root、只读 root filesystem、drop ALL capability,并满足:
- `automountServiceAccountToken=false`
- ServiceAccount 对 Secret `get/list/patch` 均为 `no`
- deny-all NetworkPolicy,无 ingress/egress
- projection read-only、`defaultMode=0440`,只包含一个精确 hash key
- 第一代 material 被观察后才以 resourceVersion-fenced `replace` 写入第二代;
- 两个 Pod 都在不重启的情况下观察到第二代;
- stdout、termination message 和最终报告不含两代值或 SecretRef。
随后删除 Secret,并以 `optional: true` 空 projection 启动一次性 observer;生产 provider
必须返回 `QL3_CLUSTER_MOUNTED_SECRET_UNAVAILABLE`。这里的 optional 只允许 Pod 启动,
不允许 material resolve 降级成功。
### 4. 证据不依赖 kubelet 日志可用性
observer 只输出 content-free JSON,并同时写入 `/dev/termination-log`。审计优先读取
PodStatus 中的终止消息,只有没有该消息时才回退 kubelet logs。因此已完成 Job 的证据
不会因节点日志通道 `EOF` 丢失,也不需要扩大 Kubernetes API/RBAC。
私有报告使用 `qinglong/plugin-package-secret-binding-kubernetes-live@v2`,以 owner-only
`0600` 原子发布。v2 在原 v1 字段上增加 control image、provider 拓扑、轮换、RBAC、
投影模式、脱敏和 missing fail-closed 证据;离线 verifier 继续接受 immutable v1 shape
但绝不允许 v1 报告伪装成 v2 provider 证明。
## 被拒绝的替代方案
### 给 control Pod 增加 Secret API 权限
拒绝。轮换由 Kubernetes atomic writer 投影完成;get/list/watch 会扩大 credential、网络、
缓存和审计面。
### 用 `disk-pressure` toleration 或降低 kubelet eviction 阈值通过门禁
拒绝。这会掩盖真实资源不足。本机运行先清理明确未使用且可重建的镜像/缓存,并在用户
授权后只回收未被容器引用的匿名卷,再从健康磁盘启动全新集群。
### 只验证单副本或重启后读取新值
拒绝。单副本不能证明 topology separation;重启后读取只能证明重新挂载,不能证明
atomic writer rotation 被现有 provider 请求观察。
### 把 Secret 值或 SecretRef 写入报告便于排障
拒绝。报告只保留 digest、计数、布尔值、错误码和节点名哈希。排障不得扩大 material
custody。
## 当前验证
2026-08-24 本机 Apple Silicon 完整 live gate 已通过:
- K3s `v1.34.3+k3s1`3 个 Ready 节点;
- PostgreSQL `server_version_num=180004`
- 2 个 management replica 位于不同节点;
- direct exact-key executor Job 成功,binding exactly once,数据库 material match 为 0
- 2 个 provider observer 位于不同节点,第一代与轮换代均为 `2/2`
- resourceVersion 前进,Pod 未重启,Secret API 三个权限均为 false
- read-only `0440`、无 token、deny-all network,输出脱敏;
- Secret 删除后以 `QL3_CLUSTER_MOUNTED_SECRET_UNAVAILABLE` 失败关闭;
- v2 报告 24/24 gates 为 true,离线审计 findings 为空,文件权限 `0600`
- v1 verifier 兼容与 v2 drift/sensitive rejection 定向测试通过。
CI 新增独立 `cluster-secret-binding-mounted-provider-kubernetes-live` Job,固定 K3s、
PostgreSQL 和 kubectl 版本,运行完整 live gate、离线复核并上传低敏报告。
## 边界与后续门禁
本 ADR 关闭 ADR-0491 的 Cluster mounted-files provider live 子门,但不把 D-397 整体
转为 Accepted,也不声明:
1. Kubernetes control-plane HA;本 fixture 是单 server + 双 agent
2. PostgreSQL 物理 failover;它由独立 125-gate HA contract 证明;
3. 直接 Vault/KMS/HSM adapter、CSI/Vault Agent 自身故障或 credential rotation
4. Cluster Legacy Env migration 的专用 SERIALIZABLE ledger、Task/Trigger mutation 和
promotion 后 receipt replay
5. 固定低性能路由设备上的真实空间、写放大、断电与恢复证据。
因此 ADR-0491 仍保持 Proposed。下一步优先完成固定 Edge 硬件空间门,随后实现并证明
Cluster migration ledger;直接外部 custody adapter 作为可选、独立供应链继续设计。
+1
View File
@@ -497,6 +497,7 @@
| [ADR-0491](./ADR-0491-bounded-secret-config-reconciliation-and-task-binding.md) | 有界 Secret/Config Reconciliation 与任务环境绑定 | ProposedD-397 local apply/rollback/completion completeEdge/Cluster gate pending |
| [ADR-0492](./ADR-0492-compatible-secret-config-completion-v3.md) | 兼容 Secret/Config 的 Reconciliation Completion v3 | Accepted |
| [ADR-0493](./ADR-0493-target-native-reconciliation-domain-preservation.md) | 目标原生 Reconciliation 域分类与身份保留 | Accepted |
| [ADR-0494](./ADR-0494-postgresql-secret-binding-and-mounted-provider-live-rotation.md) | PostgreSQL Secret Binding 与 Mounted Provider 在线轮换门 | Accepted |
## 规则