DOMAIN:SUPPLY_CHAIN:PROVENANCE¶
OWNER: julian, pol
ALSO_USED_BY: koen, victoria, ashley
UPDATED: 2026-03-26
SCOPE: build provenance, artifact signing, and verification for all projects
OVERVIEW¶
Build provenance answers: who built this, from what source, on what platform, with what inputs?
Without provenance, any artifact could be tampered with between build and deployment.
SLSA framework provides maturity levels for provenance trustworthiness.
Sigstore/cosign provide the practical signing and verification toolchain.
EU CRA will require vulnerability handling and traceability from Sep 2026.
Provenance is the mechanism that makes traceability possible.
SLSA FRAMEWORK (Supply-chain Levels for Software Artifacts)¶
CURRENT VERSION: v1.1 (2025)¶
SLSA defines progressive levels of build integrity assurance.
Each level builds on the previous one.
LEVEL 0 — NO SLSA¶
NO provenance exists.
BUILD on local developer machine.
NO verification possible.
ACCEPTABLE_FOR: local development only — never for production artifacts.
LEVEL 1 — PROVENANCE EXISTS¶
BUILD process documented.
PROVENANCE generated automatically describing how artifact was built.
PROVENANCE available to consumers.
REQUIREMENTS:
- build runs on defined platform (CI/CD)
- provenance generated automatically (not manually written)
- provenance includes: source repo, commit, builder, build timestamp
ACHIEVABLE_WITH: any CI/CD system + SBOM generation.
EFFORT: minimal — add provenance generation step to pipeline.
LEVEL 2 — HOSTED BUILD + SIGNED PROVENANCE¶
BUILD on hosted platform (not developer machine).
PROVENANCE signed (tampering detectable).
SIGNING provides evidence of which build service produced the artifact.
REQUIREMENTS (L1 plus):
- hosted build service (GitHub Actions, GitLab CI, Jenkins on managed infra)
- provenance cryptographically signed
- signature verifiable by consumers
ACHIEVABLE_WITH: GitHub Actions + Sigstore cosign.
EFFORT: moderate — add signing step to pipeline.
LEVEL 3 — HARDENED BUILD PLATFORM¶
BUILD platform hardened against insider threats.
PROVENANCE non-falsifiable (even compromised build admin cannot forge).
BUILD runs in isolated environment.
REQUIREMENTS (L2 plus):
- isolated build environments (ephemeral, fresh per build)
- non-falsifiable provenance (signed by platform, not user)
- build platform access controls (admins cannot modify build output)
- audit trail for build platform configuration changes
ACHIEVABLE_WITH: Tekton Chains + Sigstore, GitHub Actions SLSA generator.
EFFORT: significant — requires hardened CI infrastructure.
LEVEL 4 — TWO-PERSON REVIEW + HERMETIC BUILD¶
BUILD is hermetic (all inputs declared, no network access during build).
SOURCE requires two-person review before build.
PROTECTS against compromised individual developer.
REQUIREMENTS (L3 plus):
- two-person code review enforced before merge
- hermetic build (no network access, all dependencies pre-fetched)
- reproducible build (same inputs always produce same output)
ACHIEVABLE_WITH: Bazel/Nix + strict branch protection + Tekton.
EFFORT: very high — significant infrastructure investment.
GE TARGET¶
IMMEDIATE: SLSA Level 2 for all production artifacts.
12_MONTH: SLSA Level 3 for client-facing deployments.
LEVEL_4: not currently justified — revisit when enterprise clients require it.
CONTAINER IMAGE SIGNING (cosign / Sigstore)¶
WHAT IS SIGSTORE¶
Sigstore is an open-source project providing keyless signing infrastructure.
"Keyless" = no long-lived private keys to manage.
Uses short-lived certificates tied to OIDC identity (GitHub, Google, etc.).
COMPONENTS:
- FULCIO: certificate authority — issues short-lived signing certificates
- REKOR: transparency log — append-only record of all signatures
- COSIGN: CLI tool — signs and verifies container images, SBOMs, attestations
HOW COSIGN WORKS¶
- developer/CI triggers cosign sign
- cosign requests certificate from Fulcio (proves identity via OIDC)
- Fulcio issues short-lived certificate (valid minutes)
- cosign signs artifact with certificate
- signature recorded in Rekor transparency log
- signature stored in OCI registry alongside image (same repo, different tag)
SIGNING CONTAINER IMAGES¶
# Sign an image (keyless — uses OIDC identity from CI)
cosign sign ghcr.io/ge-bootstrap/admin-ui:v1.2.3
# Sign with key (if keyless not available)
cosign generate-key-pair
cosign sign --key cosign.key ghcr.io/ge-bootstrap/admin-ui:v1.2.3
# Attach SBOM to image
cosign attach sbom --sbom sbom.cyclonedx.json ghcr.io/ge-bootstrap/admin-ui:v1.2.3
# Sign the attached SBOM
cosign sign --attachment sbom ghcr.io/ge-bootstrap/admin-ui:v1.2.3
VERIFYING CONTAINER IMAGES¶
# Verify keyless signature (checks Rekor + Fulcio)
cosign verify ghcr.io/ge-bootstrap/admin-ui:v1.2.3 \
--certificate-identity=workflow@github.com \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
# Verify with key
cosign verify --key cosign.pub ghcr.io/ge-bootstrap/admin-ui:v1.2.3
CI/CD INTEGRATION¶
GITHUB_ACTIONS: sigstore/cosign-installer action + keyless signing via OIDC
GITLAB_CI: cosign binary + OIDC token from GitLab
LOCAL_K3S: key-based signing (no OIDC provider available for local builds)
ARTIFACT ATTESTATION¶
IN-TOTO ATTESTATION FORMAT¶
STANDARD: in-toto — framework for software supply chain integrity
ATTESTATION: signed statement about an artifact (who built it, how, from what)
FORMAT: JSON envelope with type, subject (artifact hash), and predicate (claims)
SLSA PROVENANCE ATTESTATION¶
PREDICATE_TYPE: https://slsa.dev/provenance/v1
CONTENTS:
- buildDefinition: build type, external parameters (source repo, commit)
- runDetails: builder identity, build metadata, timestamps
- materials: all inputs (source, dependencies)
GITHUB ATTESTATIONS (native)¶
GitHub now provides built-in attestation support:
- uses: actions/attest-build-provenance@v2
with:
subject-name: ghcr.io/ge-bootstrap/admin-ui
subject-digest: sha256:abc123...
STORES: in GitHub's attestation store, verifiable via
gh attestation verify.
CUSTOM ATTESTATIONS¶
For claims beyond provenance (vulnerability scan results, licence compliance):
cosign attest --predicate scan-results.json \
--type https://ge.nl/attestation/vuln-scan/v1 \
ghcr.io/ge-bootstrap/admin-ui:v1.2.3
REPRODUCIBLE BUILDS¶
WHAT¶
Same source code + same build environment = identical binary output, every time.
Allows independent verification that a binary was built from claimed source.
WHY¶
WITHOUT reproducibility: must trust build infrastructure.
WITH reproducibility: anyone can verify binary matches source.
REQUIRED for SLSA Level 4, valuable at any level.
CHALLENGES¶
NON-DETERMINISM_SOURCES:
- timestamps embedded in artifacts (build date in binary)
- random values (UUIDs, nonces generated at build time)
- filesystem ordering (directory listing order varies)
- build path differences (absolute paths in debug info)
- floating dependency versions (even with lock file — registry availability)
PRACTICAL STEPS¶
CHECK: pin all dependency versions (lock files committed)
CHECK: pin base image by digest (not tag)
CHECK: use SOURCE_DATE_EPOCH for timestamps
CHECK: avoid embedding build-time metadata (or make it deterministic)
CHECK: use deterministic build tools (Bazel, Nix, ko for Go)
CHECK: document build environment precisely (Dockerfile = build env definition)
GE STATUS¶
FULL_REPRODUCIBILITY: not yet achieved — significant effort.
PRACTICAL_APPROACH: pin dependencies, pin base images by digest, use Docker multi-stage builds.
GOAL: bit-for-bit reproducibility for critical client deliverables by 2027 (CRA driver).
PROVENANCE VERIFICATION IN CI/CD¶
DEPLOY-TIME VERIFICATION¶
Before deploying any artifact to production:
# Verify image signature
cosign verify --certificate-identity=... --certificate-oidc-issuer=... $IMAGE
# Verify SLSA provenance
cosign verify-attestation --type slsaprovenance $IMAGE
# Verify SBOM attestation
cosign verify-attestation --type cyclonedx $IMAGE
KUBERNETES ADMISSION CONTROL¶
TOOL: Kyverno or OPA/Gatekeeper
PURPOSE: enforce that only signed, attested images can deploy to cluster
KYVERNO_POLICY_EXAMPLE:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
rules:
- name: verify-signature
match:
resources:
kinds: [Pod]
verifyImages:
- imageReferences: ["ghcr.io/ge-bootstrap/*"]
attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: workflow@github.com
VERIFICATION CHAIN¶
EVERY link in chain must be verifiable.
BREAK any link = cannot trust the artifact.
GhostAction ATTACK (2025) — LESSONS¶
WHAT HAPPENED¶
Attackers compromised widely-used third-party GitHub Action.
Modified workflow code to exfiltrate CI/CD secrets.
Action was pinned by tag (@v1) not commit SHA.
Every repository using @v1 automatically pulled compromised version.
LESSONS¶
RULE: pin GitHub Actions by commit SHA, not tag.
# BAD — tag can be moved to malicious commit
- uses: actions/checkout@v4
# GOOD — pinned to specific commit
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
TOOL: Renovate/Dependabot can manage SHA pinning and update PRs.
VERIFY: action provenance before adopting new Actions.
ADOPTION ROADMAP¶
PHASE 1 (immediate) — SLSA Level 1¶
- [ ] generate SBOM at every build (syft/cdxgen)
- [ ] store SBOM alongside build artifacts
- [ ] document build process in CI config
PHASE 2 (3 months) — SLSA Level 2¶
- [ ] sign container images with cosign (keyless in CI, key-based for local k3s)
- [ ] sign SBOM attestations
- [ ] generate SLSA provenance attestation
- [ ] pin GitHub Actions by SHA
PHASE 3 (6 months) — SLSA Level 3¶
- [ ] ephemeral build environments (fresh runners per build)
- [ ] deploy-time signature verification (Kyverno or equivalent)
- [ ] non-falsifiable provenance (GitHub/GitLab native attestation)
READ_ALSO: domains/supply-chain/index.md, domains/supply-chain/sbom.md, domains/supply-chain/vulnerability-management.md