feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,140 @@
# ADR-0192Kubernetes 私有管理隧道客户端
- 状态:Accepted
- 日期:2026-07-29
- 关联:RFC D-145、D-175、D-181、D-182ADR-0145、ADR-0185、ADR-0191
## 背景
D-181 已交付只接受私有文件、固定 TLS 1.3 的 one-command/one-request client,但
Cluster production Service 是 ClusterIPNetworkPolicy 只允许同 namespace 的 labelled
Pod。部署者若直接创建 Job,必须把 assertion 先写入 Kubernetes SecretSecret volume
又是 root-owned symlink,既让短期身份凭据进入 etcd,也不满足 canonical/current-UID
`0600` 文件门。
把 Service 改成 LoadBalancer、通过公共 Ingress 暴露、关闭 hostname 验证,或要求在低配
节点常驻管理 sidecar,都不是可接受的产品交付。
## 决策
### 1. 增加短生命周期 Kubernetes tunnel client
在既有 `@qinglong/cluster-admin` 内新增:
- `./plugin-package-management-kubernetes-client` subpath
- `ql3-plugin-package-client-kubernetes` executable。
它运行在 operator workstation,而不是 Cluster Pod。它不创建 Secret、ConfigMap、
Pod、Job、ServiceAccount 或 listener,只列出目标 management Pod 并通过 Kubernetes
`pods/portforward` 建立一个 8443 WebSocket tunnel。一次进程只 list 一次、打开一个
tunnel、发送一个 D-181 command,然后关闭 tunnel。
不新增 workspace package、生产依赖、镜像或常驻资源;现有 cluster-admin image/npm
closure 已包含 `@kubernetes/client-node`
### 2. Kubernetes authority 是独立私有入口
port-forward 流量由 API server/kubelet 建立,不能声称由 CNI NetworkPolicy 的 labelled
Pod ingress 规则证明。它是另一条显式私有入口,必须同时满足:
- operator kubeconfig 对目标 namespace 具有 `list pods`
`create pods/portforward`
- management HTTPS 仍执行 TLS 1.3、显式 CA、Service DNS hostname 与短期 User
assertion 验证;
- client 只选择 exact management labels、Running、Ready、无删除时间且
`automountServiceAccountToken=false` 的 12 个 Pod
- list 上限为 3,存在 continue、超过两个 ready target 或返回异常对象时失败关闭;
- 不自动切换 context、namespace、Pod、端口或重试。
Kubernetes RBAC 只能控制 port-forward subresource,不能代替 QingLong Project Policy、
双人审批或 durable quota。
### 3. kubeconfig 必须是显式、封闭的短期凭据
CLI 增加第四个路径参数:
- `--kubernetes=/absolute/kubernetes.json`
`0600` 文件精确包含 schema v1、`kubeconfigFile``context``namespace`
`apiTimeoutMs`。kubeconfig 也必须是当前 UID、canonical、no-follow `0600` regular
file,最大 256 KiB;禁止 ambient/default kubeconfig 和 context fallback。
为避免上游 parser 在审查前读取 `token-file` 或执行 credential provider,本客户端只接受
专用 exact JSON kubeconfig:必须恰有一个 cluster、一个 user、一个 context,且
`current-context` 与显式 context 完全一致。选中 context 必须绑定:
- canonical HTTPS API server、显式内嵌 CA、`skipTLSVerify=false`
- 无 proxy、无外部 CA/client certificate/key 文件;
- 内嵌短期 token,或匹配的内嵌 client certificate/private key
- 无 exec plugin、auth-provider、basic auth、impersonation 或文件引用。
这会排除依赖云厂商 exec credential plugin 的 ambient kubeconfig。部署者应由身份系统
签发一个最小权限、短生命周期、材料内嵌的专用 kubeconfig;后续若支持 credential
plugin,必须作为独立审计切片,不能隐式执行任意本机程序。
### 4. tunnel 不降低 D-181 TLS/HTTP 门
WebSocket 只提供 raw byte stream。D-181 client 在其上重新建立 TLS,继续强制:
- Service DNS `ql3-plugin-package-management.<namespace>.svc` 与 port 8443
- servername、显式 management CA、TLS 1.3 min/max
- one request、无 redirect、无压缩、无 retry、bounded response/timeout
- assertion/command/output 脱敏和结果 exact-shape 校验。
Kubernetes API TLS 与 management TLS 是两个独立验证层。API server、kubelet 或 tunnel
不能伪造通过 management CA/hostname 校验的服务。
### 5. Profile 与资源边界
Edge/Standalone 不安装或启动 tunnel client;它只属于 cluster-admin operator 制品。
路由器作为 Edge 节点新增零 idle CPU/RSS、零 socket、零 timer。单节点开发 Cluster 与
2-replica production Cluster 都可选择 1 个 ready targetproduction 服务端副本与
PostgreSQL HA 基线不因本客户端降低。
## 不采用方案
### Kubernetes Job + Secret volume
会让 assertion 进入 etcd/Secret 生命周期;projected volume 还是 root-owned symlink
与 D-181 私有文件门冲突。
### 公共 Ingress、LoadBalancer 或关闭 hostname verification
扩大攻击面并移除现有私有网络假设;管理 assertion 不是公开暴露的理由。
### `kubectl exec`/stdin 注入文件
会重新引入 stdin secret protocol、Pod lifecycle 和清理语义,且难以用产品测试证明
exact bytes/owner/mode。
### 常驻 sidecar/gateway
为低频 operator command 增加常驻资源、证书轮换和故障面,对小型节点不合适。
### 自动重试或自动换 Pod
管理 COMMIT 结果可能不确定。自动重建 tunnel/换 Pod 会隐藏 exact replay 边界;失败后
必须由 operator 使用原 command 显式重放。
## 验收证据
- Kubernetes client 专项 9/9exact JSON kubeconfig、私有文件、内嵌 token 与匹配
client cert/key、proxy/skip TLS/file/exec/auth-provider/basic auth/impersonation 拒绝、
ready Pod overflow/continue/unready/token-mount 拒绝、remote error 透传、response-loss
no-retry 和 CLI 脱敏均通过。
- 使用真实 `@kubernetes/client-node` `PortForward` 类和注入的 WebSocket handler,证明
exact `/api/v1/namespaces/qinglong3-system/pods/<name>/portforward?ports=8443`
v5 channel 前缀、双向 raw bytes 与 graceful close;随后以真实本机 TLS server 证明
tunnel 上仍为 Service DNS hostname + management CA + TLS 1.3。
- cluster-admin 全量为 130 pass、0 fail、1 条既有真实 Kubernetes 条件 skip
deployment/dependency 两套测试 50/50。cluster image release、22-importer dependency、
CloudNativePG、deployment 与 edge-import 审计全部无 finding。
- npm dry-run pack 为 68 entries、88,472 bytes compressed、542,810 bytes unpacked;新增
tunnel library/CLI 的 JavaScript 与 declarations 共 27,132 unpacked bytes,已被
package export/bin 和 deployment audit 精确绑定。
- workspace 保持 22 包;cluster-admin 使用既有 `@kubernetes/client-node@1.4.0`,未新增
生产依赖、镜像、Secret、Pod、Job、listener、timer 或 controller。Edge import 仍为
121 modulesEdge/Standalone 零 idle 资源增量。
- 当前证据不冒充真实 production Kubernetes/OIDC ceremony;真实 API server
port-forward、RBAC 和两 User assertion 仍属于 release/live evidence gate,而不是
放宽本 ADR 的静态权限与协议边界。