Files
qinglong/docs/adr/ADR-0494-postgresql-secret-binding-and-mounted-provider-live-rotation.md

7.0 KiB
Raw Permalink Blame History

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-adminql3-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+k3s13 个 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 作为可选、独立供应链继续设计。