Articles / Securing the CI/CD Pipeline That Builds Your Images
Containers · CI/CD

Securing the CI/CD Pipeline That Builds Your Images

The CI/CD pipeline is the most privileged system most organisations barely secure. It holds credentials to push to registries, deploy to clusters, and often assume roles in cloud accounts — and it runs arbitrary code from every branch and pull request by design. Compromise the pipeline and an attacker inherits all of that at once, with their malicious output wearing the legitimacy of your own build. Securing it is the capstone of container supply-chain security, because it is the link with the most keys.

The pipeline is uniquely exposed because its whole purpose is to execute untrusted-ish input with trusted credentials. A pull request runs code. A dependency runs install scripts. A build step runs a Makefile. All of it executes inside an environment that can reach your registry, your secrets, and sometimes your production cluster. That combination — arbitrary code plus standing privilege — is exactly what an attacker wants, and it is why pipeline attacks have moved from theory to a regular feature of real incidents.

How pipelines get compromised

runs codeinheritsbuild & signUntrusted inputfork PR, dependency,actionCI jobarbitrary code + secretsStanding credentialsregistry push, cloud roleSigned, provenancedmalicious imageProduction / clusterUntrusted code +standing privilege
The pipeline runs untrusted input with trusted credentials — that is the target.
  • Poisoned pull requests. A workflow that runs build or test steps on PRs from forks executes attacker-controlled code — and if that job has secrets, the attacker exfiltrates them.
  • Dependency and action compromise. A malicious package or a hijacked third-party CI action runs with the job's full permissions. Referencing actions by mutable tag means a maintainer compromise silently changes what runs.
  • Leaked long-lived credentials. Static registry or cloud keys stored as CI secrets get logged, exfiltrated, or reused far from the pipeline.
  • Over-privileged jobs. A single token scoped to everything, shared across every job, so one compromised step owns the lot.
  • Cache and artifact poisoning. Tampering with a shared build cache or artifact store to inject content into later, more-trusted stages.
Why it matters: A pipeline compromise is a supply-chain compromise with your name on it. The malware ships signed, provenanced, and trusted — because your own build system made it. That is why the builder is the link worth defending hardest.

Hardening the pipeline

  • Kill long-lived cloud keys with OIDC. Let the pipeline exchange a short-lived, workload-identity token for scoped cloud access per run. Nothing durable to steal.
  • Least privilege per job. Scope each job's token to exactly what it needs; a test job should not hold registry-push rights. Default deny, grant narrowly.
  • Pin actions and images by digest. Reference third-party actions and build images by immutable hash, not tags, so an upstream compromise cannot silently alter your build.
  • Isolate untrusted triggers. Never expose secrets to workflows triggered by fork PRs; run untrusted code in a separate, unprivileged context.
  • Ephemeral, isolated runners. Fresh, single-use build environments so nothing persists between runs to poison the next one — and prefer isolated builders over sharing a Docker daemon.
  • Protect the branches that deploy. Require review and status checks on the branches wired to production; a merge is a deployment trigger, so guard it like one.
  • Scan, sign, and attest in-pipeline. Fail on critical CVEs, sign the image, and emit provenance — so the pipeline both produces and vouches for a verifiable artifact.
Short-lived cloud access via OIDC — no stored keyspermissions:
  id-token: write  # request an OIDC token for this run
  contents: read
steps:
  - uses: aws-actions/configure-aws-credentials@v4.0.2
    with: { role-to-assume: arn:aws:iam::...:role/ci-push, aws-region: us-east-1 }
    # no static access key anywhere — the role is assumed for minutes

Guarding the guard

The pipeline also generates the very provenance and signatures that downstream admission control trusts. That makes it the root of that trust — and roots of trust must themselves be defended, or every guarantee built on them is hollow. A signature from a compromised builder is worse than no signature, because it is a lie that verifies. Hardening CI/CD is therefore not one control among many; it is the foundation the rest of the supply-chain story stands on.

As ever, the foundation only holds where you can see it. Ad-hoc pipelines, a developer's personal automation with production push rights, or a forgotten runner with standing credentials are privileged systems operating outside your security program entirely. You can't harden a build pipeline you haven't discovered — and it is exactly the one you forgot about that an attacker is counting on.

Test for it — in practice
  • Skim your CI secrets for long-lived cloud or registry keys that could be swapped for short-lived OIDC tokens instead.
  • Check whether any workflow that runs on fork pull requests has access to secrets — untrusted code plus standing credentials is the classic pipeline compromise.

A quick look finds the obvious keys; auditing every pipeline for over-privileged jobs and unsafe triggers — including the ad-hoc ones — is what our assessment runs for you.

Keep reading
Container Image Scanning and the SBOM