Software Supply Chain Security: SBOM, SLSA, and Artifact Signing Complete Guide
A comprehensive guide to software supply chain security in 2026 — covering SBOMs, the SLSA framework, artifact signing with Cosign and Sigstore, and how to implement it all in your CI/CD pipeline.
The SolarWinds attack. The Log4Shell vulnerability. The xz utils backdoor. Every major supply chain incident in the last few years has proven the same point: it's not enough to secure your code. You need to secure everything that touches your code — the build process, the dependencies, the artifacts, and the deployment pipeline.
In 2026, software supply chain security isn't optional. Government regulations (the US Executive Order 14028, the EU Cyber Resilience Act) now mandate SBOMs for software sold to federal agencies. Major cloud providers require SLSA compliance for marketplace listings. And your customers are starting to ask for provenance attestations.
This guide covers the three pillars of supply chain security: SBOMs (knowing what's in your software), SLSA (proving how it was built), and artifact signing (proving it hasn't been tampered with).
The Problem: You Don't Know What You're Running
Ask yourself these questions:
- Can you list every dependency — direct and transitive — in your production containers?
- Can you prove that the binary running in production was built from the exact source code you reviewed?
- Can you verify that no one modified the artifact between your CI pipeline and your production cluster?
If you answered "no" to any of these, your supply chain has gaps that attackers can exploit.
The attack surface is enormous:
Source Code → Build System → Package Registry → Container Registry → Production
↑ ↑ ↑ ↑ ↑
Malicious Compromised Typosquatting Image tampering Runtime
commits CI runner packages or poisoning injection
Each arrow is an attack vector. Supply chain security addresses all of them.
Pillar 1: SBOMs — Know What's Inside
An SBOM (Software Bill of Materials) is a complete, machine-readable inventory of every component in your software. Think of it as a nutritional label for code.
An SBOM includes:
- Every direct dependency and its version
- Every transitive (indirect) dependency
- Licenses for each component
- Known vulnerabilities (when cross-referenced with a vulnerability database)
- Supplier information
SBOM Formats
Two formats dominate:
SPDX (Software Package Data Exchange) — maintained by the Linux Foundation, ISO standard (ISO/IEC 5962:2021). Preferred by government agencies.
CycloneDX — maintained by OWASP. More focused on security use cases (vulnerability tracking, license compliance).
Both are valid. If you're selling to US government agencies, SPDX is often required. For internal security, CycloneDX is more practical.
Generating SBOMs
Syft (by Anchore) is the most popular open-source SBOM generator:
# Generate SBOM for a container image
syft registry.example.com/myapp:v1.0 -o spdx-json > sbom.spdx.json
# Generate SBOM for a local directory
syft dir:./my-project -o cyclonedx-json > sbom.cdx.json
# Generate SBOM for a Dockerfile
syft docker:myapp:latest -o spdx-json > sbom.spdx.jsonTrivy (by Aqua Security) can also generate SBOMs and simultaneously scan for vulnerabilities:
# Generate SBOM + vulnerability scan
trivy image --format cyclonedx --output sbom.cdx.json registry.example.com/myapp:v1.0
# Scan an existing SBOM for vulnerabilities
trivy sbom sbom.cdx.jsonSBOM in CI/CD (GitHub Actions)
Here's how to generate and store SBOMs automatically:
name: Build with SBOM
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build container image
run: docker build -t myapp:${{ github.sha }} .
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: myapp:${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- name: Scan SBOM for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
input: sbom.spdx.json
format: table
severity: CRITICAL,HIGH
exit-code: 1 # Fail the build on critical/high vulns
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.jsonPillar 2: SLSA — Prove How It Was Built
SLSA (Supply-chain Levels for Software Artifacts, pronounced "salsa") is a framework that defines levels of build integrity. It answers the question: "Can you prove this artifact was built from this source code, using this build process, without tampering?"
SLSA Levels
SLSA v1.1 defines four levels:
Level 0 — No guarantees. You build on your laptop and push directly. No provenance, no verification.
Level 1 — Provenance exists. The build process generates a provenance attestation — a signed document that says "this artifact was built from this commit, on this date, using this build system." It's not tamper-proof yet, but it exists.
Level 2 — Hosted build, signed provenance. The build runs on a hosted service (GitHub Actions, Cloud Build) and the provenance is signed by the build platform. This prevents developers from falsifying the provenance.
Level 3 — Hardened build platform. The build runs in an isolated, ephemeral environment. No one — not even the project maintainers — can inject material into the build process without it being reflected in the provenance. The build platform is auditable and tamper-resistant.
Most teams should aim for Level 2 as a baseline and Level 3 for critical software.
Generating SLSA Provenance with GitHub Actions
GitHub Actions has built-in support for SLSA provenance:
name: Build with SLSA Provenance
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for signing
packages: write
attestations: write
steps:
- uses: actions/checkout@v4
- name: Build and push image
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
- name: Generate SLSA provenance
uses: actions/attest-build-provenance@v2
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: trueThis generates a signed attestation that proves:
- Which repository the code came from
- Which commit was built
- Which workflow and runner built it
- When it was built
- What inputs were used
Verifying SLSA Provenance
Anyone can verify the provenance of an artifact:
# Verify provenance using GitHub CLI
gh attestation verify oci://ghcr.io/myorg/myapp:v1.0 \
--owner myorg
# Verify using cosign
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity-regexp "https://github.com/myorg/myapp" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/myorg/myapp:v1.0Pillar 3: Artifact Signing — Prove It Hasn't Been Tampered With
Even with SBOMs and SLSA provenance, there's still a gap: how do you prove the artifact in your registry right now is the same one your CI pipeline produced? Someone with registry access could replace it.
Artifact signing solves this by cryptographically signing your container images and verifying the signature before deployment.
Cosign and Sigstore
Sigstore is an open-source project (backed by the Linux Foundation) that makes artifact signing easy. Cosign is its CLI tool for signing containers.
The key innovation of Sigstore is keyless signing — instead of managing long-lived signing keys (which are themselves a security risk), Sigstore uses short-lived certificates tied to your identity (GitHub OIDC, Google, etc.).
Signing Images in CI/CD
name: Build, Sign, and Verify
on:
push:
branches: [main]
jobs:
build-and-sign:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # For keyless signing
steps:
- uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign the image (keyless)
run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}Verifying Signatures Before Deployment
You can enforce signature verification in your Kubernetes cluster using admission controllers:
# Kyverno policy to require signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.devNow any pod that tries to run an unsigned (or incorrectly signed) image from your registry will be rejected by Kubernetes.
Putting It All Together: A Complete Supply Chain Security Pipeline
Here's what a mature supply chain security pipeline looks like:
Source Code
│
├── Dependency scanning (Dependabot, Renovate)
├── Secret scanning (GitHub Advanced Security)
└── Code review + branch protection
│
▼
Build (GitHub Actions / Cloud Build)
│
├── SBOM generation (Syft)
├── Vulnerability scan (Trivy)
├── SLSA provenance generation
└── Artifact signing (Cosign)
│
▼
Container Registry (GHCR / ECR / Artifact Registry)
│
├── Signed images only
├── SBOM attached as attestation
└── Provenance attached as attestation
│
▼
Kubernetes Cluster
│
├── Admission controller verifies signatures (Kyverno)
├── Runtime scanning (Falco)
└── SBOM-based vulnerability monitoring
Full GitHub Actions Workflow
name: Secure Supply Chain Build
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-secure:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
attestations: write
security-events: write
steps:
- uses: actions/checkout@v4
- name: Install tools
uses: sigstore/cosign-installer@v3
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- name: Scan for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
input: sbom.spdx.json
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
- name: Upload scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
- name: Sign the image
run: |
cosign sign --yes \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
- name: Attach SBOM attestation
run: |
cosign attest --yes \
--predicate sbom.spdx.json \
--type spdxjson \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
- name: Generate SLSA provenance
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: trueWhere to Start
If this feels overwhelming, here's a prioritized adoption path:
Week 1-2: SBOM generation. Add Syft or Trivy to your CI pipeline. Generate SBOMs and store them. This alone gives you visibility into your dependency tree.
Week 3-4: Vulnerability scanning. Scan your SBOMs against vulnerability databases. Set up alerts for critical CVEs. Block builds with known critical vulnerabilities.
Week 5-6: Artifact signing. Add Cosign keyless signing to your CI pipeline. This is surprisingly easy — it's literally one step in your workflow.
Week 7-8: SLSA provenance. Enable GitHub's build provenance attestation. Verify provenance in your deployment pipeline.
Week 9-10: Admission control. Deploy Kyverno or OPA Gatekeeper to enforce signature verification in your Kubernetes cluster. No unsigned images can run.
Week 11-12: Continuous monitoring. Set up runtime scanning with Falco. Monitor your SBOMs for newly discovered CVEs. Build dashboards for supply chain health.
Wrapping Up
Software supply chain security is no longer a "nice to have." With regulatory mandates, increasing attack sophistication, and the sheer complexity of modern dependency trees, securing your supply chain is as important as securing your application code.
The good news is that the tooling has matured significantly. Syft, Trivy, Cosign, Sigstore, and SLSA are all open-source, well-documented, and integrate cleanly with existing CI/CD pipelines. You can go from zero to a fully signed, attested, SBOM-equipped pipeline in a few weeks.
Start with visibility (SBOMs), add integrity (signing), then add proof (SLSA). Each layer makes your supply chain harder to compromise.
Want to learn DevSecOps from scratch? KodeKloud's security courses cover container security, Kubernetes hardening, and CI/CD security with hands-on labs.
Need a secure place to host your infrastructure? DigitalOcean provides managed Kubernetes with built-in container registry and security scanning.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Build a DevSecOps Pipeline with Trivy, SonarQube, and OPA from Scratch (2026)
Step-by-step project walkthrough: add security scanning, code quality gates, and policy enforcement to a GitHub Actions pipeline. Real configs, production-ready.
AI Agents for Automated Terraform Code Review — The Future of IaC Quality
How AI agents are automating Terraform code review with security scanning, cost estimation, best practice enforcement, and drift prevention. Covers practical tools, custom LLM pipelines, and CI/CD integration.
Build a Complete CI/CD Pipeline with GitHub Actions + ArgoCD + EKS (2026)
A full project walkthrough — from a simple app to a production-grade GitOps pipeline with automated builds, image scanning, and deployments to AWS EKS using ArgoCD.