On This Page
  1. Supply Chain Attack Landscape
  2. The Full Trust Chain
  3. SBOM: Software Bill of Materials
  4. in-toto Attestations
  5. Sigstore Policy Controller
  6. CI/CD Pipeline Hardening
  7. Dependency Management
  8. Third-Party Operators & Helm Charts
  9. Software Composition Analysis
  10. Runtime Integrity
  11. Metrics, Alerts & Runbooks
  12. Best Practices
Coverage Checklist

Supply Chain Attack Landscape

A software supply chain attack compromises software or its delivery infrastructure before it reaches the consumer. Kubernetes clusters are high-value targets because a single compromised component can affect all workloads.

Source Code Compromise

Attacker gains write access to source repository and injects malicious code. Bypasses all binary-level controls — the attack is in source.

Build System Compromise

Attacker controls CI/CD pipeline and injects malicious code during compilation or packaging without touching source. Classic SolarWinds pattern.

Dependency Confusion

Attacker publishes a package to a public registry with the same name as a private internal package at a higher version number. Build tools prefer the public version.

Typosquatting

Malicious packages with names similar to popular packages: reqeusts instead of requests, colourama instead of colorama.

Compromised Base Image

Malicious code injected into a popular Docker Hub base image. All downstream images built from it inherit the backdoor. See Image Security.

Registry Credential Theft

Attacker steals registry push credentials from CI/CD environment and replaces legitimate images with malicious ones using the same tag.

Malicious Third-Party Operator

Kubernetes operator installed from an untrusted source requests excessive RBAC permissions and acts as persistent backdoor with cluster-wide access.

Compromised Helm Chart

Helm chart from a public repository contains malicious container images or injects privileged workloads. Chart maintainer account compromise.

CI/CD Secret Exfiltration

Pull request from a fork triggers a CI workflow with access to organization secrets. Attacker exfiltrates registry credentials, signing keys, or cloud credentials.

The Full Trust Chain

Securing the supply chain means establishing and verifying trust at every step from source code to running container.

── Source → Running Pod: the full trust chain ────────────────────────

1. Source Code
├── Branch protection, required reviews, signed commits
├── Secret scanning (detect-secrets, Gitleaks, GitHub secret scanning)
└── Dependency lockfiles committed to repo


2. CI/CD Build
├── Pinned action SHAs (GitHub Actions)
├── Minimal OIDC permissions, no long-lived secrets
├── Reproducible builds (hermetic where possible)
├── Generate SBOM (Syft / Trivy)
└── Generate SLSA provenance attestation


3. Container Image
├── Multi-stage build, distroless base
├── Vulnerability scan (Trivy — block on CRITICAL/HIGH)
├── Cosign sign (keyless via OIDC)
└── Cosign attest (SBOM + provenance)


4. Registry
├── Private registry, immutable tags
├── Registry scanning gate
└── Push credentials short-lived (OIDC, not static credentials)


5. Admission
├── Sigstore Policy Controller: verify signature + attestations
├── Registry allow-list: only approved registries
└── Digest pinning enforcement


6. Running Pod
├── Falco: runtime behavior monitoring
├── Trivy Operator: continuous CVE scanning
└── Network policies restrict blast radius

SBOM: Software Bill of Materials

An SBOM is a machine-readable inventory of every component in a software artifact — OS packages, language libraries, build tools, and their versions and licenses. It is the foundation of supply chain visibility.

SBOM Formats

FormatMaintained ByPrimary UseInterchange
CycloneDX OWASP Security analysis, vulnerability correlation JSON, XML, Protobuf
SPDX Linux Foundation License compliance, legal review JSON, YAML, RDF, tag-value
SWID ISO/IEC 19770-2 Enterprise asset management XML
CycloneDX vs SPDX

CycloneDX is optimized for security use cases — it has native fields for vulnerabilities, CVSS scores, and patch status. SPDX is the NTIA minimum elements standard and better suited for license compliance. Most tools support both. Prefer CycloneDX for Kubernetes supply chain pipelines.

Generating SBOMs with Syft

# Install Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# Generate CycloneDX SBOM from container image
syft registry.example.com/myapp@sha256:abc123 \
  -o cyclonedx-json=sbom.cyclonedx.json

# Generate SPDX SBOM
syft registry.example.com/myapp@sha256:abc123 \
  -o spdx-json=sbom.spdx.json

# Generate SBOM from source directory (during build)
syft dir:. -o cyclonedx-json=sbom.cyclonedx.json

