Security

Kubernetes Supply Chain Security

● Advanced ⏱ 20 min read

In 2020, the SolarWinds attack compromised thousands of organizations by injecting malicious code into a software update. In 2021, a single developer controlled a npm package used by millions of projects. Software supply chain attacks are not theoretical — they target the build pipeline, not the running application. This guide covers the tools and patterns Kubernetes practitioners use to verify everything that runs in the cluster came from a trusted, auditable source.

Supply Chain Threat Model

The attack surface spans from source code to the running pod:

Supply chain attack surface — where threats enter
SOURCE
⚠️ malicious PR
⚠️ leaked creds
⚠️ typosquat dep
BUILD
⚠️ poisoned CI
⚠️ runner exploit
⚠️ mutable base img
REGISTRY
⚠️ tag overwrite
⚠️ reg compromise
⚠️ pull from DockerHub
DEPLOY
✅ policy admission
✅ sig verification
✅ provenance check
RUNTIME
✅ PSS restricted
✅ RBAC least-priv
✅ mTLS auth
Defense-in-depth: the further right in the pipeline you can catch an issue, the fewer malicious payloads reach production. But deploy-time admission is the last safety net.
Supply chain attack surface spans source → build → registry → deploy → runtime. Deploy-time admission is the last gate before code runs.

SLSA Levels

SLSA (Supply-chain Levels for Software Artifacts) is a security framework that defines four progressive levels of supply chain integrity, from basic (level 1) to hermetic, verified builds (level 4).

LevelRequirementsWhat it gives you
SLSA 1 Build provenance generated and published (unsigned). Audit trail. Know what was built from what.
SLSA 2 Signed provenance. Hosted build service (GitHub Actions, Cloud Build). Tamper detection. Provenance can be verified.
SLSA 3 Hardened build service. Isolated builds. No persistent credentials on build runner. Prevents build environment compromise from injecting malicious artifacts.
SLSA 4 Hermetic builds. Two-party review. Verifiable reproducibility. Maximum assurance. Can independently reproduce the exact artifact.
💡
Practical starting point: SLSA 2

SLSA 3 and 4 require significant CI infrastructure changes. For most teams, reaching SLSA 2 (signed provenance from a hosted CI service like GitHub Actions) provides substantial protection with manageable effort. GitHub Actions natively supports SLSA provenance generation via the slsa-framework/slsa-github-generator action.

SBOMs

A Software Bill of Materials (SBOM) is a machine-readable inventory of every component in an artifact — OS packages, language dependencies, transitive dependencies. When a vulnerability like Log4Shell is disclosed, an SBOM lets you instantly answer "which of my images contains log4j?"

Syft — generate an SBOM
# Generate SBOM in SPDX format
syft myapp:1.2.3 -o spdx-json > sbom.spdx.json

# Generate SBOM in CycloneDX format
syft myapp:1.2.3 -o cyclonedx-json > sbom.cdx.json

# Attach SBOM to the image in the registry (as OCI artifact)
cosign attach sbom --sbom sbom.spdx.json myapp@sha256:abc123...

# Scan an SBOM for vulnerabilities
grype sbom:./sbom.spdx.json

# Query the SBOM for a specific package
cat sbom.spdx.json | jq '.packages[] | select(.name == "log4j")'

Sigstore & Provenance

Sigstore is a set of tools for signing software artifacts and recording the signatures in a public, append-only transparency log (Rekor). Cosign handles image signing; Fulcio provides keyless certificate issuance tied to OIDC identities.

SLSA provenance with GitHub Actions
# .github/workflows/build.yml
jobs:
  build:
    permissions:
      id-token: write        # required for keyless signing
      contents: read
      packages: write
    steps:
    - uses: actions/checkout@v4

    - name: Build and push
      id: build
      run: |
        docker build -t ghcr.io/myorg/myapp:${{ github.sha }} .
        docker push ghcr.io/myorg/myapp:${{ github.sha }}
        echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/myorg/myapp:${{ github.sha }})" >> $GITHUB_OUTPUT

    - name: Sign image (keyless — Sigstore)
      run: |
        cosign sign ghcr.io/myorg/myapp@${{ steps.build.outputs.digest }}

    - name: Generate SLSA provenance
      uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
      with:
        image: ghcr.io/myorg/myapp
        digest: ${{ steps.build.outputs.digest }}

