Skip to content

UID2-6764: Add SLSA build provenance attestations to docker publish workflows#228

Open
BehnamMozafari wants to merge 10 commits intomainfrom
bmz-UID2-6764-artifact-attestation
Open

UID2-6764: Add SLSA build provenance attestations to docker publish workflows#228
BehnamMozafari wants to merge 10 commits intomainfrom
bmz-UID2-6764-artifact-attestation

Conversation

@BehnamMozafari
Copy link
Copy Markdown
Contributor

@BehnamMozafari BehnamMozafari commented May 6, 2026

Summary

Adds SLSA build-provenance attestation to every non-snapshot image published by the shared docker workflows.

  • New composite action actions/attest_image wraps the full attest+verify path: it lowercases the image ref once, calls actions/attest@v4.1.0 (pinned to 59d8942), and immediately runs gh attestation verify against the just-pushed digest.
  • Both shared-publish-java-to-docker-versioned.yaml and actions/shared_publish_to_docker/action.yaml now call attest_image@v3 instead of inlining the attest block.
  • Publish jobs gain id-token: write and attestations: write.
  • Attestation is skipped on snapshot builds via the existing not_snapshot guard.

Closes UID2-6764. Spike was UID2-5763.

Review-comment responses

# Comment Resolution
1 Real smoke test without the attest step skipped Added a temporary test-attest-image.yaml workflow that exercised the full attest+verify path against a throwaway image. Run 25542801315 green; full evidence below. Workflow then deleted in 3954ca4 since the captured evidence is permanent.
2 Add gh attestation verify step Built into attest_image so every release verifies in CI before any consumer pulls.
3 Case sensitivity of subject-name actions/attest@v4 already auto-lowercases subject-name when push-to-registry: true (verified in src/main.tsdowncaseName: inputs.pushToRegistry, applied at src/subject.ts line 47). However, gh attestation verify does not lowercase the OCI URI we pass it. To keep the signed name and the verify URI byte-identical, attest_image lowercases once at the top and reuses the value for both. (The smoke test caught a real case-sensitivity failure in the first run when ${{ github.repository }} evaluated to IABTechLab/... and docker push rejected it — proves the concern is real.)
4 Comment for NODE_OPTIONS Added inline in attest_image/action.yaml: Mirrors actions/attest-build-provenance, prevents oversized OCI registry auth-challenge headers triggering HPE_HEADER_OVERFLOW.
5 Extract duplication into a composite action Done — actions/attest_image/action.yaml is the single implementation.

Smoke test evidence

Run 25542801315 — all 9 steps green in 38s.

Test image: ghcr.io/iabtechlab/uid2-shared-actions/test-attest@sha256:e008cbdd1c67eee898020ad96d56ff0d42d762585ef4c1153479abaf5a4112bb

$ gh attestation verify \
    "oci://ghcr.io/iabtechlab/uid2-shared-actions/test-attest@sha256:e008cbdd1c67eee898020ad96d56ff0d42d762585ef4c1153479abaf5a4112bb" \
    --owner "IABTechLab"
Loaded digest sha256:e008cbdd1c67eee898020ad96d56ff0d42d762585ef4c1153479abaf5a4112bb for oci://ghcr.io/iabtechlab/uid2-shared-actions/test-attest@sha256:e008cbdd1c67eee898020ad96d56ff0d42d762585ef4c1153479abaf5a4112bb
Loaded 1 attestation from GitHub API

The following policy criteria will be enforced:
- Predicate type must match:................ https://slsa.dev/provenance/v1
- Source Repository Owner URI must match:... https://github.com/IABTechLab
- Subject Alternative Name must match regex: (?i)^https://github.com/IABTechLab/
- OIDC Issuer must match:................... https://token.actions.githubusercontent.com

✓ Verification succeeded!

The following 1 attestation matched the policy criteria

- Attestation #1
  - Build repo:..... IABTechLab/uid2-shared-actions
  - Build workflow:. .github/workflows/test-attest-image.yaml@refs/heads/bmz-UID2-6764-artifact-attestation
  - Signer repo:.... IABTechLab/uid2-shared-actions
  - Signer workflow: .github/workflows/test-attest-image.yaml@refs/heads/bmz-UID2-6764-artifact-attestation

End-to-end chain: image digest → SLSA v1 provenance → workflow file at the exact branch ref → GitHub-hosted runner OIDC identity.

Test plan

  • Snapshot smoke test on IABTechLab/uid2-admin (Java path) — run 25421656856 — workflow succeeded, "Attest build provenance" step skipped (proves the not_snapshot guard works).
  • Real attest+verify smoke test — run 25542801315 — full attest+verify path green; external gh attestation verify succeeds (output above).
  • Release-tag E2E on each consumer's first real publish after v3 float is promoted; verified digests will be recorded in the UID2-6764 ticket.

Post-merge sequence

  1. Merge this PR.
  2. Run update-major-version-tags.yaml on main immediately after — the refactored workflows reference actions/attest_image@v3, and the window between merge and tag promotion is a window where consumers triggering would fail with "action not found".
  3. Merge each of the 6 caller-repo follow-up PRs below.
  4. On each consumer's first real publish, capture the verified digest in UID2-6764.
  5. Delete IABTechLab/uid2-admin:bmz-UID2-6764-test (only existed to pin the shared workflow at the feature branch for the snapshot smoke test).

Caller-repo follow-up — already opened (one PR each, all open as of 2026-05-08)

Repo PR
IABTechLab/uid2-operator #2531
IABTechLab/uid2-core #403
IABTechLab/uid2-admin #632
IABTechLab/uid2-optout #402
UnifiedID2/uid2-snowflake #299
UnifiedID2/uid2-databricks #132

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 and v3 is promoted.

SDK images are explicitly out of scope; follow-up ticket to be filed separately.

BehnamMozafari and others added 5 commits May 6, 2026 13:46
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>
@jon8787
Copy link
Copy Markdown
Contributor

jon8787 commented May 8, 2026

Can we do a real smoke test without "Attest build provenance" step being skipped?
e.g. using uid2-test-source or similar?

Comment thread actions/shared_publish_to_docker/action.yaml Outdated
Comment thread .github/workflows/shared-publish-java-to-docker-versioned.yaml Outdated
Comment thread actions/shared_publish_to_docker/action.yaml Outdated
Comment thread actions/shared_publish_to_docker/action.yaml Outdated
BehnamMozafari and others added 5 commits May 8, 2026 17:19
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants