After SolarWinds, Codecov and log4shell it became clear that verifying the artifact at the end of the pipeline is no longer enough. An SBOM tells you what is inside the image. A Cosign signature proves that whoever signed it held a valid OIDC token. Between them sits the gap supply-chain attacks slip through: a swapped builder, a tampered workflow, the wrong commit. SLSA closes that gap with provenance — a machine-verifiable description of how exactly the artifact was built.
What each level promises
SLSA v1.0 (maintained by OpenSSF) defines four levels. L0 means no guarantees — the default state. L1 requires a documented build process and generated provenance even without a signature: enough to catch honest mistakes and quick swaps. L2 raises the bar to a hosted, isolated builder with signed provenance — closing tampering between build and publish. L3 is a hardened ephemeral builder with strong provenance: the threat model now includes a compromised builder and an insider with CI access.
L3 is almost out-of-the-box on GitHub Actions via slsa-framework/slsa-github-generator (reusable workflow). GitLab is still partial L2. A self-hosted builder labelled «we run L2» without actual isolation is the industry's most common lie, and SLSA refuses to validate it.
What sits inside provenance
Provenance is an in-toto Statement with predicate slsa.dev/provenance/v1. Inside: subject — the image and its digest; buildDefinition.externalParameters — repository, ref, workflow path; buildDefinition.resolvedDependencies — the git commit; runDetails.builder.id — the builder identifier; runDetails.metadata — invocation ID and timestamps.
Those fields answer four questions: what was built, from where, with what, and when. A signature without provenance only confirms that «someone with a valid token signed something». Provenance without a signature is JSON without proof. They work together or not at all.
Sigstore: signing without long-lived keys
Cosign in keyless mode (GA since 2.0+) removes the key-management problem. Identity comes from OIDC: GitHub Actions, Google, your Keycloak in air-gapped. Fulcio issues a certificate that lives 10 minutes, Cosign signs the artifact with it, and the entry lands in Rekor — a public append-only transparency log.
One syntax covers every OCI artifact:
cosign sign --yes ghcr.io/org/payments-api:v1.2.3
cosign attest --predicate provenance.json --type slsaprovenance ghcr.io/org/payments-api:v1.2.3
cosign attest --predicate sbom.cdx.json --type cyclonedx ghcr.io/org/payments-api:v1.2.3
With Helm 4 and OCI as the primary distribution channel, the same cosign sign applies to chart artifacts. One transparency log, one verification model, one admission rule.
Closing the loop at admission
Signing without verifying is security theater — mentioned in every SLSA document and regularly found in production. Verification has to live in the admission controller; otherwise any image rolls into the cluster bypassing the gate.
A Kyverno ClusterPolicy with verifyImages accepts a keyless attestor: a regex on subject (workflow path), the OIDC issuer, the Rekor URL. A Pod whose image fails verification is not created. On the CLI side slsa-verifier does the same:
slsa-verifier verify-image ghcr.io/org/payments-api:v1.2.3 \
--source-uri github.com/org/payments-api \
--source-tag v1.2.3 \
--builder-id 'https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_container_slsa3.yml@refs/tags/v2.0.0'
Drop --builder-id and the chain breaks: the signature is valid, but who built it and where is unknown.
Anti-patterns that show up more often than they should
One key shared between dev/staging/prod erases blast-radius isolation. A hardcoded signer email in policy does not scale to a team. latest in verifyImages.imageReferences points at a mutable target — the signature becomes meaningless. Provenance with empty internalParameters loses the invocation context. A disabled Rekor (--rekor-url="") removes detection: a stolen OIDC token slips through silently.
The K8s-native alternative
Tekton Chains is an observer for TaskRun: it generates SLSA provenance automatically, signs through Sigstore keyless, pushes the attestation next to the image in OCI and the entry to Rekor. The result is SLSA L3 without leaving Kubernetes. The price is Tekton itself — a pipeline engine with its own set of CRDs (Tasks, Pipelines, Triggers) and non-trivial operational overhead. Fit for platform-engineering teams that have already committed to K8s-first CI.
Three answers to three questions
An SBOM answers «what is inside the artifact». Provenance — «how it was built». A Cosign signature — «who vouches for it». A supply-chain attack knocks out one of those three answers; SLSA is designed so that the admission controller gets to ask all three before a Pod is created.