Files
qinglong/docs/adr/ADR-0254-digest-pinned-cross-architecture-os-vulnerability-release-gate.md
T

99 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0254Digest 固定的跨架构 OS 漏洞发布门
> ADR-0255 supersedes 本 ADR 决策 5 中 release candidate 到 publisher 的传输方式:release 现在 build once、扫描
> exact OCI layout tar,并按同一 audited digest 发布。本 ADR 的 base pin、Trivy pin、OS-only gate 与例外治理继续有效。
- 状态:Acceptedbase pin、策略审计器、CI/release 接线与负向测试已实现;联网六矩阵扫描记录待取得)
- 日期:2026-08-01
- 关联 RFCQL-RFC-0001 D-14、D-61、D-128、D-186、D-236、D-237
- 关联 ADRADR-0128、ADR-0195、ADR-0196、ADR-0253
## 背景
QingLong 3.0 已对 npm production graph 执行 high/critical advisory gate,但这不覆盖 Debian/base image OS package。
同时 control/admin Dockerfile 仍通过可覆盖的 `ARG NODE_IMAGE` 引用 tag,而 local image 已固定多架构 digest;即使
对某次本地 build 扫描通过,后续 build 也可能解析到另一份基础镜像。
Trivy 官方在 2026-03 披露过 action/tag 与部分二进制发布渠道被篡改的供应链事件,并明确要求 action 使用完整
commit SHA。D-237 不能用 movable `@v0.x``latest` 引入一个拥有 workflow 执行能力的 scanner。
## 决策
1. control、control-ai、admin、local、worker 的 dependency/build stage 固定
`node:24.18.0-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d`,最终 runtime stage 固定更小的
`node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436`
control/admin 删除 `ARG NODE_IMAGE`,所有 runtime base 都不得由 build arg 覆盖;四个 Dockerfile 继续拥有独立应用闭包和
最终 image digest。构建与运行基座分离,避免把编译工具链和 Bookworm 的 Perl/util-linux/ncurses 包带入低配设备与集群运行镜像。
2. scanner 固定为 Trivy `v0.70.0`GitHub Action 固定完整 commit
`ed142fd0673e97e23eac54620cfb913e5ce36c25`action `v0.36.0`)。禁止 tag、`master``latest`
`continue-on-error` 和 scanner cache;网络、release asset 或 vulnerability DB 不可用均失败关闭。
3. 只扫描 `vuln-type=os``scanners=vuln`、severity `HIGH,CRITICAL``ignore-unfixed=false`、exit code 1。
npm/library advisory 继续由既有 production dependency audit 独立负责,不能用 OS 例外隐藏应用依赖漏洞。
4. `.github/workflows/ql3-ci.yml` 复用已经存在的 native buildcontrol、control-ai、admin、local、worker
各 amd64+arm64,共十个真实本地 image。每个 build 后生成 image-scoped ignore view 并执行 Trivy。
5. release workflow 增加独立只读 `os-vulnerability` 十矩阵 job,在发布前用 exact Dockerfile、base digest、lock 和
source revision 构建 native candidate。`publish` 必须同时依赖 D-236 private evidence 与全部 OS scan 成功,
才能获得 GHCR/OIDC/attestation 写权限。scan job 不读取 D-236 私密 source,不参与签名或 publish。
6. 唯一例外事实源为 `deploy/containers/ql3-os-vulnerability-exceptions.json`。顶层和每条 entry 都是 exact shape
每条必须包含唯一且排序的 CVE、非空且排序的 `admin|control|local` image 集合、非空且排序的
`pkg:apk|deb|rpm` purl、owner、ticket、20512 字理由和未来到期日。
7. 例外最多 128 条,单条例外最长 30 天;到期当天即无效。策略审计器为每个 image 生成临时 `0600`、no-replace
`.trivyignore.yaml`,映射为 Trivy 官方 `id+purls+expired_at+statement`。不得直接维护 `.trivyignore`、全局 CVE ID、
path skip、Rego allow-all、VEX bypass 或无期限 exception。
8. 当前 production policy 为 `exceptions=[]`。新增例外必须先关联安全 ticket、指定责任 owner、确认 package purl 与
受影响 image、设置最短到期日,并通过变异测试;修复 base 后立即删除,不等待到期。
9. scanner、生成器、策略和 workflow 都是 repository/release-time 工具,不新增 workspace package、第三方 runtime
dependency、migration、daemon、controller、timer、watcher、listener、Pool 或 Profile 常驻资源。
## 失败与恢复
- base digest/tag/action/version 漂移:静态审计失败,先审查上游 release 与签名,再显式更新 ADR/测试;
- scanner 或 DB 下载失败:CI/release 失败,不回退到旧 DB、不跳过扫描;
- HIGH/CRITICAL 命中且无例外:升级基础镜像/OS package;只有短期无法修复时才按本 ADR 添加 exception
- exception 到期、超过 30 天、缺 owner/ticket/rationale、使用 library purl 或 image scope 为空:policy gate 失败;
- 一个架构构建/扫描失败:整个 `os-vulnerability` matrix 不满足,三个 publisher 都不得启动;
- 本地临时 scanner 下载超时:只能记录“未取得本地 live scan”,不能把静态契约或 checksum 预检记为漏洞通过。
## 被拒绝的替代方案
### 发布后扫描 GHCR digest
拒绝。HIGH/CRITICAL 被发现前 tag/manifest 已进入 registry;发布前 native candidate scan 才能阻止写入。
### 只扫描 amd64
拒绝。多架构 base 的 OS package/config 可能不同,amd64 结果不能证明 arm64 路由器/节点镜像。
### `ignore-unfixed=true`
拒绝。它把所有无修复漏洞变成隐式永久例外;D-237 要求每个例外有 scope、owner、ticket、理由和到期日。
### 直接提交 `.trivyignore.yaml`
拒绝。Trivy 的 `statement` 不参与过滤,且原生 schema 不要求 owner、ticket、最长周期或 image scope;仓库级严格
策略生成 image-specific view 可以补足这些治理约束。
### 用 action tag 或 scanner `latest`
拒绝。上游供应链事故已证明 tag 可被重写;完整 action commit 与 exact scanner version 是最低可接受边界。
## 验证
- OS exception policy 8/8empty、image scope、purl、owner/ticket/rationale、expired/same-day/>30 天、排序/重复、
`0600` no-replace 与 exact CLI
- image release contract 34/34:新增覆盖 CI/release action pin、六矩阵、publish dependency、unfixed、OS-only、
cache 与既有签名/SBOM/provenance 门;
- Cluster deployment 31/31,包含 control/admin mutable/overridable base 负向测试;
- 2026-08-22 CI 的 Bookworm runtime 扫描在 amd64/arm64 同时发现 30 个 HIGH/CRITICAL;同版 Trixie Slim 为 51 个,
Debian 13 distroless 为 1 个无修复 HIGH。固定的 Alpine 3.23 multiarch digest 使用同一 Trivy 0.70.0 与同一数据库扫描,
amd64/arm64 均为 0 HIGH/CRITICAL,且 Node 版本仍为 24.18.0;完整十镜像远端扫描仍是合并门;
- `audit:image-release:ql3` 返回 Trivy 0.70.0、exact action commit、amd64/arm64、OS-only、HIGH/CRITICAL、
`ignoreUnfixed=false`、maximum exception 30 days
- 当前 policy `exceptionCount=0`workspace 保持 19 个 package,无新增 runtime dependency。
## 上游依据
- Trivy filtering 与 YAML `id/purls/expired_at/statement`<https://trivy.dev/docs/latest/configuration/filtering/>
- Trivy ecosystem supply-chain advisory<https://github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23>
- Trivy Action v0.36.0<https://github.com/aquasecurity/trivy-action/releases/tag/v0.36.0>。