登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  业界新闻

GitHub Actions artifact attestations 的构建来源核对方法

来源:17golang原创

时间:2026-10-26 11:45:15 474浏览 收藏

发布二进制或容器镜像时,标签和校验和只能回答“拿到的文件是不是这一份”,不能完整说明“它由哪个仓库、哪条工作流产生”。GitHub Actions 的 artifact attestations 用签名证明把构建产物和来源信息绑在一起,再用 GitHub CLI 按仓库、签名工作流和来源提交核对。

官方地址:https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations

要点速览
  • attestation 主要证明产物与来源仓库、工作流和提交的关系,不等于完整记录 runner 镜像。
  • 生成证明至少要给工作流写入 OIDC 和 attestation 权限,动作放在产物生成之后。
  • 在线核对优先用 API 获取证明;离线场景要同时保存 bundle 和 trusted root,并重新审视仓库改名边界。

先划清 attestation 能证明什么

我第一次把它接进发布流水线时,最容易混淆的是“来源证明”和“执行环境证明”。证明里有产物摘要、来源仓库、提交、签名工作流等信息,适合回答“这个文件是不是由指定来源构建出来的”。但社区讨论也指出,runner 名称、runner 组和具体镜像并不天然等价于这条来源链;不要把一次验证通过写成“所有构建环境都可信”。

核对对象适合回答不要过度推断
Repository产物绑定到哪个仓库仓库当前名称一定没有改过
Signer workflow哪条工作流签了证明工作流内部每一步都不可变
Source ref来源分支或提交范围runner 镜像和硬件配置
Runner environment部分环境属性完整机器审计记录
GitHub Actions artifact attestation 将构建产物、OIDC 身份、证明包和来源仓库连接起来的静态结构说明图
图1:artifact attestation 构建来源链路的原创结构说明图,不是 GitHub 页面截图。

在构建工作流中生成来源证明

先让构建步骤生成文件,再在同一个 job 的后面调用 actions/attest。权限要放在工作流或 job 的可见配置里,避免用过大的默认权限掩盖实际依赖。

permissions:
  # 让 attestation 动作向 Sigstore 请求短期签名身份
  id-token: write
  # 允许把证明保存到 GitHub 的 attestation 存储
  attestations: write
  # 只有需要关联产物存储记录时才保留这项权限
  artifact-metadata: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build release binary
        run: make release
      - name: Create build provenance
        uses: actions/attest@v4
        with:
          # 绑定刚刚生成的文件,而不是随意绑定工作区目录
          subject-path: dist/my-app

这里的关键不是把 YAML 粘贴得越复杂越好,而是确保 subject-path 指向最终发布物。若发布的是容器镜像,应按官方动作的容器模式配置并确认镜像已经推送到 registry;文件产物则优先使用明确的路径。新接入时先把权限和产物路径写成最小闭环,再考虑 SBOM 或自定义 predicate。

用 GitHub CLI 按身份核对产物

验证时至少给出仓库或组织范围。对生产发布,我更愿意把签名工作流也锁定,因为“来自这个仓库”比“来自这条受控工作流”宽得多。必要时再加来源提交,形成可审查的三层约束。

# 用仓库、签名工作流和来源提交收窄验证范围
gh attestation verify dist/my-app \
  --repo example/acme-app \
  --signer-workflow example/acme-app/.github/workflows/release.yml \
  --source-ref refs/tags/v1.4.0

# 需要交给门禁程序判断时,输出结构化结果
gh attestation verify dist/my-app \
  --repo example/acme-app \
  --format json \
  --jq '.[].verificationResult.signature.certificate.sourceRepositoryIdentifier'

默认 predicate 是 SLSA provenance;如果系统使用的是 SBOM 或自定义声明,要显式指定 --predicate-type,否则验证命令和团队理解可能不一致。若采用 reusable workflow,真正签名的是可复用工作流,检查项应随之改用 --signer-workflow 或 --signer-repo。

GitHub Actions artifact attestation 在线 API 与离线 bundle 验证边界的静态关系说明图
图2:在线与离线验证边界的原创关系说明图,不是 CLI 运行截图。

离线环境保存 bundle 与可信根

隔离环境不能直接访问 GitHub API 时,在线机器先下载 attestation bundle,再把可信根材料一并带入离线区。两个文件缺一不可:bundle 是针对产物的证明,trusted root 用来验证签名链。

# 在线机器:按仓库下载产物对应的证明包
gh attestation download dist/my-app --repo example/acme-app

# 在线机器:导出验证签名链需要的可信根材料
gh attestation trusted-root > trusted_root.jsonl

# 离线机器:指定本地 bundle 和 trusted root 进行验证
gh attestation verify dist/my-app \
  --repo example/acme-app \
  --bundle PATH/TO/BUNDLE.jsonl \
  --custom-trusted-root trusted_root.jsonl

离线核对有一个容易漏掉的边界:--repo 会和证明证书里记录的仓库名称比较,但它不能自动判断仓库后来是否改名,也不能阻止另一个仓库复用旧名称。对高价值发布物,还应在线取得仓库 ID,在离线 JSON 结果中比对 sourceRepositoryIdentifier。

把核对动作放进发布门禁

可以把下面这张清单放进 release job 的最后一步:

  • 产物路径是否固定,摘要是否对应最终上传的文件或镜像。
  • 权限是否只包含本次证明所需的 id-token、attestations 和必要的元数据权限。
  • 验证是否至少限定仓库,生产环境是否继续限定签名工作流和来源 ref。
  • 离线交付是否把 bundle、trusted root 和核对结果一起归档。
  • 是否把 provenance 与 runner 镜像、硬件和临时环境的证据分开管理。

这样做的结果是:attestation 负责“产物从哪里来”,发布门禁负责“哪些来源可以进入下一阶段”,而 runner 和构建环境仍由独立的执行记录补足。职责拆开后,验证失败时更容易判断是产物不匹配、签名身份不对,还是门禁策略过窄。

相关问题

只有仓库参数够不够?

低风险内部构建可以先用 --repo。正式发布建议继续限制签名工作流,并在需要时限制来源 ref,避免同一仓库里其他工作流也能满足条件。

为什么验证通过仍不能说明 runner 镜像可信?

来源证明关注产物、仓库、提交和签名身份;具体 runner 镜像、组和机器配置属于另一类执行环境证据,不能由一次 provenance 验证自动替代。

仓库改名后离线验证要怎么做?

不要只看旧仓库名称。在线查询当前仓库 ID,离线使用 JSON 输出读取证书中的来源仓库 ID,再把两个 ID 做比对。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>