Policy Admission

Admission controllers run as webhooks and evaluate every create/update request to the API server. Policy engines let you write rules in a policy language (Kyverno uses YAML; OPA uses Rego) rather than building a custom webhook server.

ToolLanguageStrengths
KyvernoYAML/CELKubernetes-native policies. Easy to read. Mutation + generation built in. No separate language to learn.
OPA GatekeeperRegoPowerful Rego language. Better for complex logic. Widely adopted in enterprises.
Sigstore Policy ControllerYAMLFocused on image signature verification. Simple setup for cosign-based workflows.

Kyverno Policies

Kyverno — block images without a digest
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-digest
spec:
  validationFailureAction: Enforce
  rules:
  - name: check-image-digest
    match:
      any:
      - resources:
          kinds: ["Pod"]
    validate:
      message: "Images must be pinned to a digest (@sha256:...)."
      foreach:
      - list: "request.object.spec.containers"
        deny:
          conditions:
            any:
            - key: "{{ element.image }}"
              operator: NotContains
              value: "@sha256:"
Kyverno — verify SLSA provenance attestation
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-slsa-provenance
spec:
  validationFailureAction: Enforce
  rules:
  - name: check-slsa-provenance
    match:
      any:
      - resources:
          kinds: ["Pod"]
    verifyImages:
    - imageReferences:
      - "ghcr.io/myorg/*:*"
      attestations:
      - type: https://slsa.dev/provenance/v0.2
        attestors:
        - count: 1
          entries:
          - keyless:
              subject: "https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main"
              issuer: "https://token.actions.githubusercontent.com"
        conditions:
        - all:
          - key: "{{ buildType }}"
            operator: Equals
            value: "https://github.com/slsa-framework/slsa-github-generator/container@v1"

OPA Gatekeeper

ConstraintTemplate — block latest tag
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sbanlatestimage
spec:
  crd:
    spec:
      names:
        kind: K8sBanLatestImage
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8sbanlatestimage
      violation[{"msg": msg}] {
        container := input.review.object.spec.containers[_]
        endswith(container.image, ":latest")
        msg := sprintf("Image %v uses :latest tag — pin to a specific version", [container.image])
      }
      violation[{"msg": msg}] {
        container := input.review.object.spec.containers[_]
        not contains(container.image, ":")
        msg := sprintf("Image %v has no tag — pin to a specific version", [container.image])
      }
Constraint — apply the template
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBanLatestImage
metadata:
  name: ban-latest-image
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups: [""]
      kinds: ["Pod"]
    excludedNamespaces: ["kube-system"]

CI/CD Security Gates

Defense-in-depth means catching issues as early as possible. A practical CI pipeline for supply chain security:

  1. Dependency scanning — run trivy fs . or snyk test against the source tree to catch vulnerable dependencies before building.
  2. Build from a pinned, verified base image — pin FROM debian:12@sha256:abc... and verify the base image signature in CI.
  3. Generate SBOM during build — attach to the image with cosign attach sbom.
  4. Scan the built imagetrivy image --exit-code 1 --severity CRITICAL myapp:sha.
  5. Sign the image and generate SLSA provenance — keyless via GitHub Actions OIDC.
  6. Admission verification at deploy — Kyverno or Policy Controller verifies the signature and provenance attestation before the pod runs.
⚠️
Don't rely on admission alone

Admission controllers can be bypassed if a cluster admin has broad permissions. They are a gate, not a moat. Combined with image signing and SBOM attestations that trace back to an auditable CI run, you create a chain of custody — not just a yes/no gate.