Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

[SECURITY][CI]: Pin and verify Buildx binary provenance instead of version: latest #6436

Copy link
Copy link

Description

@jonpspri
Issue body actions

Summary

The multi-platform Docker workflow downloads the Buildx CLI at version: latest on every run without verifying its provenance. I propose pinning an exact Buildx release and verifying the official Sigstore bundle produced by docker/github-builder before the binary is executed, with fail-closed behavior.

Current behavior

On main at commit ff1fcc828966e8cf6c1905a4bd189415b6412904, .github/workflows/docker-multiplatform.yml invokes docker/setup-buildx-action (itself SHA-pinned at 8d2750c68a42422c14e847fe6c8ac0403b4cbd6f, v3) but requests version: latest at three call sites:

  • Lines 200-204 (wheel-image build)
  • Lines 342-346 (main build)
  • Lines 459-462 (s390x build)

Permalink:

- name: Set up Docker Buildx
if: steps.gate.outputs.build == 'true'
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
with:
version: latest

The action's SHA pin protects the action's own code, but version: latest means the Buildx binary fetched on each run is whatever release is newest at that moment. A compromised or unintended upstream release would be executed immediately in a job that holds a token capable of pushing to GHCR.

Threat model

  • version: latest introduces a moving supply-chain target: the exact binary running in the release job changes without any review or commit in this repository.
  • A tampered or unexpectedly re-released Buildx binary would run with GHCR push credentials and OIDC identity before any image-level signing occurs.
  • The existing cosign sign --recursive and cosign attest steps (lines 559-581 of the same workflow) protect the Context Forge images we produce, via keyless OIDC signing and an SPDX SBOM predicate. They do not verify the Buildx executable itself; image attestation answers "who built this image and what is in it," not "was the build tool authentic."
  • docker/buildx release assets do not currently expose GitHub Artifact Attestations verifiable via gh attestation verify --repo docker/buildx, so the trust anchor needs to come from Docker's own signing pipeline.

Proposed implementation

  1. Pin an exact Buildx version (for example version: v0.19.3, or whatever release is current and reviewed at adoption time) in all three setup-buildx-action invocations, replacing version: latest.
  2. Verify the official Sigstore provenance bundle published through Docker's docker/github-builder pipeline before the binary is used. Verification should strictly check:
    • The certificate issuer / Fulcio identity chain expected for Docker's official builder.
    • The workflow identity embedded in the signing certificate, bound to the official docker/github-builder build workflow, not to an arbitrary repository or workflow.
    • The subject name and digest in the bundle matching the exact pinned Buildx release asset for each target platform.
  3. Recheck SHA-256 digests of the downloaded binary both before and after the setup step runs, and compare them against the digest bound in the verified bundle, to catch any substitution between verification and execution.
  4. Fail closed: if bundle verification, identity matching, or digest comparison fails for any reason, the workflow must abort. No fallback to an unverified binary and no silent downgrade to latest.

This complements, rather than replaces, the existing Cosign signing and SBOM attestation of the produced images: provenance verification covers the build tool going in, and Cosign covers the artifacts coming out.

Acceptance criteria

  • All docker/setup-buildx-action call sites in .github/workflows/docker-multiplatform.yml specify an exact Buildx release version; version: latest no longer appears in the workflow.
  • A verification step runs before Buildx is first executed in each job, validating the official docker/github-builder Sigstore bundle with strict issuer and workflow-identity checks plus subject/digest binding to the pinned release.
  • SHA-256 of the resolved Buildx binary is rechecked before and after setup and must match the verified bundle subject digest.
  • Any verification failure causes the job to fail; there is no code path that proceeds with an unverified binary.
  • CI demonstrates the negative case: a deliberately wrong digest or wrong workflow identity in a test run causes the expected failure.
  • The existing cosign sign / cosign attest behavior for produced images is unchanged.

Rollout considerations

  • A one-time manual review step is needed to select and record the initial pinned Buildx version and its expected digest.
  • Version bumps become explicit pull requests (Dependabot or a small scheduled workflow can open them), which makes Buildx upgrades reviewable like any other dependency change.
  • The verification logic could live in a small reusable composite action so the three call sites stay consistent and future workflows inherit the same guarantees.
  • If docker/buildx later publishes GitHub Artifact Attestations, the verification step can adopt gh attestation verify as an additional or alternative check without changing the fail-closed contract.

Happy to help draft the verification step or a proof-of-concept composite action if that would be useful.

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.