# Generate from Docker image tarball
syft docker-archive:myapp.tar -o cyclonedx-json=sbom.cyclonedx.json

# Include licenses in output
syft registry.example.com/myapp@sha256:abc123 \
  -o cyclonedx-json=sbom.cyclonedx.json \
  --config syft.yaml  # syft.yaml: catalogers, exclude patterns

Generating SBOMs with Trivy

# Generate CycloneDX SBOM with Trivy
trivy image \
  --format cyclonedx \
  --output sbom.cyclonedx.json \
  registry.example.com/myapp@sha256:abc123

# Generate SPDX SBOM
trivy image \
  --format spdx-json \
  --output sbom.spdx.json \
  registry.example.com/myapp@sha256:abc123

# Docker buildx: generate SBOM during build (BuildKit 0.11+)
docker buildx build \
  --sbom=true \
  --output type=registry \
  --tag registry.example.com/myapp:1.0.0 \
  .

Distributing SBOMs via OCI Registry

# Attach SBOM as OCI artifact referrer (using cosign)
cosign attach sbom \
  --sbom sbom.cyclonedx.json \
  --type cyclonedx \
  registry.example.com/myapp@sha256:abc123

# Verify and retrieve attached SBOM
cosign download sbom registry.example.com/myapp@sha256:abc123

# Attach as attestation (preferred — signed and verifiable)
cosign attest \
  --key cosign.key \
  --type cyclonedx \
  --predicate sbom.cyclonedx.json \
  registry.example.com/myapp@sha256:abc123

# Download and verify SBOM attestation
cosign verify-attestation \
  --key cosign.pub \
  --type cyclonedx \
  registry.example.com/myapp@sha256:abc123 | \
  jq '.payload | @base64d | fromjson | .predicate'

in-toto Attestations

in-toto is a framework for cryptographically securing software supply chain operations. It defines a format for recording facts about software artifacts (attestations) that can be verified independently of the artifact itself.

── in-toto attestation structure ────────────────────────────────────

Attestation Envelope (DSSE — Dead Simple Signing Envelope)
├── Statement
│ ├── _type: "https://in-toto.io/Statement/v0.1"
│ ├── subject: [{name, digest}] ← what artifact this is about
│ └── predicateType: "..." ← what kind of fact

└── Predicate (varies by predicateType)
├── slsaprovenance/v1: build metadata (builder, source, inputs)
├── cyclonedx: SBOM component list
├── spdx: SBOM (SPDX format)
├── cosign.sigstore.dev/attestation/vuln/v1: vulnerability scan results
└── custom: any JSON predicate you define

SLSA Provenance Attestation

# SLSA provenance predicate (v1) — generated by build platform
{
  "_type": "https://in-toto.io/Statement/v0.1",
  "subject": [{
    "name": "registry.example.com/myapp",
    "digest": { "sha256": "abc123..." }
  }],
  "predicateType": "https://slsa.dev/provenance/v1",
  "predicate": {
    "buildDefinition": {
      "buildType": "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
      "externalParameters": {
        "workflow": {
          "ref": "refs/tags/v1.2.3",
          "repository": "https://github.com/myorg/myrepo",
          "path": ".github/workflows/release.yaml"
        }
      }
    },
    "runDetails": {
      "builder": {
        "id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0"
      },
      "metadata": {
        "invocationId": "https://github.com/myorg/myrepo/actions/runs/1234567890",
        "startedOn": "2024-01-15T10:00:00Z",
        "finishedOn": "2024-01-15T10:05:00Z"
      }
    }
  }
}

Vulnerability Attestation

# Generate vulnerability scan attestation with Trivy + Cosign

# 1. Run Trivy scan and save results
trivy image \
  --format cosign-vuln \
  --output vuln-results.json \
  registry.example.com/myapp@sha256:abc123

# 2. Attest vulnerability scan results
cosign attest \
  --key cosign.key \
  --type vuln \
  --predicate vuln-results.json \
  registry.example.com/myapp@sha256:abc123

# 3. Verify vulnerability attestation at admission (Policy Controller)
# (Policy Controller can reject images whose latest scan exceeds CVE threshold)

Sigstore Policy Controller

The Sigstore Policy Controller is a validating admission webhook that enforces image signature and attestation policies. It is more expressive than Kyverno's verifyImages and is the reference implementation for Sigstore-native policies.

Policy Controller vs Kyverno verifyImages

