7.0 KiB
ADR-0494:PostgreSQL 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 base:base 已支持创建
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 client;Edge/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,也不声明:
- Kubernetes control-plane HA;本 fixture 是单 server + 双 agent;
- PostgreSQL 物理 failover;它由独立 125-gate HA contract 证明;
- 直接 Vault/KMS/HSM adapter、CSI/Vault Agent 自身故障或 credential rotation;
- Cluster Legacy Env migration 的专用 SERIALIZABLE ledger、Task/Trigger mutation 和 promotion 后 receipt replay;
- 固定低性能路由设备上的真实空间、写放大、断电与恢复证据。
因此 ADR-0491 仍保持 Proposed。下一步优先完成固定 Edge 硬件空间门,随后实现并证明 Cluster migration ledger;直接外部 custody adapter 作为可选、独立供应链继续设计。