Secure Software Supply Chain: SBOM and Provenance
The SolarWinds breach didn’t happen because attackers broke through a firewall or exploited a production server. They compromised the build system. Malicious code was compiled, packaged, and signed by SolarWinds’ own infrastructure, then distributed to 18,000 customers as a legitimate software update. Every vulnerability scanner gave it a clean bill of health because the malware was baked in at compile time by a trusted build process.
Contamination injected in the kitchen. By someone with access. The food inspector checked the finished product. Found nothing wrong. The contamination happened before inspection.
The scanners did exactly what they were designed to do. They just weren’t designed for this.
- SolarWinds wasn’t a runtime exploit. It was a build system compromise. The malware was compiled and signed by trusted infrastructure. Every scanner said clean. The kitchen contaminated the food. The inspector passed it.
- Artifact provenance (SLSA) proves who built what, from which source, on which runner. Without it, you’re operating on trust. Trust is what supply chain attackers exploit.
- SBOM makes dependency risk visible. The ingredients list. You can’t patch what you can’t inventory. Automate SBOM generation in every build.
- Dependency pinning with hash verification prevents silent substitution. A compromised package registry can serve modified versions. Tampered ingredients with the same label. Pin to exact versions with integrity hashes.
- Admission controllers reject unsigned or unattested images at deploy time. The final inspection gate. Nothing unsigned runs in production.
Supply chain attackers don’t break your security. They become it.
The Build System Is the Attack Surface
The Sonatype 2024 State of the Software Supply Chain report documented over 245,000 malicious packages identified across major registries in a single year. The attack surface has shifted from production infrastructure to the software factory itself. Not the restaurant. The supply chain feeding it.
Most engineering teams trust their CI/CD servers without question. Pipeline says green, ops deploys. Who questions it? Almost nobody. And that implicit trust is the exact architectural gap attackers have figured out how to exploit. The kitchen staff you never background-checked. If an attacker compromises your build runner, they don’t need to steal data directly. They inject a backdoor during compilation. Your own pipeline packages that backdoor, signs it with your corporate certificates, and deploys it to every environment.
Five distinct injection points sit between a developer’s commit and production. Five places the ingredients can be tampered with. Traditional security focuses on the endpoints. Supply chain security focuses on the pipeline itself.
Cryptographic Provenance: Proving What You Built
Every artifact your pipeline produces needs cryptographic provenance. The chain-of-custody label. No exceptions, no “we’ll add it later.”
Provenance is a signed attestation proving: this specific source commit (by SHA), compiled by this specific trusted runner (identified by workload identity), using these exact dependency versions (per the SBOM), produced this binary with this specific content hash. The label that says which farm grew the ingredients, which kitchen prepared the food, which recipe was followed. If a deployed container can’t present that provenance chain backed by a valid digital signature, your infrastructure should refuse to run it.
Sigstore’s cosign makes this practical. It adds under 5 seconds per artifact to sign the image and attach the attestation. The signing key can be ephemeral (keyless signing via Fulcio), which removes the key management overhead that historically made artifact signing too painful for teams to bother with. SLSA provides the maturity framework for measuring how far your provenance guarantees extend.
- Build pipeline runs on a managed CI service with workload identity (not shared credentials)
- Source commits are GPG-signed or verified through branch protection rules
- Dependency lockfile exists and is committed to the repository
- Container registry supports OCI artifact attestations
- Kubernetes cluster (or equivalent) runs an admission controller able to verify signatures
SLSA Levels: A Practical Maturity Ladder
SLSA gives you a concrete target for hardening your supply chain step by step. Food safety certification levels. Most organizations start at Level 0 (no provenance at all) and can reach Level 2 within a single quarter of focused platform work.
| SLSA Level | Provenance Guarantee | What It Requires | Effort |
|---|---|---|---|
| Level 0 | None. No provenance metadata. You trust the artifact exists | Nothing. This is the default | Zero |
| Level 1 | Build process documented. Provenance metadata exists but unsigned | Automated build + provenance generation | Low. Add provenance to existing CI |
| Level 2 | Provenance signed by hosted build service. Tamper-evident | Hosted CI/CD (GitHub Actions, Cloud Build). Signed provenance | Medium. Migrate to hosted builders if not already |
| Level 3 | Hardened build platform. Provenance non-forgeable. Hermetic builds | Isolated build environment, two-person review, reproducible builds | High. Requires build infrastructure investment |
Most teams should target Level 2. Level 3 is for high-value targets (package registries, critical infrastructure, financial systems).
The jump from Level 0 to Level 2 is the highest-ROI investment. From no label to a certified chain-of-custody. It covers the most common attack vectors (compromised source, unsigned artifacts) and fits cleanly with GitHub Actions, GitLab CI, and most managed CI services. Level 3 requires ephemeral runners. A kitchen that’s built, used once, and demolished. More work, but it closes the SolarWinds-class attack vector where the build system itself is compromised.
Don’t: Treat vulnerability scanning as your supply chain security strategy. Scanners check for known-bad patterns in completed artifacts. The food inspector checking the finished dish. They can’t detect backdoors inserted by compromised maintainers or build systems. SolarWinds passed every scanner.
Do: Layer provenance attestation on top of scanning. Sign every artifact. Verify signatures at deploy time. Scanning catches known CVEs. Provenance catches build-time compromise. The inspector and the chain-of-custody label. You need both.
Enforcing Immutable Artifacts
Once an artifact is built and signed, it’s immutable. The tamper-evident seal. The container image tested in staging must be the exact same bytes promoted to production. No recompilation. No rebuilding for different environments.
A surprising number of teams still rebuild for production because “the staging config is different.” The moment you recompile, you’ve created an entirely unverified artifact. It passed no tests. Has no provenance. Repackaging the food in a different box. Nobody tested the new box. Yet you’re deploying it as if it passed everything. Environment configuration belongs in runtime config (environment variables, config maps, secrets), never compiled into the artifact.
The enforcement mechanism: your deployment pipeline verifies the content digest (SHA256) of the container image against the signed attestation before allowing promotion. Checking the seal before accepting delivery. If the digest doesn’t match, the deployment is blocked. Kyverno or OPA Gatekeeper in Kubernetes can enforce this as an admission policy. Your continuous integration and delivery pipeline should treat an unsigned or digest-mismatched artifact exactly the same as a failed test.
Kyverno policy for verifying image signatures and provenance
# Rejects any pod with an unsigned or unattested container image.
# Requires cosign signatures attached during CI.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-provenance
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- entries:
- keyless:
issuer: "https://token.actions.githubusercontent.com"
subject: "https://github.com/your-org/*"
attestations:
- predicateType: "https://slsa.dev/provenance/v1"
conditions:
- all:
- key: "{{ builder.id }}"
operator: Equals
value: "https://github.com/your-org/your-repo"
Governing the Dependency Graph
Modern production software runs on layers of open-source dependencies. Hundreds to thousands of transitive packages. Hundreds of ingredient suppliers, each with their own suppliers. Every one of those transitive authors has, in a sense, write access to your production environment.
SBOM makes the full dependency tree visible. The ingredients list. But visibility without enforcement is a dashboard nobody looks at until something explodes. Build a gate, not a dashboard. Dashboards inform. Gates prevent. The ingredients list that actually blocks delivery when something is wrong.
Your policy engine should read the SBOM and fail the build when it finds packages from known malicious publishers, unmaintained projects (no commits in 2+ years), dependencies with Critical CVEs where a patch exists, or packages flagged by your security team. Expired ingredients. Recalled suppliers. This dependency governance fits directly into the platform engineering toolchain so enforcement runs on every build with no manual work.
| When SBOM enforcement works | When it doesn’t |
|---|---|
| Automated in CI, zero developer friction | Manual SBOM review processes |
| Policy gates block builds with violations | Dashboards report but don’t stop deployment |
| Transitive dependencies included in the scan | Only direct dependencies inventoried |
| Hash verification alongside version pinning | Version pinning without integrity checks |
| Exceptions need documented justification with expiry | Blanket exceptions that never get revisited |
Building the Paved Road
Every control in this article can be a manual checklist. And every manual checklist gets skipped under deadline pressure. Not a discipline problem. A systems design problem. Food safety checklists that nobody fills out when the lunch rush hits.
The only reliable approach: make these capabilities automatic defaults. Developer pushes code. Pipeline signs the commit, generates the SBOM, evaluates dependency policy, compiles in an ephemeral environment, produces a signed attestation, blocks promotion if anything fails. The developer doesn’t think about supply chain security. Doesn’t have to. The safety built into the kitchen equipment. The security practices that actually survive contact with deadline pressure are the ones built into the DevOps pipeline as invisible infrastructure.
| Control | Effort to Build | Developer Friction | Supply Chain Coverage |
|---|---|---|---|
| Dependency pinning + hash verification | Low (hours) | None after setup | Prevents silent package substitution |
| Automated SBOM generation | Low (days) | None (runs in CI) | Full dependency visibility |
| SBOM policy gate (block on violation) | Medium (1-2 weeks) | Low (fail-fast feedback) | Blocks known-bad dependencies |
| Artifact signing (Sigstore cosign) | Medium (2-4 weeks setup) | None (transparent) | Proves artifact integrity |
| Ephemeral build runners | High (quarter) | None (transparent) | Prevents persistent build compromise |
| Admission controller (reject unsigned) | Medium (1-2 weeks) | None (enforcement at cluster) | Blocks unverified deployments |
What the Industry Gets Wrong About Supply Chain Security
“Vulnerability scanning protects the supply chain.” Scanning finds known vulnerabilities in known packages. The food inspector checking for known contaminants. It can’t find backdoors inserted by compromised maintainers, malicious code in build systems, or supply chain attacks that don’t match any CVE signature. Contamination the inspector wasn’t trained to look for. SolarWinds passed every scan. The attack was invisible to scanners by design.
“Pin dependencies and you’re safe.” Pinning prevents accidental upgrades. It doesn’t prevent a compromised registry from serving modified content at the pinned version. Same label. Different contents. Hash verification (integrity checksums) alongside version pinning catches this. Most teams pin without hashing, which addresses half the threat.
SolarWinds worked because the build system was trusted without question. The kitchen was never inspected. With signed commits, ephemeral build environments, attestation verification, and admission policies that reject unverified artifacts, that trust becomes earned instead of assumed. The paved road doesn’t just make security easier. It makes the SolarWinds attack vector structurally impossible in your pipeline. Every ingredient traced. Every kitchen inspected. Every package sealed.