Both enforce Cosign signatures at admission. Policy Controller is purpose-built for Sigstore and supports richer attestation policies (e.g., require SBOM with no Critical CVEs). Kyverno's verifyImages is integrated with Kyverno's broader policy framework. If you already run Kyverno, use its verifyImages. If you want a dedicated supply chain tool, use Policy Controller. See Image Security for Kyverno verifyImages details.

# Install Sigstore Policy Controller
helm repo add sigstore https://sigstore.github.io/helm-charts
helm install policy-controller sigstore/policy-controller \
  --namespace cosign-system \
  --create-namespace
# Opt namespace into Policy Controller enforcement
kubectl label namespace production \
  policy.sigstore.dev/include=true

ClusterImagePolicy: Keyless Signature

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: require-signed-images
spec:
  images:
  - glob: "registry.example.com/**"
  authorities:
  - keyless:
      url: https://fulcio.sigstore.dev
      identities:
      - issuer: https://token.actions.githubusercontent.com
        subject: https://github.com/myorg/myrepo/.github/workflows/release.yaml@refs/heads/main

ClusterImagePolicy: Require SBOM Attestation

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: require-sbom-attestation
spec:
  images:
  - glob: "registry.example.com/**"
  authorities:
  - keyless:
      url: https://fulcio.sigstore.dev
      identities:
      - issuer: https://token.actions.githubusercontent.com
        subjectRegExp: "https://github.com/myorg/.*"
    attestations:
    - name: must-have-sbom
      predicateType: https://cyclonedx.org/bom
      policy:
        type: cue
        data: |
          # CUE policy: SBOM must have at least one component
          predicate: components: [_, ...]

ClusterImagePolicy: Require Vulnerability Scan with No Critical CVEs

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: require-vuln-attestation
spec:
  images:
  - glob: "registry.example.com/**"
  authorities:
  - keyless:
      url: https://fulcio.sigstore.dev
      identities:
      - issuer: https://token.actions.githubusercontent.com
        subjectRegExp: "https://github.com/myorg/.*"
    attestations:
    - name: no-critical-cves
      predicateType: cosign.sigstore.dev/attestation/vuln/v1
      policy:
        type: cue
        data: |
          import "time"
          # Scan must be less than 24 hours old
          predicate: scanner: result: Summary: Critical: 0
          predicate: metadata: scanFinishedOn: >time.Now().Add(-24*time.Hour)

CI/CD Pipeline Hardening

The CI/CD pipeline is the highest-privilege environment in the supply chain. Compromising the build pipeline allows injecting malicious code into all downstream artifacts without modifying source.

GitHub Actions Hardening

# .github/workflows/release.yaml — hardened workflow
name: Release
on:
  push:
    tags: ['v*']

permissions:              # Minimal top-level permissions
  contents: read

jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write     # Required for OIDC token (Cosign keyless)
      packages: write     # Required for pushing to GHCR
      attestations: write # Required for GitHub Attestations API

    steps:
    # Pin actions to immutable SHA — never use @main or @v1 (mutable tags)
    - name: Checkout
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349  # v3.7.1

    - name: Log in to GHCR
      uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567  # v3.3.0
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}  # Short-lived, auto-rotated

    - name: Build and push
      id: build
      uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75  # v6.9.0
      with:
        context: .
        push: true
        tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
        sbom: true           # BuildKit SBOM generation
        provenance: mode=max  # SLSA provenance

    - name: Sign image
      run: |
        cosign sign --yes \
          ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
      env:
        COSIGN_EXPERIMENTAL: "1"
Never Use Mutable Action References

Using actions/checkout@v4 is dangerous — the v4 tag can be moved to point to a different commit. A compromised action maintainer or tag overwrite runs arbitrary code in your pipeline. Pin every action to an immutable commit SHA. Use tools like pin-github-action or Dependabot to manage SHA updates.

Fork PR Security

# Prevent fork PRs from accessing secrets
on:
  pull_request_target:    # DANGER: runs in context of base repo, has secrets
# Use pull_request instead — runs in fork context, no secrets
on:
  pull_request:           # Safe: no access to base repo secrets

# Require approval for first-time contributors
# Settings → Actions → Fork pull request workflows → Require approval

Securing CI Secrets

