Production Reference

DevSecOps & Supply Chain Cheatsheet

Operational reference for securing the software supply chain. cosign signing, SBOM generation, SLSA provenance, GitHub Actions security patterns, and the gates that catch supply-chain attacks at CI time before they reach production.

Command-firstProduction notesSecurity warningsHardened patterns

Sigstore / cosign (artifact signing)

5 commands
cosign generate-key-pair

Generate a cosign keypair locally. Use only for testing — production should use keyless signing.

Warning: Local keys must be protected like any private key. Lost = re-issue all signatures; leaked = forged signatures.

cosign sign --identity-token=$OIDC_TOKEN <image>@sha256:<digest>

Keyless signing via Sigstore Fulcio. The signing identity comes from your OIDC provider (GitHub, Google, etc.).

Production note: Keyless is the production default. The signing certificate is short-lived and tied to a verifiable identity — no keys to leak or rotate.

cosign verify --certificate-identity-regexp=".*@example.com$" --certificate-oidc-issuer=https://accounts.google.com <image>

Verify a signature, gating on identity (issuer + subject pattern).

Warning: Without --certificate-identity, ANY Sigstore-signed image passes — defeats the purpose. Always pin the expected signer.

cosign attest --predicate sbom.spdx.json --type spdx <image>

Attach an SBOM (or other predicate) as an attestation alongside the image. Stored in the registry.

Production note: Predicate types: spdx, cyclonedx, slsaprovenance, vuln, custom. Use the standardized types when possible.

cosign verify-attestation --type slsaprovenance --certificate-identity-regexp ".*@github.com$" <image>

Verify an attestation matches expected predicate type and signer.

Production note: Pair with `cue` or `rego` to validate the predicate content (e.g. provenance level >= L3).

SBOMs (Software Bill of Materials)

5 commands
syft <image> -o spdx-json > sbom.spdx.json

Generate an SPDX-format SBOM from an image. SPDX is the ISO standard.

Production note: Generate at build time; attach as attestation. Recreating SBOMs after-the-fact is unreliable.

syft <image> -o cyclonedx-json > sbom.cdx.json

CycloneDX format. Often preferred by security tooling.

Production note: Both are valid. Pick one for your org and stick with it — multi-format proliferation creates confusion.

grype <image> / grype sbom:sbom.spdx.json

Vulnerability scan against an image or pre-generated SBOM. SBOM-based scans are deterministic.

Production note: Run grype in CI on every build. Gate releases on severity threshold (e.g. fail on Critical, page on High).

trivy image --severity HIGH,CRITICAL <image>

Trivy is the alternative to grype. Both are CNCF; pick one.

Production note: Trivy also scans IaC, secrets, misconfigurations — broader scope, different mental model.

oras attach --artifact-type "application/spdx+json" <image> sbom.spdx.json

Attach SBOM as an OCI referrer to an image. Stays linked through registry copies.

Production note: Modern registries (ghcr, ecr, gcr) support OCI 1.1 referrers — SBOMs travel with the image automatically.

SLSA provenance

5 commands
SLSA Build Level 1

Provenance document is generated. Build is documented but not necessarily reproducible.

Production note: Achievable with simple in-pipeline scripts; little assurance value alone.

SLSA Build Level 2

Build runs on a hosted service (e.g. GitHub Actions); provenance is signed.

Production note: GitHub Actions + slsa-github-generator is a turnkey path to L2.

SLSA Build Level 3

Provenance is non-forgeable: the build platform attests directly, not the build itself.

Production note: L3 is the realistic production target. Requires hardened build runners and isolated provenance generation.

slsa-verifier verify-artifact --provenance-path attestation.intoto.jsonl --source-uri github.com/org/repo --source-tag v1.2.3 <artifact>

Verify SLSA provenance against expected source repo and tag.

Production note: Run as an admission gate — only deploy artifacts whose provenance ties to your trusted source repo.

github_action_jobs: build: permissions: id-token: write uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1

GitHub Actions reusable workflow that produces SLSA L3 provenance for container images.

Production note: id-token: write is required for OIDC-based keyless signing. Pin the workflow to a specific tag, not @main.

Image admission policy (Kubernetes)

3 commands
kyverno verify-images: imageReferences: - "registry.example.com/*" attestors: - entries: - keyless: subject: ".*@example.com$" issuer: https://accounts.google.com

Kyverno cluster policy that requires keyless cosign signature on all images from your registry.

Production note: Audit-mode rollout (validationFailureAction: audit) before enforce. Logs unsigned images without blocking; gives you a window to clean up.

cosigned (admission webhook)

