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
- 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.
- 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.
- 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.
- 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
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.
Summary
The multi-platform Docker workflow downloads the Buildx CLI at
version: lateston every run without verifying its provenance. I propose pinning an exact Buildx release and verifying the official Sigstore bundle produced bydocker/github-builderbefore the binary is executed, with fail-closed behavior.Current behavior
On
mainat commitff1fcc828966e8cf6c1905a4bd189415b6412904,.github/workflows/docker-multiplatform.ymlinvokesdocker/setup-buildx-action(itself SHA-pinned at8d2750c68a42422c14e847fe6c8ac0403b4cbd6f, v3) but requestsversion: latestat three call sites:Permalink:
mcp-context-forge/.github/workflows/docker-multiplatform.yml
Lines 342 to 346 in ff1fcc8
The action's SHA pin protects the action's own code, but
version: latestmeans 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: latestintroduces a moving supply-chain target: the exact binary running in the release job changes without any review or commit in this repository.cosign sign --recursiveandcosign atteststeps (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/buildxrelease assets do not currently expose GitHub Artifact Attestations verifiable viagh attestation verify --repo docker/buildx, so the trust anchor needs to come from Docker's own signing pipeline.Proposed implementation
version: v0.19.3, or whatever release is current and reviewed at adoption time) in all threesetup-buildx-actioninvocations, replacingversion: latest.docker/github-builderpipeline before the binary is used. Verification should strictly check:docker/github-builderbuild workflow, not to an arbitrary repository or workflow.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
docker/setup-buildx-actioncall sites in.github/workflows/docker-multiplatform.ymlspecify an exact Buildx release version;version: latestno longer appears in the workflow.docker/github-builderSigstore bundle with strict issuer and workflow-identity checks plus subject/digest binding to the pinned release.cosign sign/cosign attestbehavior for produced images is unchanged.Rollout considerations
docker/buildxlater publishes GitHub Artifact Attestations, the verification step can adoptgh attestation verifyas 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.