ApproachDescriptionSecurity Level
Static secrets in CI varsLong-lived credentials stored in CI settingsLow
OIDC federationCI gets short-lived token via OIDC trust with cloud providerHigh
Vault AppRole from CICI authenticates to Vault, retrieves short-lived credsHigh
GitHub Actions OIDC → AWS AssumeRoleWithWebIdentityNo static AWS credentials; role assumed per workflow runHighest
# GitHub Actions OIDC → AWS ECR (no static credentials)
- name: Configure AWS credentials via OIDC
  uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecr-push
    aws-region: us-east-1

# AWS IAM role trust policy (allows GitHub Actions OIDC)
{
  "Effect": "Allow",
  "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
    },
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
    }
  }
}

Dependency Management

Lockfile Integrity

Lockfiles record the exact versions and checksums of every dependency. Committing them to the repository ensures reproducibility and enables detection of unexpected changes.

LanguageLockfileIntegrity MechanismEnforce in CI
Gogo.sumSHA-256 hash of each module versiongo mod verify
Node.jspackage-lock.json / yarn.lockSHA-512 per package (npm); SHA-1 (yarn)npm ci (fails if lock mismatch)
Pythonrequirements.txt + hashes / poetry.lock--hash=sha256: per packagepip install --require-hashes
RustCargo.lockChecksum per crate versioncargo fetch --locked
Java (Maven)None built-inUse checksum plugin or Gradle verificationGradle: verification-metadata.xml
# Python: install with hash verification
# requirements.txt with hashes
requests==2.31.0 \
  --hash=sha256:58cd2187423839f822e6c5d5ded81a... \
  --hash=sha256:942c5a758f98d790eaed1a29cb6eefc...

# Install enforcing hash verification
pip install --require-hashes -r requirements.txt

# Go: verify all module checksums
go mod verify
# Fails if any module hash doesn't match go.sum

# Node: use npm ci (not npm install) in CI
# npm ci: installs exactly what's in package-lock.json, fails on mismatch
npm ci --only=production

Private Module Proxy / Artifact Repository

Routing all dependency downloads through a private proxy prevents dependency confusion attacks — the proxy enforces that internal package names never resolve to public registries.

# Go: use private module proxy (Athens, Artifactory, Nexus)
GONOSUMCHECK=*.internal.example.com \
GONOSUMDB=*.internal.example.com \
GOFLAGS=-mod=mod \
GOPROXY=https://goproxy.internal.example.com,direct \
  go build ./...

# npm: private registry via .npmrc
# .npmrc
registry=https://registry.example.com
//registry.example.com/:_authToken=${NPM_TOKEN}
# Block public registry fallback:
@myorg:registry=https://registry.example.com

# pip: private PyPI mirror
pip install --index-url https://pypi.internal.example.com/simple/ \
  --no-deps \
  requests==2.31.0
Dependency Confusion Attack Vector

If your build tool falls back to a public registry when a package isn't found in your private registry, an attacker can publish a package with the same name as your internal package at a higher version. The build tool will download and execute the attacker's package. Prevent fallback by configuring your proxy to be authoritative for your namespace — or block all external registry access from build nodes.

Automated Dependency Updates

# .github/renovate.json — automated dependency updates with PR per change
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true     // Auto-merge patch updates if CI passes
    },
    {
      "matchDepTypes": ["action"],
      "pinDigests": true     // Pin GitHub Action versions to SHA
    }
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"]
  }
}

Third-Party Operators & Helm Charts

Kubernetes operators and Helm charts from external sources are high-risk: they often request cluster-admin or broad RBAC permissions and run privileged workloads.

Operator Security Vetting Checklist

CheckCommand / ToolRed Flag
RBAC permissions requestedkubectl auth can-i --list --as=system:serviceaccount:operator-ns:operator-sacluster-admin, wildcard resources, secret reads cluster-wide
Pod securityReview operator Deployment specprivileged:true, hostPID, hostNetwork, hostPath mounts
Image provenancecosign verify operator imagesNo signature, unknown registry, no provenance
CVE statustrivy image on operator imagesCRITICAL CVEs in running operator image
Source codeReview GitHub repoInactive maintainers, no release process, no security policy
Network egressApply NetworkPolicy deny-all firstOperator dials back to vendor infrastructure unexpectedly

Helm Chart Verification

# Helm provenance: verify chart integrity (classic Helm signing)
# Chart packager creates .prov file with PGP signature

# Import chart maintainer's PGP key
gpg --import maintainer-pubkey.gpg

# Install with provenance verification
helm install myrelease myrepo/mychart --verify

# Download chart and verify manually
helm pull myrepo/mychart --verify --provenance

