feat(ql3): attest physical edge release archives

This commit is contained in:
whyour
2026-08-14 15:29:33 +08:00
parent 51145a8028
commit 1d83d1d6bb
10 changed files with 1901 additions and 5 deletions
@@ -400,8 +400,41 @@ recorder 以 `--direct-service-start-evidence=<absolute-report>` 导入;通过
移除 `direct_release_unit_without_evidence_wrapper`firmware/bootloader、
whole-device 写放大、断电、签名和其它未采集门不变。
### Release archive 外部签名证明
ADR-0399 将 release archive Gate 设计为 QingLong verify-only 的两阶段协议。基础物理
报告必须已导入通过的 direct release start evidence`prepare` 把 source repository、
40 位 Git revision、物理设备/boot、release archive digest、实机 artifact tree 与 Node
identity 写入 canonical signing payloadoperator 在 QingLong 外部使用 Ed25519 私钥签署,
`finalize` 再用固定 SPKI 公钥复算 fingerprint 并验证 detached signature
```sh
pnpm evidence:physical-edge-release -- prepare \
--physical-report=/opt/qinglong/evidence/physical.json \
--release-archive=/opt/qinglong/releases/qinglong3-edge.tar.gz \
--repository=https://github.com/whyour/qinglong.git \
--revision=<40-lowercase-git-revision> \
--payload=/opt/qinglong/evidence/release-payload.json
# operator 外部签署 exact payload bytes
pnpm evidence:physical-edge-release -- finalize \
--physical-report=/opt/qinglong/evidence/physical.json \
--release-archive=/opt/qinglong/releases/qinglong3-edge.tar.gz \
--payload=/opt/qinglong/evidence/release-payload.json \
--signature=/opt/qinglong/evidence/release-payload.sig \
--trusted-public-key=/etc/qinglong/release-ed25519.pub \
--expected-repository=https://github.com/whyour/qinglong.git \
--expected-revision=<40-lowercase-git-revision> \
--output=/opt/qinglong/evidence/physical-release-evidence.json
```
验签通过只关闭 `release_archive_signature`。公钥分发/撤销、真实签发 ceremony、固定设备
采集、firmware/bootloader、whole-device flash、adopted migration 与断电证据继续独立;
输出永久保持 `supported=false`。私钥不得进入 QingLong 仓库、设备数据目录、环境变量或命令。
## 后续约束
物理设备 candidate recorder、进程 idle sampler、专用文件系统 fault probe、正式 TaskDefinition 规模记录、Compose storage、warm-Node native application start、wrapper init-manageddirect release init-managed 跨启动候选协议已实现;真实 Local Workflow 也已进入 128/256 MiB CI 资源门,但下一切片仍需补齐 firmware/bootloader clock、整机 wakeup/FTL 写放大、2.x adopted migration、application recovery、受控突发断电和 release archive signature,并在固定设备采集现有协议报告,而不是继续降低容器内存数字。只有固定 x64/arm64 设备矩阵完成后,才能把某一档位从 `ci_*` 提升为产品支持证据;任何阈值调整都必须说明硬件、内核、文件系统、工作负载和历史基线,Cluster 扩容结论继续走独立容量测试。
物理设备 candidate recorder、进程 idle sampler、专用文件系统 fault probe、正式 TaskDefinition 规模记录、Compose storage、warm-Node native application start、wrapper init-manageddirect release init-managed 与外部 release attestation 候选协议已实现;真实 Local Workflow 也已进入 128/256 MiB CI 资源门,但下一切片仍需补齐 firmware/bootloader clock、整机 wakeup/FTL 写放大、2.x adopted migration、application recovery、受控突发断电,并在固定设备执行现有 recorder 与真实 signing ceremony,而不是继续降低容器内存数字。只有固定 x64/arm64 设备矩阵完成后,才能把某一档位从 `ci_*` 提升为产品支持证据;任何阈值调整都必须说明硬件、内核、文件系统、工作负载和历史基线,Cluster 扩容结论继续走独立容量测试。
首次 active recorder 已绑定最终可发布 application artifact 与正式 readiness contractwrapper init-managed recorder 把 Node 前 kernel uptime、systemd/OpenRC 和 live process tree 纳入同一 bootdirect recorder 再以生产 startup receipt 关闭 wrapper 与最终 release unit 的结构差异。但 Linux uptime 仍不含 firmware,当前所有开发机/Docker 结果也未替代固定物理设备报告,所以仍不能替代完整 power-on Gate。当前 Executor benchmark 或一次模块 import同样不能替代。TaskDefinition 持久化 schema、Repository 与读取路径现已由 ADR-0089 冻结,100/1000/10000 规模记录器也已接入基础报告,但尚未采集固定设备结果。其 fresh schema migration 计时不得替代 2.x adopted database migration,后者仍须记录可审计的额外磁盘峰值采样精度。未完成的边界继续保留在 remaining evidence。
@@ -0,0 +1,118 @@
# ADR-0399:物理 Edge 证据的外部发布归档证明
- 状态:Accepted(协议、exact recorder 重建与本地离线验签已实现;18-package/backend/边界门通过,固定物理设备和真实 release ceremony 待执行)
- 日期:2026-08-14
- 关联 RFCQL-RFC-0001 D-05、D-06、D-86、D-196、D-307
- 关联 ADRADR-0088、ADR-0206、ADR-0363、ADR-0398
## 上下文
物理 Edge 聚合报告已经能绑定设备、数据文件系统、同 boot workload,以及最终
systemd/OpenRC unit 直接运行的 Node、production application artifact 和 startup
receipt;但它仍把 `release_archive_signature` 保留为发布 Gate。仅记录 Git revision
或归档 SHA-256 不足以证明归档、源码 revision、设备实际运行 artifact 与 Node 是同一
次发布决策;让 QingLong 自己生成并保存发布私钥,又会把构建、签发和验证 authority
混入低配设备工具。
基础 importer 还存在一个较窄的身份绑定缺口:Edge Executor、Node SQLite 和 Plugin
Package recovery 三个子 workload 虽在 recorder 中由同一进程启动,离线重建时没有逐项
复验其 platform/architecture。攻击者可以在重算外层摘要后把其他主机报告注入统一报告。
## 决策
新增非 workspace-package、无第三方依赖、无常驻进程的
`scripts/ql3-physical-edge-release-evidence.cjs`,根命令为
`evidence:physical-edge-release`。它只承担确定性载荷生成与验签,不持有或调用私钥。
协议分两阶段:
1. `prepare` 读取当前 UID 所有、`0600`、无 symlink 的统一物理报告,完整重建其 manifest、
三个基础 workload、全部 supplemental evidence、qualification 和 SHA-256;必须已有通过的
direct release service start evidence,并且 `release_archive_signature` 恰好仍缺一次。
2. 工具以稳定 descriptor 流式读取不超过 64 MiB、不可被 group/other 写入的 release archive
生成不带换行的 canonical JSON signing payload。payload 精确绑定 repository、40 位小写
Git revision、物理报告摘要、设备/boot、归档摘要/字节数,以及实机 direct report 中的
artifact tree、metadata、entrypoint、Node digest/version。
3. 发布 operator 在工具之外通过 HSM、KMS、离线机或 OpenSSL 使用 Ed25519 私钥签署 payload
原始字节。私钥不进入 QingLong data path、配置、环境变量、报告或进程参数。
4. `finalize` 从调用方固定的 Ed25519 SPKI 公钥重新计算 SHA-256 fingerprint,重新读取并
计算全部输入,要求 payload 是 exact canonical bytes,再验证 64-byte detached signature。
source revision、archive 或物理报告任一漂移都失败关闭;输出以 `0600`、no-replace、fsync
发布。
通过的最终 envelope 只把 `release_archive_signature` 替换为
`release_archive_signature_or_attestation`。它永久保持 `supported:false`,保留原报告中的
firmware/bootloader、whole-device flash、migration、断电等所有未完成证据。
基础 recorder 同时收紧 workload identityEdge report 的 `host.platform/architecture`
SQLite report 的 `platform/arch`、Plugin Package report 的
`identity.platform/architecture` 必须全部等于统一物理观测值。该检查不增加设备工作量,
只拒绝跨主机拼接。
## Operator ceremony
```sh
pnpm evidence:physical-edge-release -- prepare \
--physical-report=/opt/qinglong/evidence/physical.json \
--release-archive=/opt/qinglong/releases/qinglong3-edge.tar.gz \
--repository=https://github.com/whyour/qinglong.git \
--revision=<40-lowercase-git-revision> \
--payload=/opt/qinglong/evidence/release-signing-payload.json \
--json
# 在 QingLong 工具之外签署 exact payload bytes;下面只是一种 operator 实现。
openssl pkeyutl -sign -rawin \
-inkey /offline/release-ed25519.key \
-in /opt/qinglong/evidence/release-signing-payload.json \
-out /opt/qinglong/evidence/release-signing-payload.sig
chmod 600 /opt/qinglong/evidence/release-signing-payload.sig
pnpm evidence:physical-edge-release -- finalize \
--physical-report=/opt/qinglong/evidence/physical.json \
--release-archive=/opt/qinglong/releases/qinglong3-edge.tar.gz \
--payload=/opt/qinglong/evidence/release-signing-payload.json \
--signature=/opt/qinglong/evidence/release-signing-payload.sig \
--trusted-public-key=/etc/qinglong/release-ed25519.pub \
--expected-repository=https://github.com/whyour/qinglong.git \
--expected-revision=<40-lowercase-git-revision> \
--output=/opt/qinglong/evidence/physical-release-evidence.json \
--json
```
public key 与 archive 必须是 canonical regular file、无 symlink,且 group/other 不可写;
physical report、payload、signature 必须为当前 UID 所有的 `0600` 文件。所有输入在读取前后
复验 device/inode/size/mtime/ctime,输出拒绝覆盖已有文件。
## 不证明的内容
- verifier 不展开 archive,也不自行复现 build;“archive、source 与 runtime artifact 属于同一
发布”的关系由被信任 operator 的签名证明。
- 公钥分发、轮换、撤销、透明日志和 signer 组织流程仍由 release authority 负责。
- 签名不证明固定设备已经采集、firmware/bootloader 起点、exclusive cold cache、整机 CPU
wakeup、NAND/FTL 写放大、突发断电、adopted migration 或 Cluster 容量。
- 本地生成的测试 key 和合成物理 fixture 只验证协议,不可作为正式 release evidence。
## 被否决的方案
1. **把私钥放入仓库或 recorder**:混合签发与验证 authority,并扩大低配设备 Secret 面,拒绝。
2. **只签 archive digest**:无法绑定 source revision、实机报告和实际运行 artifact,拒绝。
3. **只信任外层物理报告摘要**:无法阻止可重算摘要下的跨 platform workload 拼接,拒绝。
4. **验签成功即设置 `supported:true`**:签名只关闭一个 Gate,不能替代剩余物理证据,拒绝。
5. **为一次性工具新建 workspace package**:没有独立制品、依赖或常驻 authority,违反 package
边界原则,拒绝。
## 验收
- exact payload、Ed25519 成功验签、公钥 fingerprint、归档篡改、source mismatch、私有文件
mode、canonical path、no-replace 和 remaining evidence 由 backend contract test 覆盖。
- 跨 platform Plugin Package workload 必须使基础物理 candidate 失败。
- 工具不得新增 workspace package、production dependency、daemon、listener、timer、watcher、
数据库 migration 或 Edge 常驻导入。
- 阶段合并前必须通过相关定向测试、18-package clean build/test、完整 backend、package 边界、
dependency/import 审计和 GitNexus staged change 审计。
当前实现完成后,18-package clean build/test 退出 0;完整 backend 为 1,208 项、1,206
通过、2 条 Linux 条件跳过、0 失败。package boundary 仍为 18 个 package
`singleSourcePackages=[]``shallowSourcePackages=[]`Cluster dependency、Edge import、
service bridge import 与 Cluster deployment 审计均无 finding。以上只验证协议和回归,
本地合成 fixture 与测试 key 不替代固定设备报告或正式 release signing ceremony。
+1
View File
@@ -402,6 +402,7 @@
| [ADR-0396](./ADR-0396-generation-transition-plugin-package-secret-binding.md) | 按 Package Generation 切换 Plugin Package Secret Binding | Proposed |
| [ADR-0397](./ADR-0397-explicit-cluster-secret-action-manual-recovery.md) | Cluster Secret Action 显式人工恢复 | Accepted(实现、单节点 PostgreSQL、完整 workspace/后端/边界与 physical HA 门完成) |
| [ADR-0398](./ADR-0398-pre-activation-plugin-package-candidate-qualification.md) | Plugin Package 激活前候选资格校验与自动保留旧版本 | Proposed(v2 私有报告/离线审计已编排、全量/HA 门完成;远端 Kubernetes 成功记录与物理低配门待闭合) |
| [ADR-0399](./ADR-0399-external-release-attestation-for-physical-edge-evidence.md) | 物理 Edge 证据的外部发布归档证明 | Acceptedexact recorder 重建与离线验签已实现;固定实机 ceremony 待执行) |
## 规则