Sigstore-maintained admission controller that enforces signed images.

Production note: Lighter-weight than Kyverno if signing is the only policy you need. Kyverno wins for richer policy needs.

connaisseur (legacy)

Older admission controller for image signing. Still supported.

Warning: Kyverno + cosign or cosigned have eclipsed Connaisseur for new deployments.

GitHub Actions security

6 commands
permissions: contents: read id-token: write

Workflow-level permissions. Default is full GITHUB_TOKEN — explicit minimum is the safe default.

Warning: Without explicit permissions, every workflow has read+write to everything. A compromised dependency exfiltrates secrets immediately.

uses: actions/checkout@v4 # never @main, never @master

Pin actions to a specific tag or SHA. Renovate/Dependabot handle updates.

Production note: For public actions, prefer SHA pinning with comments: `uses: actions/checkout@8e5e7e5` # v4.1.7. SHAs are immutable; tags can be moved.

jobs.<job>.environment: production

Tie a job to a protected environment with required reviewers. Adds human gate to deploys.

Production note: Protected environments + branch protection rules + required reviewers = ".github/workflows/deploy.yml cannot deploy without two human approvals".

secrets: inherit / env: { GH_TOKEN: ${{ secrets.GH_TOKEN }} }

Secret scoping in reusable workflows. inherit shares all; explicit env is least privilege.

Warning: inherit is convenient but leaks every secret to every reusable workflow. Pass only the secrets needed.

pull_request_target vs pull_request

pull_request_target runs in the context of the base branch with secrets — risky for fork PRs. pull_request runs in fork context without secrets.

Warning: pull_request_target with checkout of fork code is the canonical "GitHub Actions RCE via PR" pattern. Don't check out fork code with secrets in scope.

job.<id>.steps[].if: always() && needs.scan.outputs.severity == 'critical'

Gate on outputs from previous jobs. Use to block deploys on scan failure.

Production note: Standard CI gate: scan -> deploy, with deploy `needs: [scan]` and a severity check.

Pre-commit / CI gates

4 commands
gitleaks detect --source . --redact

Find committed secrets. Run as pre-commit hook AND in CI.

Warning: Pre-commit alone is insufficient — developers can skip hooks. CI gate is the enforcement layer.

trivy config <dir>

Scan IaC (Terraform, K8s manifests, Dockerfiles) for misconfigurations.

Production note: Run on every PR that touches infra. Surface findings as PR comments via reviewdog or trivy-action.

kube-linter lint <dir>

Lint Kubernetes manifests for common security issues (privileged, no probes, missing labels).

Production note: Pair with kustomize/Helm rendering in CI so you lint the final manifest, not the template.

osv-scanner scan ./

Scan dependencies against the OSV database. Faster + more comprehensive than language-specific tools.

Production note: OSV covers npm, pypi, go, maven, etc. in one tool. Simplifies dependency-scan toolchain.

Hardened patterns

Common misconfigurations

The unsafe pattern, the replacement, and the reason the two are not equivalent in production.

FIXReview

Risky

# Local keypair signing
cosign sign --key cosign.key <image>

Hardened

# Keyless via OIDC
cosign sign <image>
# (cosign auto-detects CI OIDC token and uses Fulcio)
# In GitHub Actions:
permissions:
  id-token: write

Why it matters: Local cosign keys must be stored, distributed, and rotated — and "cosign.key" leaks find their way into Git or build logs regularly. Keyless signing uses a short-lived cert tied to a verifiable identity (your OIDC issuer + email). No keys, no rotation, much smaller attack surface.

FIXReview

Risky

# Verify image is signed (any signer)
cosign verify <image>

Hardened

# Verify signed BY THE EXPECTED IDENTITY
cosign verify <image> \
  --certificate-identity-regexp=".*@example.com$" \
  --certificate-oidc-issuer=https://accounts.google.com

Why it matters: `cosign verify` without identity flags only checks "is this signed by anyone via Sigstore?" — which is trivially satisfied by an attacker signing their own image. The identity flags pin the expected signer, so only signatures from your CI / your team pass.

FIXReview

Risky

# GitHub Action with default permissions
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: ./build.sh

Hardened

jobs:
  build:
    permissions:
      contents: read
      id-token: write
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@8e5e7e5
      # ^ pinned to a specific SHA
    - run: ./build.sh

Why it matters: Default GITHUB_TOKEN permissions include write to contents, packages, deployments, and more. A compromised dependency in build.sh exfiltrates secrets and rewrites repository history. Explicit minimum permissions + SHA-pinned actions close the most common GitHub Actions supply-chain attacks.

Go deeper