mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
161 lines
9.0 KiB
Markdown
161 lines
9.0 KiB
Markdown
# ADR-0238:Production Worker 证书续期单 Cadence 装配
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-08-01
|
||
- 关联 RFC:QL-RFC-0001 D-23、D-58、D-60、D-175、D-207、D-218、D-222
|
||
- 关联 ADR:ADR-0060、ADR-0061、ADR-0121、ADR-0234、ADR-0235、ADR-0236
|
||
|
||
## 背景
|
||
|
||
ADR-0061 已实现 Worker 本地 CSR、证书校验、原子 generation 安装和跨重启退避,
|
||
ADR-0234 又让 production credential provider 在每个 HTTPS request 前读取当前 active
|
||
identity。然而 production Worker 组合根没有调用续期协调器:部署即使提供外部 CA
|
||
adapter,也只能另建 timer、watcher 或旁路进程触发续期,无法继承既有 Session、drain、
|
||
错误隔离和 Edge/Node 资源预算。
|
||
|
||
把外部 CA 直接放入 `cluster-control` 会扩大常驻入口的签发权限;为续期再建 package、
|
||
sidecar 或 timer 又会把空闲成本推给低性能路由设备。需要在不改变 CA authority 边界的
|
||
前提下,把已有一次性续期操作接入唯一 production cadence,并证明正在运行的 Worker
|
||
可以在不替换 Session 的情况下完成信任收缩。
|
||
|
||
## 决策
|
||
|
||
### 1. 续期是可选 Profile capability,不是内建 CA
|
||
|
||
`ProductionWorkerHeadlessApplicationEnabledOptions` 接受可选的
|
||
`certificateRenewal.run()` capability。它只返回 ADR-0061 的低敏续期结果,不取得 CA
|
||
私钥、部署 Secret、listener 或数据库。
|
||
|
||
`runProductionWorkerProcess` 提供可选 `createCertificateRenewal(config, credentials)`
|
||
factory,让具体 Worker Profile 把 cert-manager、Vault、SPIFFE 或离线签发 adapter 与同一
|
||
证书目录组合。默认 CLI 未配置 adapter 时不构造 coordinator、不加载 enrollment PKI、
|
||
也不增加任何后台活动。
|
||
|
||
### 2. 复用唯一 cadence,固定先证书后网络业务
|
||
|
||
启动顺序固定为:
|
||
|
||
1. 取得 journal owner 并完成有界 startup reconciliation;
|
||
2. 执行一次证书维护;
|
||
3. 只有 identity 可用时才注册 Worker Session;
|
||
4. 启动既有唯一 `unref` cadence。
|
||
|
||
每个 cadence 的顺序固定为 certificate maintenance → Session heartbeat → execution
|
||
supervision/Pull。并发 tick 继续由 application 和 renewal coordinator 各自 coalesce;
|
||
不新增 timer、watcher、Agent、socket、Pool、进程或 sidecar。
|
||
|
||
`not_due`、`renewed` 和 `backing_off` 继续业务;`retry_scheduled` 在旧证书仍有效时继续
|
||
业务并发出稳定的 `certificate_renewal_failed` diagnostic。issuer 原始错误、CSR、PEM、
|
||
路径和 token 不进入 diagnostic 或 process event。
|
||
|
||
### 3. 身份不可用必须本地失败关闭
|
||
|
||
续期抛错或返回 `unavailable` 时,production Session lifecycle 同时:
|
||
|
||
- 把 Session coordinator 置为 blocked,使 `current()` 不再向 execution plane 暴露可用
|
||
Session,阻止新的 Pull;
|
||
- 把 capacity oracle 置零,阻止后续 heartbeat 宣告空闲容量;
|
||
- 发出 `certificate_unavailable` 低敏 diagnostic;
|
||
- 仍运行 execution supervision,使已有本地执行可以按 durable Lease 到期停止,而不是
|
||
因证书失败永久逃逸监督。
|
||
|
||
本地安装新证书本身不能解除 fence。只有同一 Session 的后续真实认证 heartbeat 成功,
|
||
Session coordinator 才清除 blocked,capacity oracle 才重新 active。这样不会把“CA 已
|
||
签发”冒充“网络路径、服务端信任和业务 credential 均已恢复”。
|
||
|
||
### 4. 换证复用一个 Agent,但不能复用旧 TLS socket
|
||
|
||
credential provider 继续每请求读取 active generation。共享 HTTPS Agent 的 pool key 同时
|
||
绑定 certificate、private key 与 trust anchors;active generation 改变后,下一请求使用
|
||
新的 pool key 和 TLS socket,不创建第二个 Agent。Ingress trust reload 继续轮换 TLS
|
||
generation、关闭 idle connection,并拒绝旧 generation socket。
|
||
|
||
### 5. CA overlap 与 trust contraction 由部署编排
|
||
|
||
常驻 QingLong 控制面仍不持有 CA 私钥,也不开放匿名 CSR endpoint。标准轮换顺序为:
|
||
|
||
1. ingress 先信任 CA-A + CA-B;
|
||
2. Worker 通过外部 issuer 取得 CA-B 证书并原子切换 active generation;
|
||
3. 下一次请求使用新 pool key;
|
||
4. ingress 显式 reload,只保留 CA-B;
|
||
5. 证明 CA-A 旧证书被拒、CA-B 新证书被接受;
|
||
6. 同一 Worker Session/generation 继续 heartbeat、Pull、Lease 与 completion。
|
||
|
||
Edge 可省略 adapter,或用低频外部 maintenance/离线 ceremony 注入 capability;Cluster
|
||
节点可由 cert-manager、Vault、SPIFFE 或 Secret rollout controller 注入。两者共享相同
|
||
一次性协调语义和失败关闭策略,不为资源档位复制 runtime。
|
||
|
||
## 不采用方案
|
||
|
||
### 在 Worker 内再建续期 timer 或文件 watcher
|
||
|
||
拒绝。它会与 production cadence 竞争、增加 Edge 空闲资源,并在 Kubernetes 多文件
|
||
Secret 非原子更新时间窗读取混合材料。
|
||
|
||
### 由 cluster-control 在线签发 Worker 证书
|
||
|
||
拒绝。它把外部 transport 漏洞升级为设备根身份签发权,也混淆 transport possession 与
|
||
`ql3w`、Session、Lease 三层 authority。
|
||
|
||
### 续期成功后立即本地解除 Session fence
|
||
|
||
拒绝。证书可能尚未被 ingress 信任,网络或 credential 仍可能失败;恢复必须由真实
|
||
heartbeat 证明。
|
||
|
||
### 换证时重启 Worker 或创建第二个 Agent
|
||
|
||
拒绝。重启会扩大 Run/drain 恢复窗口,双 Agent 会增加 socket/RSS 并使旧证书连接的关闭
|
||
时点不清晰。credential-bound pool key 已能在单 Agent 内分离代际。
|
||
|
||
### 为 certificate lifecycle 新建 workspace package
|
||
|
||
拒绝。续期协调器、process composition、Session lifecycle 和 transport 已由同一 Worker
|
||
部署单元拥有;再拆包没有独立发布、authority 或 Profile 替换价值,只会恢复一文件一包。
|
||
|
||
## 影响
|
||
|
||
- workspace 保持 20 个 package;
|
||
- 不新增生产依赖、migration、表、角色、端口、listener、Pool、连接、timer、watcher、
|
||
Agent、进程或 sidecar;
|
||
- disabled Worker 与未注入 CA adapter 的 Worker 保持零续期常驻成本;
|
||
- Edge/Node 继续只用同一个 runtime,通过现有 cadence/并发/页预算区分资源档;
|
||
- `ProductionWorkerSessionLifecycle` 增加可选 fail-closed capability;只有配置 certificate
|
||
renewal 时才强制要求它,既有无续期 headless embedding 保持兼容;
|
||
- 外部 CA adapter、CA 私钥、Secret rollout 与 ingress reload authority 仍在部署层,不
|
||
进入 Worker 或 Cluster 常驻业务入口。
|
||
|
||
## 验证
|
||
|
||
1. GitNexus 刷新后包含 35,661 nodes、81,415 edges、285 flows;upstream impact 中
|
||
production headless composition 2 个直接调用者、production product root 0 个上游、
|
||
process root 2 个上游、Worker Session coordinator 4 个上游、renewal coordinator 7 个
|
||
上游,均为 LOW,未命中已知 execution flow。`detect-changes` 对 tracked compare 为 LOW、
|
||
0 flow;Git 本身不会把尚未跟踪的 QL3 文件归入 compare,因此另以 strict 编译、入口
|
||
测试、依赖审计和 live contract 补足;
|
||
2. Worker strict TypeScript closure check 通过;全量 132/132,覆盖启动前 identity
|
||
unavailable 清理、单 cadence 顺序、
|
||
unavailable fail-close、同 Session authenticated heartbeat 恢复和唯一 TLS Agent;
|
||
3. `test:worker-postgres-live:ql3` 在 Linux Node `24.18.0` 与 PostgreSQL
|
||
`18.4 (Debian 18.4-1.pgdg13+1)` 上完成一次真实续期:issuer 调用 1 次,证书 SHA-256
|
||
改变,ingress TLS generation 变为 2,CA-A 旧证书被拒,CA-B 新证书被接受;
|
||
4. trust contraction 后 Worker 保持同一 Session ID、generation 1,瞬时旧 socket 失败
|
||
只产生有界 `session_tick_failed` diagnostic;后续真实认证请求恢复,无 Session replacement;
|
||
5. 最新一次同一合约随后完成 Offer→starting→running→67 次 Lease renew→31-byte Artifact→
|
||
completion,Run/Attempt succeeded、Lease completed,并在运行中完成 `ql3w` credential
|
||
A→B、A revoke、最终 drain/offline 与三个数据库角色权限断言;`gates.passed=true`;
|
||
6. 合约成功与失败路径均删除临时 PostgreSQL/Node 容器和私有证书目录,不保留 CA 私钥。
|
||
7. PostgreSQL 18.4 arm64 physical HA 独立复验 `remote_apply`、timeline 1→2、旧主先
|
||
fencing、未确认分区写在晋升后不存在、`pg_rewind` 只读同步重入和两个 fresh control
|
||
replicas,全部 gate `passed=true`;Worker/HA 临时容器与网络均已清理,既有
|
||
`ql3-cnpg-evidence-control-plane` 未修改。
|
||
|
||
## 仍未完成
|
||
|
||
- cert-manager、Vault PKI、SPIFFE/SPIRE 或离线 CA 的正式 adapter/部署模板;
|
||
- Kubernetes credential/identity generation 与单节点 PVC rollout 已由 ADR-0239 完成;仍缺
|
||
ingress reload controller、生产 RBAC、权限审计、CA overlap 分区与回滚;
|
||
- 证书到期、续期失败、blocked duration、TLS handshake 拒绝的 metric/alert;
|
||
- 真实 Kubernetes 多节点 CSI PVC detach/reattach、node loss 与 production 360 秒长 drain;
|
||
- TPM/PKCS#11/non-exportable private key provider;
|
||
- 固定 x64/arm64 路由设备的时钟跳变、断电、文件系统和资源门禁。
|