Skip to content

ci(release): generate provenance in an isolated reusable workflow (SLSA L3) - #45

Merged
jmgilman merged 1 commit into
masterfrom
ci/slsa-l3-provenance
Jun 28, 2026
Merged

jmgilman merged 1 commit into
masterfrom
ci/slsa-l3-provenance

Conversation

@jmgilman

Copy link
Copy Markdown
Contributor

Summary

Moves artifact provenance out of the build jobs into a reusable workflow
(.github/workflows/attest.yml) so the signing OIDC identity is unreachable by
the build steps — the SLSA Build L3 requirement. Provenance still lives on
GitHub's attestation API (gh attestation verify keeps working); the
signer-workflow is now attest.yml. This is PR3 of the template-go-api
session-015 reproduction
(mise → melange/apko → SLSA L3 → skills/docs).

  • attest.yml (new, workflow_call): one isolated attest job — binary
    checksums (actions/attest --subject-checksums) + OCI provenance
    (actions/attest-build-provenance@v4.1.1, for both the image and the chart).
    Carries its own GHCR login (the build job's login doesn't cross the
    reusable-workflow boundary).
  • release.yml: drop the 3 inline actions/attest provenance steps; add
    attest-binaries / attest-image / attest-chart callers (Option A — the
    Helm chart provenance is isolated too). Keyless cosign + the syft image
    SBOM
    attestation stay inline (separate controls). Every caller grants
    packages: write (a reusable workflow can't request more than its caller).
  • Kyverno signer flip: values.yaml attestor.subjectRegExp
    release.yml → attest.yml, in lockstep with where provenance is now signed.
    attest-build-provenance emits the same slsa.dev/provenance/v1 predicate +
    buildtypes/workflow/v1 buildType the policy already requires, so only the
    signer identity changes
    .
  • Summary: gh attestation verify → attest.yml for binaries/image/chart;
    cosign verify stays release.yml. DELETE_ME documents the coupling.

Testing

  • moon run root:check green; helm template --set kyverno.imageVerification.enabled=true
    now renders the policy trusting attest.yml with the unchanged
    type/buildType.
  • Ground truth: the live v0.1.2 image attestation carries
    slsa.dev/provenance/v1 + buildtypes/workflow/v1 — exactly what
    attest-build-provenance produces, so the Kyverno predicate contract is
    preserved.
  • A 3-agent adversarial review (reusable-workflow permissions/contexts, Kyverno
    signer/predicate alignment, job-graph completeness) returned zero findings.
  • attest.yml is unreachable by the dry-run (which never attests); only a real
    release tag exercises it, so a throwaway-tag rehearsal is the final check.

🤖 Generated with Claude Code

…SA L3)

Move artifact provenance out of the build jobs into a reusable workflow
(.github/workflows/attest.yml). A reusable workflow runs with its own OIDC
identity that the calling build steps cannot reach, which is the SLSA Build L3
requirement; in-job attestations are only L2. Provenance still lives on GitHub's
attestation API (gh attestation verify keeps working) — the signer-workflow is
now attest.yml.

- attest.yml (new, workflow_call): one isolated `attest` job that does binary
  checksums (actions/attest --subject-checksums) and OCI provenance
  (actions/attest-build-provenance, used for both the image and the chart).
- release.yml: drop the three inline `actions/attest` provenance steps; add
  attest-binaries / attest-image / attest-chart caller jobs (the user's Option A:
  the Helm chart provenance is isolated too). binary-release-assets now uploads
  checksums.txt as an artifact for the binary caller; keyless cosign + the syft
  image SBOM attestation stay inline (separate controls, not the SLSA provenance).
- Every caller grants packages: write — a reusable workflow cannot request more
  than its caller, and the shared attest job declares it for the image/chart
  push-to-registry; attest.yml carries its own GHCR login since the build job's
  login does not cross the reusable-workflow boundary.
- charts/template-k8s/values.yaml: flip the Kyverno trusted signer
  attestor.subjectRegExp from release.yml to attest.yml, in lockstep with where
  provenance is now signed. attest-build-provenance emits the same
  slsa.dev/provenance/v1 predicate + actions/buildtypes/workflow/v1 buildType the
  policy already requires, so only the signer identity changes. The chart-render
  test (test/chart/rbac_test.go) is updated to expect the attest.yml signer.
- Release inspection summary: gh attestation verify now points at attest.yml for
  binaries/image/chart; cosign verify stays release.yml (cosign signer unchanged).
- DELETE_ME: document attest.yml as the provenance signer and the Kyverno coupling.

The attest.yml path is unreachable by the dry-run (which never attests) — only a
real release tag exercises it, so a throwaway-tag rehearsal is the final check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jmgilman
jmgilman force-pushed the ci/slsa-l3-provenance branch from 41184f1 to f8b2696 Compare June 28, 2026 22:02
@jmgilman
jmgilman merged commit d26d07f into master Jun 28, 2026
6 checks passed
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.

1 participant