# Helm OCI: charts stored as OCI artifacts can be signed with Cosign
cosign sign oci://registry.example.com/charts/mychart:1.2.3
cosign verify \
  --key cosign.pub \
  oci://registry.example.com/charts/mychart:1.2.3
# Helm chart security review: render and inspect before installing

# Render all templates to stdout
helm template myrelease myrepo/mychart --values my-values.yaml

# Check for dangerous RBAC
helm template myrelease myrepo/mychart | \
  grep -A5 "ClusterRoleBinding\|cluster-admin\|\"*\""

# Check for privileged pods
helm template myrelease myrepo/mychart | \
  grep -i "privileged\|hostPID\|hostNetwork\|hostPath"

# Use Polaris to audit rendered templates
helm template myrelease myrepo/mychart | polaris audit --audit-path -
Helm Dependency Charts

Helm charts can have sub-chart dependencies. A parent chart with reasonable permissions might pull in a sub-chart with cluster-admin. Always render and inspect the full template output, not just the parent chart. Run helm dependency update and review charts/ directory contents.

Software Composition Analysis

Software Composition Analysis (SCA) identifies open-source components and their known vulnerabilities (CVEs) and license obligations in your codebase and container images.

SCA in the Pipeline

# Grype: vulnerability scan from SBOM
grype sbom:sbom.cyclonedx.json \
  --fail-on critical \
  --output json > vuln-report.json

# OWASP Dependency-Check for Java/Node/Python
dependency-check \
  --project myapp \
  --scan ./app \
  --format JSON \
  --out ./reports \
  --nvdApiKey $NVD_API_KEY

# OSV-Scanner: checks against OSV database (comprehensive, fast)
osv-scanner --lockfile package-lock.json
osv-scanner --sbom sbom.cyclonedx.json

VEX: Vulnerability Exploitability eXchange

VEX documents allow you to assert that a CVE in a dependency is not exploitable in your specific artifact — reducing false positives in vulnerability reports.

# VEX document: assert CVE not exploitable in our context
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "@id": "https://example.com/vex/myapp-1.0.0",
  "author": "security@example.com",
  "timestamp": "2024-01-15T10:00:00Z",
  "statements": [
    {
      "vulnerability": { "name": "CVE-2023-44487" },
      "products": [{ "@id": "pkg:oci/myapp@sha256:abc123" }],
      "status": "not_affected",
      "justification": "vulnerable_code_not_in_execute_path",
      "impact_statement": "HTTP/2 is not used by this component; TLS terminates at ingress"
    }
  ]
}

# Generate VEX with vexctl
vexctl create \
  --product "pkg:oci/myapp@sha256:abc123" \
  --vuln CVE-2023-44487 \
  --status not_affected \
  --justification vulnerable_code_not_in_execute_path

# Trivy respects VEX: filter results using VEX document
trivy image \
  --vex vex-document.openvex.json \
  registry.example.com/myapp@sha256:abc123

Runtime Integrity

dm-verity for Container Image Verification

dm-verity is a Linux kernel mechanism that cryptographically verifies block device data on every read. When used with container storage, it ensures the on-disk image layers match their expected hash — preventing tampering after pull.

dm-verity in Container Runtimes

containerd and CRI-O support read-only overlay filesystems backed by dm-verity via the stargz-snapshotter or overlayfs-verity. This is primarily useful in environments where you cannot fully trust the node storage, such as edge deployments or multi-tenant bare-metal clusters.

Kata Containers for Workload Isolation

Kata Containers run each pod in a lightweight VM instead of a container. This provides hardware-level isolation between workloads and between workloads and the host kernel — the strongest available workload isolation.

# Use Kata Containers runtime class for high-security workloads
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata-containers
handler: kata
scheduling:
  nodeSelector:
    kata-containers: "true"
---
spec:
  runtimeClassName: kata-containers
  containers:
  - name: sensitive-workload
    image: registry.example.com/payment-service@sha256:abc123

Metrics, Alerts & Runbooks

Key Metrics

MetricSourceDescription
cosign_verification_failures_totalPolicy ControllerImages rejected due to missing/invalid signature
policy_controller_admissions_totalPolicy ControllerTotal admissions evaluated by Policy Controller
trivy_vulnerability_idTrivy OperatorCVEs in running workloads (see Image Security)
sbom_age_hoursCustom / SBOM pipelineAge of most recent SBOM per image
unsigned_image_deployments_totalPolicy Controller auditDeployments that would be blocked in enforce mode

