UID2-6764: Add SLSA build provenance attestations to docker publish workflows#228
Open
BehnamMozafari wants to merge 14 commits intomainfrom
Open
UID2-6764: Add SLSA build provenance attestations to docker publish workflows#228BehnamMozafari wants to merge 14 commits intomainfrom
BehnamMozafari wants to merge 14 commits intomainfrom
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Attestation runs after the docker push but before the changelog/release steps. Without continue-on-error, an attest failure leaves a half-finished release: image pushed, no GitHub Release created. Tolerate attest failures during the v3 rollout so consumers aren't stuck mid-release if attestation breaks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This reverts commit 80a5560.
This was referenced May 8, 2026
Contributor
|
Can we do a real smoke test without "Attest build provenance" step being skipped? |
jon8787
reviewed
May 8, 2026
jon8787
reviewed
May 8, 2026
jon8787
reviewed
May 8, 2026
Addresses jon8787's review comments on PR #228: - #2 verify step: attest_image now calls 'gh attestation verify' immediately after signing so misconfigured signatures fail at build time, not consumer pull time. - #3 case sensitivity: lowercase the image ref once and reuse it for both signing and verifying. actions/attest@v4 already lowercases subject-name internally when push-to-registry is true (verified at the pinned commit 59d8942 in src/main.ts and src/subject.ts), but 'gh attestation verify' does NOT lowercase the OCI URI we pass it; doing it ourselves keeps the signed name and the verified URI byte-identical. - #4 NODE_OPTIONS comment: brief comment explaining why we mirror actions/attest-build-provenance's defensive HTTP header bump. - #5 extract: pulled the attest+verify pair into a single composite action so the Java workflow and the non-Java composite action share one implementation. Adds .github/workflows/test-attest-image.yaml: a manually-dispatched smoke test that builds a throwaway image and exercises the full attest+verify path. Use this whenever attest_image or actions/attest@v4 changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop after merge — only here so the smoke test can run before the workflow file lands on main (gh workflow run / API dispatch require the file to exist on the default branch). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github.repository is mixed case; docker rejects mixed-case tags at push time. Compute a lowercased ref once and reuse it for the push tag, the attest_image input, and the independent re-verify command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…st is green Run 25542801315 verified the attest+verify path end-to-end. Reverting to workflow_dispatch only so the test stops auto-firing and remains as an on-demand regression check after merge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run 25542801315 captured the verified attestation evidence on PR #228; keeping the workflow would just push throwaway test images on every manual dispatch. The composite action lives at actions/attest_image and can be re-tested in any future change by re-adding this workflow file ad-hoc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end smoke against private UnifiedID2/uid2-test-source surfaced a real gh CLI limitation: attestations signed by GitHub's internal Sigstore instance (used for private repos) fail verification with 'Error: verifying with issuer "GitHub, Inc."'. Tried --no-public-good, --bundle-from-oci, --cert-oidc-issuer combinations; same result. Signing and upload still succeed (bundle lands in both the attestations API and the OCI registry), so external verifiers remain authoritative. Demote the in-CI verify failure to a warning for private repos only; public repos still hard-fail on verify mismatch as Jon's review #2 intended. Evidence: UnifiedID2/uid2-test-source actions run 25643422322 — full shared-publish-to-docker-versioned.yaml chain green (setup → buildx → vulnerability_scan → push → attest_image sign+upload → shared_create_releases draft), attestation signed for ghcr.io/.../uid2-6764-smoke@sha256:05058e77... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
actions/attest@v4.1.0 sets create-storage-record:true by default, which calls GitHub's artifact-metadata API to cross-link the signed attestation to the build artifact. Without artifact-metadata:write the call returns 403 and the run logs 'Failed to persist storage record'. The storage record itself is independent of the signature/upload chain (those still succeed), but it powers the "Attestations" tab in the GitHub UI and surfaces attestations for org-wide policy/discovery. Required on the callee's job permissions block too; reusable workflows take the intersection of caller and callee permissions. Consumer rollout PRs receive a matching grant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous "private-repo verify caveat" misdiagnosed cli/cli#9045: the failure isn't about the GitHub-internal Sigstore CA, it's that the cert SAN of a reusable-workflow signer doesn't match the default --owner regex. Add --cert-identity-regex to accept either signer pattern (reusable workflow under IABTechLab/uid2-shared-actions, or composite-action caller under ${{ github.repository }}), and remove the warning demotion so verify hard-fails uniformly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds SLSA build-provenance attestation to every non-snapshot image published by the shared docker workflows.
actions/attest_imagewraps the full attest+verify path: it lowercases the image ref once, callsactions/attest@v4.1.0(pinned to59d8942), and runsgh attestation verifyagainst the just-pushed digest. Verify failure is hard on public repos; demoted to a warning on private repos (see "Private-repo verify caveat" below).shared-publish-java-to-docker-versioned.yamlandactions/shared_publish_to_docker/action.yamlnow callattest_image@v3instead of inlining the attest block.id-token: writeandattestations: write.not_snapshotguard.Closes UID2-6764. Spike was UID2-5763.
Review-comment responses
UnifiedID2/uid2-test-source.gh attestation verifystepattest_image. Public repos hard-fail on mismatch; private repos warn-only due to a known gh CLI limitation.subject-nameactions/attest@v4already auto-lowercasessubject-namewhenpush-to-registry: true(verified insrc/main.ts→downcaseName: inputs.pushToRegistry, applied atsrc/subject.tsline 47). Howevergh attestation verifydoes not lowercase the OCI URI we pass it.attest_imagelowercases once and reuses for both. (The first unit-smoke run caught a real case-sensitivity failure when${{ github.repository }}evaluated toIABTechLab/...anddocker pushrejected it — proves the concern.)NODE_OPTIONSattest_image/action.yaml.actions/attest_image/action.yaml.Smoke test evidence
1. Unit smoke (composite action in isolation)
Run 25542801315 —
attest_imageagainst a throwaway alpine image built inline. External verify:2. End-to-end smoke (full shared workflow chain)
Run 25643422322 on
UnifiedID2/uid2-test-source(private), branchrelease-UID2-6764-smoke. Workflowconclusion: success. Exercised in order:shared_publish_setup→ docker login → docker metadata → docker build (withload: true) →actions/vulnerability_scan@v3→ docker push (push: true) →actions/attest_image(lowercase →actions/attest@v4.1.0→ in-CI verify) →actions/shared_create_releases@v2(draft release).Attestation created for
ghcr.io/unifiedid2/uid2-test-source/uid2-6764-smoke@sha256:05058e770dbfc2fe1c8c6a516e2a9b224cd668af5c52f13b87c44f17e2b69e4c, signed by GitHub's internal Sigstore instance, uploaded to both the GitHub attestations API and the OCI registry.Private-repo verify caveat
The end-to-end smoke surfaced a real gh CLI limitation: for attestations signed by GitHub's internal Sigstore instance (the cert chain used for private repos),
gh attestation verifyfails with:Tried
--no-public-good,--bundle-from-oci, and explicit--cert-oidc-issuer; same failure. The attestation itself is genuinely created and uploaded — bothAttestation uploaded to repositoryandAttestation uploaded to registrylog lines fire, and the bundle is fetchable.To keep private consumers (
UnifiedID2/uid2-snowflake,UnifiedID2/uid2-databricks) from hard-failing on every publish,attest_imagedemotes a verify failure to a::warning::whengithub.event.repository.private == true. Public repos (the four IABTechLab/* consumers) still hard-fail on a real verify mismatch, preserving Jon's review-#2 intent. External verifiers remain authoritative for both.Test plan
IABTechLab/uid2-admin(Java path) — run 25421656856 —not_snapshotguard verified.attest_image— run 25542801315 — sign + verify green.v3is promoted; verified digests recorded in UID2-6764.Post-merge sequence
update-major-version-tags.yamlonmainimmediately after — the refactored workflows referenceactions/attest_image@v3, and consumers triggering between merge and tag promotion would fail with "action not found".IABTechLab/uid2-admin:bmz-UID2-6764-test(the snapshot smoke pin branch).Caller-repo follow-up — already opened (one PR each, all open)
Each grants
id-token: write+attestations: write(plus the implicit defaults the publish job already relied on). They're additive and harmless until this PR merges andv3is promoted.SDK images are explicitly out of scope; follow-up ticket to be filed separately.