Why Supply Chain Security Matters#

Your container image contains hundreds of dependencies you did not write. A compromised base image or malicious package runs with your application’s full permissions. Supply chain attacks target the build process because it is often less guarded than runtime.

The goal is to answer three questions for every artifact: what is in it (SBOM), who built it (signing), and how was it built (provenance).

SBOM Generation#

A Software Bill of Materials lists every dependency in an artifact. Two tools dominate.

Syft#

Syft outputs SBOMs in SPDX or CycloneDX format:

# Generate SBOM from a container image
syft packages registry.example.com/myapp:v1.2.3 -o spdx-json > myapp-sbom.spdx.json

# Scan a local directory (e.g., before building the image)
syft packages dir:./app -o cyclonedx-json > myapp-sbom.cdx.json

Trivy#

Trivy generates SBOMs and scans for vulnerabilities in a single pass:

trivy image --format cyclonedx --output sbom.cdx.json registry.example.com/myapp:v1.2.3

Embed SBOM generation in CI immediately after the image build step. Store SBOMs alongside images in the registry as OCI artifacts.

Image Signing with Cosign#

Cosign, part of the Sigstore project, signs container images using either a key pair or keyless signing with an OIDC identity.

Key-based Signing#

# Generate a key pair (store the private key securely)
cosign generate-key-pair

# Sign an image
cosign sign --key cosign.key registry.example.com/myapp:v1.2.3

# Verify
cosign verify --key cosign.pub registry.example.com/myapp:v1.2.3

Keyless Signing with Sigstore#

Keyless signing ties the signature to an OIDC identity and records it in Rekor (a public transparency log). No private key to manage:

# In GitHub Actions, this uses the OIDC token automatically
cosign sign registry.example.com/myapp@sha256:abc123...

Attaching SBOMs as Signed Attestations#

cosign attest --predicate myapp-sbom.spdx.json --type spdxjson \
  --key cosign.key registry.example.com/myapp:v1.2.3

Attestation is stronger than simple attachment because it is signed. Consumers verify the SBOM was produced by a trusted builder.

Enforcing Signatures in Kubernetes#

Signing images is useless if the cluster does not verify them. Kyverno is a policy engine that can block unsigned images at admission time.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: verify-cosign-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "registry.example.com/*"
          attestors:
            - count: 1
              entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
                      -----END PUBLIC KEY-----

Any pod using an image from registry.example.com that lacks a valid cosign signature is rejected. This works with both key-based and keyless verification.

Dependency Scanning#

Dependabot (GitHub-native) opens PRs to update dependencies with known CVEs:

version: 2
updates:
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"

Renovate offers more flexibility and can pin image digests automatically:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended", "docker:pinDigests"],
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true
    }
  ]
}

SLSA Framework#

Supply-chain Levels for Software Artifacts (SLSA, pronounced “salsa”) defines four levels of increasing build integrity:

  • Level 1: Build process is documented and produces provenance metadata.
  • Level 2: Build runs on a hosted service with signed provenance. Provenance is generated by the build platform, not the user script.
  • Level 3: Build platform is hardened. Source and build configuration are verified. Build is isolated and cannot be influenced by user parameters beyond the entry point.
  • Level 4: Two-person review and hermetic, reproducible builds.

For most teams, Level 2 is achievable immediately with GitHub Actions and the SLSA provenance generator.

Generating SLSA Provenance in GitHub Actions#

jobs:
  build:
    outputs:
      digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v4
      - id: build
        run: |
          docker build -t registry.example.com/myapp:${{ github.sha }} .
          docker push registry.example.com/myapp:${{ github.sha }}
          echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' registry.example.com/myapp:${{ github.sha }} | cut -d@ -f2)" >> "$GITHUB_OUTPUT"

  provenance:
    needs: build
    permissions:
      id-token: write
      contents: read
      actions: read
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
    with:
      image: registry.example.com/myapp
      digest: ${{ needs.build.outputs.digest }}

Hardening CI/CD Pipelines#

The pipeline itself is an attack surface. A compromised pipeline injects code into every build.

Pin actions by SHA, not tag. Tags are mutable:

# Vulnerable — tag can be moved
- uses: actions/checkout@v4

# Secure — immutable reference
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

Use ephemeral runners. Persistent runners accumulate credentials and cached state. Use --ephemeral on runner registration to destroy after each job.

Restrict pipeline token permissions. GitHub Actions GITHUB_TOKEN defaults to read-write:

permissions:
  contents: read
  packages: write

Isolate secrets per environment. Do not share production credentials with PR builds. Use GitHub Environments with required reviewers for production deployments.

Require signed commits on main. Enforce branch protection rules requiring signature verification to prevent unsigned workflow modifications.

Supply chain security is not a single tool. It is a pipeline: generate SBOMs, sign artifacts, attest provenance, verify at deployment, and continuously scan dependencies.