Alerts

# Alert: Unsigned image deployment blocked
- alert: UnsignedImageBlocked
  expr: increase(cosign_verification_failures_total[5m]) > 0
  for: 0m
  severity: critical
  annotations:
    summary: "Image signature verification failed — possible supply chain attack or CI misconfiguration"

# Alert: SBOM attestation missing for new deployment
- alert: SBOMAttestationMissing
  expr: increase(policy_controller_admissions_total{policy="require-sbom-attestation",result="denied"}[5m]) > 0
  annotations:
    summary: "Deployment rejected: missing SBOM attestation"

# Alert: Critical CVE in SBOM attestation
- alert: CriticalCVEInAttestaton
  expr: increase(policy_controller_admissions_total{policy="require-vuln-attestation",result="denied"}[5m]) > 0
  annotations:
    summary: "Deployment rejected: Critical CVE in vulnerability attestation"

# Alert: Policy Controller webhook unhealthy
- alert: PolicyControllerWebhookDown
  expr: up{job="policy-controller"} == 0
  for: 2m
  severity: critical
  annotations:
    summary: "Policy Controller webhook down — supply chain enforcement not active"

Runbooks

Unsigned Image Deployment Blocked

1. Check which image is failing: Policy Controller logs
2. Determine if CI signing step ran: check workflow logs
3. If CI failure: re-run signing step, push signature
4. If attack suspected: check registry push audit logs for unauthorized pushes

Dependency Confusion Detected

1. Identify the suspicious package version pulled
2. Check if it came from public registry vs internal proxy
3. Block package at proxy level immediately
4. Audit all CI runs that may have used the package
5. Rotate any credentials that ran in affected CI jobs

Compromised Third-Party Operator

1. Delete operator deployment immediately
2. Audit RBAC actions by operator SA in last 24h via audit logs
3. Rotate any secrets the operator had access to
4. Check for resources created by the operator (check ownerReferences)

Policy Controller Admission Failures Spike

1. Check Policy Controller pod health and logs
2. Verify Fulcio/Rekor (Sigstore) are reachable (for keyless)
3. Check if signing keys/certificates have expired
4. Review ClusterImagePolicy for syntax errors after recent changes

Build Pipeline Secret Exposure

1. Immediately rotate all secrets that may have been exposed
2. Identify which runs/forks had access to the workflow
3. Revoke and re-issue registry push credentials, signing keys
4. Audit all images pushed during the exposure window

Best Practices

1

Sign every image — enforce verification at admission

Keyless Cosign signing in CI costs nothing and takes seconds. Without admission enforcement, signing is security theater. Deploy Policy Controller or Kyverno verifyImages with Enforce mode in production. See Image Security for Cosign details.

2

Generate and attest SBOMs for every build

SBOMs provide the component inventory needed for rapid CVE triage when new vulnerabilities are disclosed. Without an SBOM, determining whether your workload is affected requires manual analysis of every image.

3

Pin all CI actions to immutable SHA digests

Mutable action references (@main, @v1) execute whatever code the tag points to at run time — including code pushed after a maintainer account compromise. Use Renovate to automate SHA updates.

4

Use OIDC federation instead of static CI credentials

Static registry push credentials, cloud API keys, and signing keys stored in CI secrets are a high-value target. Replace them with OIDC federation: GitHub Actions OIDC → AWS AssumeRoleWithWebIdentity, GCP Workload Identity, or Azure Federated Credentials.

5

Route all dependencies through a private proxy

A private artifact proxy (Artifactory, Nexus, Athens) centralizes dependency fetching, prevents dependency confusion attacks, and provides a single place to block compromised packages. Configure build tools to use the proxy with no public fallback for internal package namespaces.

6

Vet third-party operators before installation

Review RBAC requests, pod security settings, and image provenance of every operator before installing it in a production cluster. Prefer operators from vendors with active security programs, signed images, and published SBOMs.

7

Achieve SLSA Level 3 for critical workloads

SLSA Level 3 provides non-forgeable provenance from a hosted build platform. Use the slsa-github-generator reusable workflow to generate SLSA 3 attestations automatically for every release. Require SLSA provenance verification in Policy Controller.

8

Use VEX to reduce CVE triage noise

Not every CVE in a dependency is exploitable in your deployment context. Publish VEX documents alongside your SBOMs to assert non-exploitability for known false-positive CVEs. This reduces alert fatigue and lets teams focus on real risks.