mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 19:29:13 +08:00
120 lines
8.7 KiB
Markdown
120 lines
8.7 KiB
Markdown
# ADR-0265:有预算的一次性 Model Provider Credential Test Connection
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-08-03
|
||
- 关联:RFC D-08、D-12、D-159、D-175;ADR-0169、ADR-0263、ADR-0264
|
||
|
||
## 背景
|
||
|
||
Provider credential 的 bind/revoke ceremony 已有独立 manager,但“测试连接”若直接放进
|
||
manager,会迫使常驻管理进程同时持有 Provider Secret 与任意外网访问能力;若只由客户端传 URL、
|
||
deadline 或 retry,又会成为 SSRF、费用放大和绕过部署网络策略的入口。低配路由设备不应为一个
|
||
偶发操作长期支付 listener、连接池、timer 或 AI provider 依赖;Cluster 则需要 durable quota、
|
||
崩溃后不重复请求 provider、同步复制和晋升后的明确结果。
|
||
|
||
## 决策
|
||
|
||
### 1. Manager 只签发计划,不接触 Secret 或 Provider 网络
|
||
|
||
公开操作固定为 `provider-credential.test.plan`。请求只接受 `requestId/testId/projectId/provider`,
|
||
principal 由 transport 注入;endpoint、adapter、deadline、response/model/cost/retry 预算全部从
|
||
server-owned exact allowlist 解析。manager 要求五分钟内的 strong User、`secret.manage` 和当前
|
||
Project/RoleBinding fence,在一个 SERIALIZABLE transaction 内消费 durable quota、写 immutable plan
|
||
和 allowed SecurityAudit。相同业务 identity 与相同 lifetime 的晚到 API retry 返回原始 plan,不重复
|
||
消费 quota;调用方不能提交 URL、SecretRef、deadline、budget 或 retry。
|
||
|
||
allowlist 是不含 Secret 的 ConfigMap,单文件不超过 64 KiB、最多 16 个 provider。endpoint 只允许
|
||
canonical HTTPS,deadline 1–15 秒、response 1–256 KiB、models 1–256、费用固定为零、retry 固定为零;
|
||
plan 最长存活五分钟。manager Deployment 继续不挂 Provider Secret,base NetworkPolicy 继续没有
|
||
Provider egress。
|
||
|
||
### 2. Provider authority 只存在于 caller-driven one-shot tester
|
||
|
||
新增能力仍留在既有 `@qinglong/ai`、`@qinglong/cluster-admin` 和
|
||
`@qinglong/cluster-postgres` 的显式 subpath,不新增 workspace package。tester 只接受 exact
|
||
`executionId/testId/allowlist` command,使用专用 `ql3_ai_credential_tester` PostgreSQL role 和
|
||
`maxConnections=1`:
|
||
|
||
1. 先提交 immutable execution intent;
|
||
2. 只有 `status=created` 才在同一个 one-shot Pod 内执行不带 credential 的 CNI 收敛屏障:exact
|
||
provider TCP 必须可达,同时 deny-canary(Cluster 使用 Kubernetes API Service)必须不可达;两者共用
|
||
原始 deadline,不重试 Provider API;
|
||
3. 屏障成立后才逐请求解析 current binding、读取一个 Project-bound projected Secret,并在 Provider
|
||
请求前提交 content-free credential-use audit;
|
||
4. 对计划中的 `/models` 只执行一次 GET;
|
||
5. 只持久化 `reachable|unreachable`、model count、duration 与 digest,不保存 endpoint response、
|
||
SecretRef、token/header 或 raw error;
|
||
6. completion 暂时不可用时只重试同一个结果写入一次,绝不重打 provider。
|
||
|
||
已有 intent 但无 result 表示 `outcome_unknown`,必须停止且不得网络重放;已有 result 返回 exact
|
||
existing。deadline 使用 AbortSignal 和 monotonic clock 双围栏;超时、provider error、响应超限或
|
||
model 超限统一保存 content-free `unreachable`。
|
||
|
||
### 3. PostgreSQL 与部署权限分离
|
||
|
||
`pg-9015` 新增 plan/quota/execution/result 四张表。manager 只能写 plan/quota/allowed audit,tester
|
||
只能读 plan/current binding、append execution/result/use audit;tester 不能读写 quota、SecurityAudit、
|
||
Prompt output、invocation、price、Package、Worker、admin、maintenance 或 migration authority,也没有
|
||
`ql3` schema USAGE。readiness 对拒绝权限使用 `pg_catalog` OID 检查,不能为让自检通过而扩权。
|
||
|
||
Kubernetes tester 是 opt-in、caller-driven Job:`backoffLimit=0`、`activeDeadlineSeconds=60`、
|
||
`ttlSecondsAfterFinished=300`、tokenless ServiceAccount、non-root/read-only/drop ALL,request 为
|
||
25m CPU/48 MiB、limit 为 500m/192 MiB。base 只有 DNS egress,因此故意不能访问 provider;
|
||
CloudNativePG overlay 只增加 tester role 的 PostgreSQL 5432,部署者再以私有 overlay 为 exact private
|
||
provider `/32:443` 放行。标准 NetworkPolicy 不能表达 FQDN,故应用 exact HTTPS allowlist 与部署 CIDR
|
||
必须同时成立;公网 SaaS 需要 CNI FQDN policy 或受审 egress proxy,不能静默放宽 `0.0.0.0/0`。
|
||
|
||
### 4. Edge/Standalone 保持零常驻成本
|
||
|
||
本 ADR 不把 Cluster manager/tester、`pg`、Kubernetes client、listener 或 pool 带入 Edge/Standalone。
|
||
低配设备若后续需要同等产品入口,应复用短生命周期本机 Owner/SQLite ceremony 和 AI feature gate;
|
||
不得为了 API 对称启动常驻 daemon。Cluster 可由 Job 横向处理不同请求,但每个 test 仍是唯一 intent、
|
||
零 provider retry;不能把副本数变成隐式 provider 重试。
|
||
|
||
## 被否决方案
|
||
|
||
1. **让 manager 直接测试**:合并身份管理、Secret read 和任意 provider egress 三种 authority。
|
||
2. **客户端直接传 URL/预算**:打开 SSRF、长连接、响应放大和费用绕过。
|
||
3. **已有 intent 自动重打 provider**:COMMIT 响应丢失时会产生重复外部副作用和费用。
|
||
4. **按 provider 建 workspace package**:只有少量协议/adapter 文件,没有新的独立交付或依赖反转价值。
|
||
5. **base NetworkPolicy 放行公网 443**:无法证明流量只去 allowlist hostname。
|
||
6. **给 tester `ql3` schema USAGE 以通过 readiness**:扩大实际 authority,并掩盖拒绝权限探针缺陷。
|
||
|
||
## 当前验证
|
||
|
||
- AI 全量 176 项:173 通过、3 条条件跳过、0 失败;Cluster Admin 全量 242 项:240 通过、
|
||
2 条条件跳过、0 失败;plan/execution PostgreSQL 定向 9/9;
|
||
- manager service/transport/client/process、executor/process/CLI、Kustomize base/CNPG、manager/tester/
|
||
CloudNativePG/dependency audit 均通过;workspace 仍为 19,没有为 test connection 新增 package;
|
||
- PostgreSQL 18.4 arm64 physical HA 实际使用 `ql3_ai_credential_tester` 单连接池,证明 plan/execution
|
||
exact replay、provider 调用恰好一次、Secret material 清零一次、completion COMMIT 已提交但响应
|
||
丢失后以同结果收敛、四类 durable fact 无 Secret、`remote_apply` 同步 WAL、timeline 1→2 晋升后
|
||
facts/readiness 完全一致;新增五个具体 gate 和总 `gates.passed=true`;
|
||
- HA 运行后 `ql3-ha-*` 容器、网络、卷零残留,既有
|
||
`ql3-cnpg-evidence-control-plane` 容器保持运行且未被操作。
|
||
- 显式 opt-in 的真实三节点 K3s/arm64 纵切面使用锁定的 K3s `v1.34.3+k3s1`、CloudNativePG
|
||
`1.30.0` 与 PostgreSQL `18.4`,三个 PostgreSQL 实例分别预绑定到两个 worker 和唯一 control-plane;
|
||
初始主库固定在 worker A,停止该 worker 后 primary 成功切换,worker 恢复后数据库回到 3/3 Ready,
|
||
唯一 control-plane 始终在线;
|
||
- 纵切面真实执行 8 个 one-shot Job(7 个 execution、1 个 exact replay),证明 projected Secret
|
||
material generation 1→2、旧 material 拒绝、新 Pod 重解析、Provider Pod/CIDR 轮换、旧 CIDR
|
||
fail-close、exact private `/32:8443`、DNS/CloudNativePG allow 与 Kubernetes API/公网 deny。CNI 在新
|
||
Pod 创建后存在短暂策略编程窗口,因此 tester 现在必须在 Secret/binding/audit 前同时证明 Provider
|
||
allow 与 Kubernetes API deny,不能以另一个预检 Pod 代替;
|
||
- 最终 content-free 报告为 `gates.passed=true`:7 plan/execution/result、7 plan audit、5 次实际到达
|
||
credential-use 阶段的 use audit、4 reachable、3 unreachable、5 次 Provider `/models` 请求、零 replay
|
||
duplicate,并在 CloudNativePG failover 后完整存活;报告审计和部署审计均无 finding;
|
||
- Docker K3s 的本地 `imagePullPolicy: Never` 镜像会在节点重启或 containerd GC 后消失,测试夹具在
|
||
节点恢复后重新导入 exact 本地 admin image,再执行全节点收敛。这是离线测试制品恢复,不是生产
|
||
Provider 或数据库重试。成功运行后随机 K3s 容器、网络、卷和测试镜像零残留,受保护的既有
|
||
evidence control-plane 未被操作。
|
||
|
||
## 剩余生产证据与边界
|
||
|
||
本 ADR 的私有 Provider test-connection 接受门已经关闭,但该 Docker 夹具使用 privileged K3s、
|
||
fixture-only prebound hostPath、单个 Docker host 和唯一 Kubernetes control-plane;它不能冒充动态 CSI、
|
||
基础设施 STONITH、control-plane HA 或跨故障域证据。确定性私有 HTTPS Provider 也不是外部 SaaS。
|
||
公网 SaaS 仍必须选择并验证 CNI FQDN policy 或 egress proxy,不能复用私网 `/32` 结论;真实外部
|
||
OIDC/mTLS、KMS/Vault/HSM custody、首次 provision/active rotation/lost-secret recovery 继续由后续
|
||
ADR 和发布门负责。
|