5.9 KiB
ADR-0191:私有 Cluster Plugin Package 管理客户端
- 状态:Accepted
- 日期:2026-07-29
- 关联:RFC D-140、D-144、D-145、D-175、D-178、D-180、D-181; ADR-0144、ADR-0145、ADR-0185、ADR-0188、ADR-0190
背景
Cluster 已有独立、默认关闭的 Plugin Package management HTTPS process,服务端具备
TLS 1.3、强 User assertion、Project Policy、durable quota、Approval 与低敏响应。
ql3-plugin-package-manage 只是该服务端进程入口;现有调用端仅是 release live-evidence
脚本,不是可交付的日常 operator CLI。
因此 D-180 虽已完成数据库与 transport 能力,部署者仍需要自行拼 HTTP header、JSON 和
TLS 参数。直接建议 curl 会让 assertion 进入 shell history/process argv,并容易关闭
证书验证或把完整错误响应写入日志。
决策
1. 新增 client binary,不复用 server binary
在既有 @qinglong/cluster-admin 内新增:
./plugin-package-management-clientlibrary subpath;ql3-plugin-package-clientexecutable。
不新增 workspace package、第三方生产依赖、数据库角色、Service、listener、timer、
watcher 或 controller。server 的 ql3-plugin-package-manage 名称和启动语义保持不变,
避免一个 binary 同时承担入站 authority 与出站 client。
2. 私有文件是唯一 credential/command 输入
CLI 只接受三个非秘密路径参数:
--config=/absolute/client.json--command=/absolute/command.json--assertion=/absolute/assertion.jwt
禁止从 argv、环境变量、stdin 或 command JSON 接收 assertion、Bearer token、私钥、 数据库 DSN 或 CA 内容。
config、command、assertion 必须是当前 POSIX UID 拥有、规范绝对路径、非 symlink、
0600 regular file,并通过 lstat → O_NOFOLLOW open → fstat 在同一 descriptor
读取。config/assertion 最大 16 KiB,command 最大 256 KiB。CA 是公开验证材料,但仍须
是规范、非 symlink、owner 为当前 UID 或 root、group/world 不可写的 regular file,
最大 256 KiB。
config schema v1 精确包含:
endpoint:固定/api/v3/plugin-packages/management的 HTTPS URL;servername:显式 DNS 名,必须等于 URL hostname;caFile:绝对 CA bundle 路径;requestTimeoutMs:1–30 秒。
不读取 HTTP_PROXY、HTTPS_PROXY、NO_PROXY 或系统代理。
3. 一次进程只执行一个 exact command
command 必须是 management transport 已公开的 schema v1 command,operation 只能是:
- Package install
propose|decide|inspect; - publisher emergency revocation
propose|decide|inspect; - publisher trust transition
propose|decide|inspect。
客户端不补 ID、不改 expected version、不派生 digest、不上传 PEM/snapshot,也不自动 重试。网络结果不确定时,operator 必须用原 command 文件显式重放;服务端 exact replay 负责收敛。
4. TLS 与 HTTP 严格失败关闭
client 固定:
- TLS 1.3 min/max;
rejectUnauthorized=true;- endpoint hostname 与显式
servername一致; - CA bundle 显式注入;
- 无 redirect、无 connection pooling、无压缩、单一 POST;
- request/response 各 256/128 KiB 硬上限;
- exact
application/jsonrequest 与application/json; charset=utf-8response; - bounded timeout,超时销毁 socket。
成功响应必须是 exact schema v1 envelope、带有 bounded request ID,且 result operation 与 command operation 完全一致。非 200 响应只保留 status、bounded error code、 request ID 和可选 Retry-After;不回显 response body、header、assertion、路径或 TLS diagnostic。
5. 输出保持低敏
stdout 只输出服务端已经审查过的低敏 result envelope。stderr 失败事实只包含稳定 component/event/error code、可选 HTTP status/request ID/retry-after;不输出 Error message、stack、文件路径、endpoint、assertion、command 原文或证书内容。
客户端在请求结束后清零可变 assertion/CA/command Buffer。Node.js/HTTP 内部可能产生 不可控复制,因此本能力不声称 secure enclave 或零内存残留;短生命周期进程和不落盘/ 不回显是当前边界。
不采用方案
用 curl 文档代替产品 client
Bearer 参数容易进入 shell history/process argv,TLS/redirect/response 上限也无法被 仓库门禁稳定约束。
client 自行持有 IdP 私钥并签 assertion
会把身份签发 authority 混入 Package 管理客户端;本 CLI 只消费外部 ceremony 产生的 短期 assertion。
把 client 合入 cluster-control
常驻 runtime 不应获得管理 assertion 或出站管理 authority。
新增独立 workspace package
客户端与现有 management protocol 同发布、无新依赖或独立制品矩阵;新增第 23 个包不满足 ADR-0185。
自动重试
网络断开不能证明服务端未提交。自动重试会隐藏 outcome unknown;显式 exact replay 才是 可审计恢复。
验收证据
@qinglong/cluster-admin/plugin-package-management-client与ql3-plugin-package-client已发布在既有 cluster-admin 包,workspace 仍为 22 包, 未新增第三方生产依赖、listener、timer、watcher 或 controller。- client 专项 5/5 通过:真实 TLS 1.3 + CA + DNS hostname、one request、九种 operation、 unknown operation、private/canonical/no-follow 文件、redirect、content-type、128 KiB response、timeout 与 CLI 输出脱敏均有正负向证据。
- cluster-admin 全量回归为 121 pass、0 fail、1 条真实 Kubernetes 条件 skip;构建闭包 同时编译 runtime-core、cluster-postgres 与 cluster-admin。
- cluster deployment audit 18/18,dependency boundary 37/37;22 个 package importer、 31 个 cluster-admin source、CloudNativePG 与 edge-import 审计均无 finding。
- operator 使用契约见
docs/operations/ql3-plugin-package-management-client.md。