DevSecOps

CI/CD Supply-Chain Security

Control identity, artifacts, secrets, and promotion policy along the path code already travels. A scanner report is not a security program.

The chain is only as strong as the privileged job

Most delivery incidents that look like “security” are identity and promotion failures: a long-lived key, a workflow anyone can edit, a mutable tag, or a finding list with no owner. Adding another scanner after those are wrong will not help.

This guide uses a hypothetical GitHub Actions → OCI registry → Kubernetes path. It is educational, not a claim about a production program or a certification.

Separate untrusted checks from privileged delivery

Pull requests can run unit tests, formatters, and dependency review without cloud credentials. The job that publishes an image or applies infrastructure should run from a protected branch or a GitHub Environment with reviewers, using an identity that cannot be assumed from a fork pull request.

YAML · EDUCATIONAL EXAMPLE
# Educational workflow fragment. Pin actions by digest in high-risk repos.
name: publish
on:
  push:
    branches: [main]
permissions:
  contents: read
  id-token: write
jobs:
  publish:
    runs-on: ubuntu-latest
    environment: staging
    permissions:
      contents: read
      id-token: write
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::111122223333:role/example-gha-ecr
          aws-region: eu-west-1
      - run: ./scripts/build-and-push.sh

id-token: write is required for OIDC. It is also a privilege: any step in that job can request a token. Keep the job small. The IAM role’s trust policy must restrict sub to the repository and environment, not repo:example/api:*.

OIDC is not secure merely because it is enabled. A trust policy that matches every ref in the repository is a stored key with extra steps.

Pin what you consume

Third-party Actions resolved by a moving tag can change under you. For workflows that deploy or handle secrets, pin by commit SHA. The same rule applies to base images: build FROM example@sha256:… or a lock file, not FROM node:latest.

An SBOM and provenance attestation help later incident response. They do not replace access review. Treat them as evidence, not as a pass/fail halo.

Scan before publish; enforce at promotion

Build-time scans of dependencies and images catch issues when they are cheap to fix. Promotion policy decides what may enter staging and production.

A useful policy talks about exploitability and exposure, not every CVE:

  • Block critical findings that are known-exploitable and reachable on an internet-facing image.
  • Give lower findings an owner and a deadline.
  • Record exceptions with expiry. Infinite exceptions are disabled policy.
TEXT · EDUCATIONAL EXAMPLE
# Educational gate sketch. Replace with your scanner's exit contract.
if [ "$CRITICAL_EXPLOITABLE_EXPOSED" -gt 0 ]; then
  echo "promotion blocked: reachable critical finding"
  exit 1
fi

A gate that fails the build on every informational finding will be bypassed. A gate that never fails will not change behavior.

Secrets and revocation

Secrets belong in a manager or an environment store, injected at runtime. They do not belong in Git, in echo debug steps, or in image layers.

A secrets scan that finds a token is the start of the incident. Revoke the credential, rotate dependents, and close the leak path (workflow log, forked PR, chat paste). A scanner checkbox without revocation is theatre.

Runtime identity

The deploy role should push to one registry repository and reconcile one namespace—or apply one Terraform workspace—not administer the account. The application’s runtime identity (IRSA, workload identity) should be similarly narrow.

Admission policy can require non-root, known registries, and resource requests. Know whether a failed policy webhook fails open or closed; that choice is an availability decision.

Troubleshooting the control plane of delivery

  • Job assumed the wrong role: inspect the OIDC sub and the trust policy, not only aws sts get-caller-identity after the fact.
  • Scan “passed” but production is vulnerable: you scanned a different digest than you deployed, or the policy ignored exposure.
  • Secret still valid after a leak: rotation did not include every consumer.
  • Deploy blocked by admission: read the webhook message before widening the policy to “allow all.”

Common failure modes

  • Access keys in repository secrets “until OIDC is ready.”
  • Cluster-admin service accounts behind a green image scan.
  • Mutable :latest tags in production manifests.
  • Workflow files anyone on the default branch can edit without review.
  • Three scanners, no owner, no clock.

Reliable GitHub Actions covers build-once promotion and verification. Multi-environment isolation covers the account and state boundaries those identities must not cross. See DevSecOps, DevOps & CI/CD, and the CI/CD lab.

Related technical guides

LET’S BUILD WHAT’S NEXT

Good engineering starts with a conversation.

Let’s connect