attested-delivery/attested-pipeline-template
GitHub: attested-delivery/attested-pipeline-template
语言无关的 GitHub Actions 发布流水线模板,通过 SLSA 来源证明、SBOM、安全门禁签名和失败阻断验证,确保仅经过完整校验的制品被发布。
Stars: 0 | Forks: 0
# attested-pipeline-template
一个与语言无关的 GitHub 仓库模板,演示了**经过验证的发布架构**(attested release architecture):到达消费者手中的每一个制品(artifact)都与经过验证的版本在字节上完全一致,携带 SLSA 来源(provenance)和 SBOM 证明(attestations),并在每个环节重新验证,且发布过程在验证失败时会阻断(fail-closed)。
标签(tag)不会发布任何未经验证的内容。
## 不变量
```
build → attest-provenance → generate+attest-SBOM → fail-closed-verify → [tag-gated] publish
```
没有步骤可以跳过。如果 `verify` 失败,`publish` 将不会运行 —— GitHub Actions
通过 `needs:` 依赖图来强制执行这一点。
## 此模板包含的内容
```
.github/
workflows/
release.yml # The attested release pipeline (see below)
ci.yml # CI + mandatory action-pin check
SECURITY.md # Verification instructions for consumers
README.md # This file
LICENSE # Apache-2.0
```
### `release.yml` — 经过验证的发布流水线
| Job(作业) | 功能描述 | 关键权限 |
|---|---|---|
| `meta` | 从 git ref 解析制品名称 + 版本 | `contents: read` |
| `build` | 构建制品,证明(attest) SLSA 构建来源,暴露主体摘要(subject digest) | `id-token: write`, `attestations: write` |
| `sbom` | 通过 Syft 生成 CycloneDX SBOM,并进行证明 | `id-token: write`, `attestations: write` |
| `gate-sast` / `attest-sast` | SAST (CodeQL) → 对主体生成 seam-sign 判定 (`sast/v1`) | `security-events: write` / `attestations: write` |
| `gate-sca` / `attest-sca` | SCA (OSV) → 对主体生成 seam-sign 判定 (`sca/v1`) | `security-events: write` / `attestations: write` |
| `gate-trivy` / `attest-iac-license` | IaC/license (Trivy) → 生成 seam-sign 判定 (`iac-license/v1`) | `security-events: write` / `attestations: write` |
| `vex` | OpenVEX 处置,自签名(`reusable-vex.yml`,`openvex.dev/ns/v0.2.0`)应用于主体 | `id-token: write`, `attestations: write` |
| `verify` | **失败阻断(Fail-closed)**:验证来源 + SBOM + 每一项 seam 判定 (sast/sca/iac-license) + VEX | `attestations: read` |
| `publish` | 创建带有校验和的 GitHub Release | `contents: write` — 仅在推送 tag 时触发,且仅在 `verify` 通过时运行 |
该流水线支持将 `workflow_dispatch` 作为试运行(dry-run):完整的
构建 → 证明 → 验证链条会运行,但 `publish` 会被跳过(受 tag 限制)。
**制品判定在发布时被签名并验证。**
每一个用于描述已发布制品的 gate 判定 —— SAST (CodeQL)、SCA
(OSV)、IaC/license (Trivy)、container-scan (Trivy image) 和 DAST (ZAP) ——
都会在发布时被签名并验证,通过摘要绑定到发布主体上。SAST
分析的是确切要发布的源代码,因此它的判定会像其他判定一样伴随发布一同传递。供应链安全态势(Scorecard)是一个仓库级别的信号,而不是
制品判定。此模板是一个**完整的验证参考**:`release.yml` 中包含 SAST + SCA
+ IaC/license + VEX,`dast.yml` 中包含 DAST,`quality-gates.yml` 中包含合并时的
SAST/SCA/Scorecard/Trivy。Container-scan (Trivy image) 在此
不适用(此模板发布的是 tarball,而不是 image);有关容器化的变体,请参见
`attested-delivery/rust-template`。
### `quality-gates.yml` — 合并时的 gate
这是针对中心化 SAST (CodeQL)、SCA (OSV)、安全态势 (Scorecard) 以及
IaC/license (Trivy) 可复用工作流的轻量级调用方。每一个都以 SARIF 进行标准化,并显示在
仓库的 Security 标签页下;您可以将 "Code scanning results" 设置为必需的状态检查(status check),
使其成为合并 gate。安全态势(Scorecard)在此作为仓库级别的信号存在。
### `ci.yml`` — CI + Pin 检查
包含一个强制性的 `pin-check` 作业 —— 它是中心化的
`attested-delivery/.github` **`pin-check.yml`** 可复用工作流的轻量级调用方(通过 `.github`
仓库的 commit SHA 进行锁定),符合 CLAUDE.md 第 2/3 节的要求(使用中心化的可复用工作流,绝不
重复造轮子)。如果任何 `uses:` 通过 tag 或
分支而不是完整的 40 个字符的 commit SHA 引用 action,它将失败并阻断。
### `dast.yml` — 经过验证的 DAST (ZAP)
具体的 DAST 示例:将中心化的 `reusable-zap.yml` 指向一个**正在运行**
的目标(`workflow_dispatch` 输入 `target`,默认为组织已部署的站点),
然后 `attest-dast` 对判定进行 seam-sign 签名,作为绑定到主体摘要上的 `dast/v1`,
并且一个失败阻断的 `verify` 作业会对其重新检查。请搭建您自己已部署的
preview/staging 环境并传入其 URL。要求组织 Actions 允许列表(allow-list)必须允许
单个 action `zaproxy/action-full-scan@*`(或其确切锁定的 SHA)—— 而不是
`zaproxy/*` 拥有者通配符 —— 因为 `reusable-zap.yml` 会拉取
`zaproxy/action-full-scan`。
## 适配此模板
### 1. 将此作为模板仓库使用
在 GitHub 上点击 **"Use this template"** 以创建一个新仓库。
### 2. 替换构建步骤
在 `release.yml` 中,找到 `build` 作业中的 `# ADAPT THIS STEP` 块:
```
- name: Build artifact
run: |
# Replace this with your toolchain's build command.
# Output MUST land under dist/
git archive --format=tar.gz ...
```
按不同语言的示例:
**Go:**
```
- name: Build artifact
run: |
mkdir -p dist
GOOS=linux GOARCH=amd64 go build -o dist/${NAME}-${VERSION}-linux-amd64 ./cmd/...
```
**Rust:**
```
- name: Build artifact
run: |
cargo build --release --locked
mkdir -p dist
cp target/release/mybinary dist/${NAME}-${VERSION}-linux-amd64
```
**Node.js:**
```
- name: Build artifact
run: |
npm ci
npm run build
mkdir -p dist
tar -czf dist/${NAME}-${VERSION}.tar.gz --exclude=node_modules .
```
**Docker:**
将 `actions/attest-build-provenance` 与指向 `docker buildx build --iidfile` 输出的 image digest 的 `subject-digest` 配合使用。
### 3. 更新 action 的 SHA
此模板中的 action SHA 是在 **2026-06-18** 解析的。定期轮换它们:
```
gh api repos/actions/checkout/commits/v4 --jq .sha
gh api repos/actions/attest-build-provenance/commits/v2 --jq .sha
gh api repos/actions/attest-sbom/commits/v2 --jq .sha
gh api repos/anchore/sbom-action/commits/v0 --jq .sha
gh api repos/softprops/action-gh-release/commits/v2 --jq .sha
gh api repos/actions/upload-artifact/commits/v4 --jq .sha
gh api repos/actions/download-artifact/commits/v4 --jq .sha
```
更新工作流文件中的 SHA 以及末尾的 `# vX.Y.Z` 注释。
### 4. 更新 SECURITY.md 中的仓库引用
将 `attested-delivery/attested-pipeline-template` 替换为您的组织/仓库(org/repo)。
### 5. 发布
```
git tag v1.0.0
git push origin v1.0.0
```
发布流水线将自动触发。在此处查看:
`https://github.com///actions`
### 6. 验证发布制品
```
gh attestation verify \
--repo / \
--predicate-type "https://slsa.dev/provenance/v1"
```
完整的验证指南请参见 [SECURITY.md](SECURITY.md)。
## 已锁定的 Actions 参考
所有 actions 都锁定到了完整的 40 字符 SHA(于 2026-06-18 解析):
| Action | Tag | SHA |
|---|---|---|
| `actions/checkout` | v4.2.2 | `34e114876b0b11c390a56381ad16ebd13914f8d5` |
| `actions/attest-build-provenance` | v2.4.0 | `e8998f949152b193b063cb0ec769d69d929409be` |
| `actions/attest-sbom` | v2.3.0 | `bd218ad0dbcb3e146bd073d1d9c6d78e08aa8a0b` |
| `anchore/sbom-action` | v0.24.0 | `e22c389904149dbc22b58101806040fa8d37a610` |
| `softprops/action-gh-release` | v2.2.2 | `3bb12739c298aeb8a4eeaf626c5b8d85266b0e65` |
| `actions/upload-artifact` | v4.6.2 | `ea165f8d65b6e75b540449e92b4886f43607fa02` |
| `actions/download-artifact` | v4.3.0 | `d3f86a106a0bac45b974a628896c90dbdf5c8093` |
## 架构说明
### 为什么需要失败阻断(fail-closed)验证?
一个提供证明但不进行验证的构建流水线会给人一种虚假的
安全感。能够在构建后篡改制品的攻击者(例如,被入侵的
制品存储库)将会生成一个其证明
验证会失败的制品。失败阻断的 `verify` 作业会在发布前捕获此问题。
### 为什么需要 SHA 锁定的 action?
像 `actions/checkout@v4` 这样的 tag 是可变的 —— 仓库所有者可以随时将该 tag 移动到
不同的 commit 上。而像
`actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5` 这样的 SHA 是不可变的。
针对 CI/CD 工具的供应链攻击(例如,tj-actions/changed-files、
reviewdog)正是利用了可变的 action 引用。
### 为什么需要 SLSA 来源?
SLSA (Supply-chain Levels for Software Artifacts) 来源证明
以加密方式将制品与生成它的 workflow 运行绑定在一起,
包括源代码 commit SHA、workflow 文件路径和
runner 环境。这可以防止替换攻击,即不同的构建
产生出同名但内容不同的制品。
### 没有 tag 的试运行(dry-run)
```
gh workflow run release.yml
```
这将执行完整的 构建流程 → 证明 → 验证链条。`publish` 作业
会被跳过,因为 `startsWith(github.ref, 'refs/tags/')` 的结果为 false。
## License
Apache-2.0。参见 [LICENSE](LICENSE)。
标签:DevSecOps, GitHub Actions, SBOM, SLSA认证, 上游代理, 硬件无关, 自动笔记