diff --git a/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md b/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md index 2f6ea7da..0f36a45d 100644 --- a/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md +++ b/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md @@ -11,7 +11,7 @@ 最新增量证据(2026-08-24): -- D-404/ADR-0499(已验收):Cluster Worker 现在有可选的直接外部 Secret custody adapter,而不再只能依赖 Kubernetes Secret value projection。`vault-kv-v2` 位于既有 `@qinglong/cluster-control` Remote Execution 子域,只有显式选择 provider 的 Cluster 进程才动态加载;基础部署继续使用 `mounted-files`,Edge/Standalone 不新增 package、依赖、daemon、timer、watcher、连接池或常驻内存。adapter 只在 durable Run/Attempt/Lease/Worker Session/execution digest/SecretRef authority 通过后,用 `SHA-256(canonical SecretRef)` 路径读取 KV v2;只接受显式私有 CA 的 TLS 1.3、每次重新打开的短期 orphan/non-renewable service token 和唯一精确 policy,不跟随 redirect、不使用系统 CA 回退、不缓存值或 token。Kubernetes overlay 删除 value Secret projection,只挂载 CA 与 token;普通 Secret、opaque environment bundle 和总响应仍受原 16/96/256 KiB 边界约束,空 Secret 保持合法,异常 envelope、metadata、digest、token、TLS 或 Vault availability 均失败关闭且不回退。真实 arm64 Vault 1.21.4 gate 已完成 3-share/2-threshold init、两个普通 Secret 与一个 bundle、value/token 原子轮换、旧 accessor revoke、缺失 material、不可信 CA、seal/unseal 与同持久存储容器替换;`0600` content-free 报告 SHA-256 为 `df225509cb763009b610cb0aea2207e0b07b5e05a44cf8cf0dff1633c1624d52`,audit 为 `compatible=true/findings=[]`。Cluster Control 为 `279 total / 277 pass / 2 conditional skip / 0 fail`,backend 为 `1574 total / 1572 pass / 2 conditional skip / 0 fail`,18-package clean build/test 退出 0;package、Cluster dependency、122-module Edge import、部署和 14 档 Local artifact 审计全部 compatible,基础 Edge/Standalone 仍为 `2,669,390 / 2,669,468 bytes`、325 files、58 modules。共享 CI 新增原生 x64/arm64 live matrix。该 fixture 关闭 QingLong 直接 custody adapter/data-boundary 门,不冒充生产 Vault HA、KMS/HSM seal、审计设备或灾备证明;ADR-0491 现在只剩固定低性能物理 Edge 的真实空间、RSS/I/O、写放大、ENOSPC 与断电恢复门。 +- D-404/ADR-0499(已验收):Cluster Worker 现在有可选的直接外部 Secret custody adapter,而不再只能依赖 Kubernetes Secret value projection。`vault-kv-v2` 位于既有 `@qinglong/cluster-control` Remote Execution 子域,只有显式选择 provider 的 Cluster 进程才动态加载;基础部署继续使用 `mounted-files`,Edge/Standalone 不新增 package、依赖、daemon、timer、watcher、连接池或常驻内存。adapter 只在 durable Run/Attempt/Lease/Worker Session/execution digest/SecretRef authority 通过后,用 `SHA-256(canonical SecretRef)` 路径读取 KV v2;只接受显式私有 CA 的 TLS 1.3、每次重新打开的短期 orphan/non-renewable service token 和唯一精确 policy,不跟随 redirect、不使用系统 CA 回退、不缓存值或 token。Kubernetes overlay 删除 value Secret projection,只挂载 CA 与 token;普通 Secret、opaque environment bundle 和总响应仍受原 16/96/256 KiB 边界约束,空 Secret 保持合法,异常 envelope、metadata、digest、token、TLS 或 Vault availability 均失败关闭且不回退。真实 arm64 Vault 1.21.4 gate 已完成 3-share/2-threshold init、两个普通 Secret 与一个 bundle、value/token 原子轮换、旧 accessor revoke、缺失 material、不可信 CA、seal/unseal 与同持久存储容器替换;capability-free `0600` content-free 报告 SHA-256 为 `281fe542e1bf6078132216b9701a28e76f36dcbccc95367997d8457773a9c210`,audit 为 `compatible=true/findings=[]`。live fixture 以非 root、read-only rootfs、`cap-drop=ALL`、零新增 capability、`no-new-privileges` 与 `memory-swappiness=0` 运行,并为原生 hosted runner 的 capability/lock 差异显式关闭 fixture mlock;生产 Vault host 的 swap/mlock 硬化不由该门冒充。Cluster Control 为 `279 total / 277 pass / 2 conditional skip / 0 fail`,backend 为 `1574 total / 1572 pass / 2 conditional skip / 0 fail`,18-package clean build/test 退出 0;package、Cluster dependency、122-module Edge import、部署和 14 档 Local artifact 审计全部 compatible,基础 Edge/Standalone 仍为 `2,669,390 / 2,669,468 bytes`、325 files、58 modules。共享 CI 新增原生 x64/arm64 live matrix。该 fixture 关闭 QingLong 直接 custody adapter/data-boundary 门,不冒充生产 Vault HA、KMS/HSM seal、审计设备或灾备证明;ADR-0491 现在只剩固定低性能物理 Edge 的真实空间、RSS/I/O、写放大、ENOSPC 与断电恢复门。 - D-397/ADR-0491(进行中):Secret/Config reconciliation 已先冻结行为保持边界,不能把“密文已保存”冒充“任务已迁移”。2.x `Envs` 的 active 行必须按 `isPinned DESC、position DESC、createdAt ASC、id ASC` 重放旧顺序,同名值用 `&` 形成唯一 effective Secret;该 diff --git a/docs/adr/ADR-0499-direct-vault-kv-worker-secret-custody.md b/docs/adr/ADR-0499-direct-vault-kv-worker-secret-custody.md index e1351ca6..a1cfd4b0 100644 --- a/docs/adr/ADR-0499-direct-vault-kv-worker-secret-custody.md +++ b/docs/adr/ADR-0499-direct-vault-kv-worker-secret-custody.md @@ -95,7 +95,9 @@ path "auth/token/lookup-self" { - unseal 后恢复,Vault 容器在同一持久存储上替换后值仍可读取; - 私有 `0600` 报告不含 token、SecretRef、value、证书私钥或 Vault path。 -最终本地报告 SHA-256 为 `df225509cb763009b610cb0aea2207e0b07b5e05a44cf8cf0dff1633c1624d52`,离线 audit 为 `compatible=true`、`findings=[]`。共享 CI 在原生 x64/arm64 runner 上分别构建 provider、审计 overlay、拉取同一 digest-pinned Vault image 并重跑完整 live contract;远程运行结果作为提交后的独立证据。 +该单机 fixture 以当前非 root UID/GID、read-only rootfs、`cap-drop=ALL`、不增加 capability、`no-new-privileges` 和 `memory-swappiness=0` 运行。为兼容 capability/lock 策略不同的原生 Linux hosted runner,fixture 显式 `disable_mlock=true`;这只作用于临时测试 authority,不替代生产 Vault 主机的 swap 禁用或 mlock、HA seal 与审计硬化门禁。 + +最终 capability-free 本地报告 SHA-256 为 `281fe542e1bf6078132216b9701a28e76f36dcbccc95367997d8457773a9c210`,离线 audit 为 `compatible=true`、`findings=[]`。共享 CI 在原生 x64/arm64 runner 上分别构建 provider、审计 overlay、拉取同一 digest-pinned Vault image 并重跑完整 live contract;远程运行结果作为提交后的独立证据。 本地完整验证中,Cluster Control 为 `279 total / 277 pass / 2 conditional skip / 0 fail`,backend 为 `1574 total / 1572 pass / 2 conditional skip / 0 fail`,18-package clean build/test 退出 0。package boundary 保持 18 packages、`singleSourcePackages=[]`、`shallowSourcePackages=[]`;Cluster dependency、122-module Edge import、service-manager bridge、Cluster deployment、Local image、Vault overlay 和 14 档 Local artifact 均 compatible。基础 Edge/Standalone 制品仍为 `2,669,390 / 2,669,468 bytes`、325 files、58 loaded modules,不包含 Cluster Control 或 Vault adapter。 diff --git a/scripts/ql3-vault-kv-worker-secret-live-contract.cjs b/scripts/ql3-vault-kv-worker-secret-live-contract.cjs index 904e8cfb..2a5c0d79 100644 --- a/scripts/ql3-vault-kv-worker-secret-live-contract.cjs +++ b/scripts/ql3-vault-kv-worker-secret-live-contract.cjs @@ -302,8 +302,8 @@ function startVaultContainer(container, publish, directory, tls) { `${uid}:${gid}`, '--cap-drop', 'ALL', - '--cap-add', - 'IPC_LOCK', + '--memory-swappiness', + '0', '--security-opt', 'no-new-privileges:true', '--read-only', @@ -436,7 +436,7 @@ async function main() { 'vault-server.hcl', [ 'ui = false', - 'disable_mlock = false', + 'disable_mlock = true', 'api_addr = "https://127.0.0.1:8200"', 'cluster_addr = "https://127.0.0.1:8201"', 'storage "file" {', @@ -760,7 +760,7 @@ async function main() { }, limitations: [ 'single-host file storage is not Vault integrated-storage HA or an HSM seal quorum', - 'the short-lived private CA and service tokens are live fixture authorities rather than enterprise PKI or workload identity', + 'the capability-free live fixture disables mlock with zero container swappiness and does not prove production Vault host swap hardening', 'the live gate proves direct external custody resolution and rotation, not fixed physical Edge storage behavior', ], }; diff --git a/test/back/ql3VaultKvWorkerSecretLiveAudit.test.cjs b/test/back/ql3VaultKvWorkerSecretLiveAudit.test.cjs index 1b9a3e45..02ec7a1d 100644 --- a/test/back/ql3VaultKvWorkerSecretLiveAudit.test.cjs +++ b/test/back/ql3VaultKvWorkerSecretLiveAudit.test.cjs @@ -1,6 +1,8 @@ 'use strict'; const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); const { test } = require('node:test'); const { @@ -10,6 +12,14 @@ const { validateVaultKvWorkerSecretLiveReport, } = require('../../scripts/ql3-vault-kv-worker-secret-live-audit.cjs'); +const LIVE_CONTRACT = fs.readFileSync( + path.resolve( + __dirname, + '../../scripts/ql3-vault-kv-worker-secret-live-contract.cjs', + ), + 'utf8', +); + function fixture() { return { schemaVersion: 1, @@ -86,3 +96,10 @@ test('rejects sensitive material or widened report shape', () => { assert.ok(findings.includes('report envelope is invalid')); assert.match(findings.join('; '), /endpoint is forbidden/); }); + +test('keeps the hosted-runner fixture capability-free and non-swappable', () => { + assert.match(LIVE_CONTRACT, /'--cap-drop',\n\s+'ALL'/); + assert.match(LIVE_CONTRACT, /'--memory-swappiness',\n\s+'0'/); + assert.match(LIVE_CONTRACT, /'disable_mlock = true'/); + assert.doesNotMatch(LIVE_CONTRACT, /'--cap-add'|'IPC_LOCK'/); +});