Securing the CI/CD Pipeline Itself
Your pipeline has production credentials, can deploy anywhere, and runs code from every contributor. It's one of the most powerful — and most overlooked — attack surfaces you own.
We spend a lot of energy scanning the code that flows through the pipeline and far less securing the pipeline itself. That's backwards. A CI/CD system holds production credentials, can deploy to every environment, and executes arbitrary code on every push. Compromise it once and you don't need to compromise anything else. Recent supply-chain incidents have made this concrete: the build system is a primary target, not an afterthought.
Start with secrets
The most common pipeline weakness is also the most preventable: secrets sitting in plaintext. Hard-coded tokens in .gitlab-ci.yml, long-lived cloud keys stored as CI variables, credentials echoed into build logs.
The fixes, in order of impact:
- Eliminate long-lived credentials. Use OIDC federation so the pipeline exchanges a short-lived identity token for temporary cloud credentials. No static AWS keys to leak.
- Centralize what's left. Pull genuine secrets from a vault at runtime, scoped per job, never persisted.
- Scan for leaks. Run a secrets scanner in the pipeline so a committed credential fails the build instead of reaching history.
# OIDC-based credentials — no static keys stored in CI
deploy:
id_tokens:
AWS_TOKEN:
aud: https://sts.amazonaws.com
script:
- aws sts assume-role-with-web-identity \
--role-arn "$DEPLOY_ROLE_ARN" \
--web-identity-token "$AWS_TOKEN" \
--role-session-name ci-deploy
Apply least privilege to the pipeline
A build job that only needs to push an image to a registry should not hold a role that can delete an RDS cluster. Scope each job's permissions to exactly what it does. In practice this means separate roles for build, test, and deploy stages, and separate credentials per environment. If a test job is compromised by a malicious dependency, the blast radius is contained.
This is the same RBAC discipline that pays off in Kubernetes: access you don't grant can't be abused. When I hardened cluster RBAC to least privilege, unauthorized-access incidents dropped by 50% — and the same principle applies to every identity in your delivery chain.
Protect the integrity of what you build
Securing inputs and credentials still leaves the question: can you trust the artifact that comes out? Two practices close the gap:
- Pin and verify dependencies. Lock versions and verify checksums so a dependency can't silently change under you.
- Sign your artifacts. Sign images and generate a software bill of materials (SBOM) at build time, then verify signatures at deploy time so only artifacts your pipeline actually produced can run in production.
Make the pipeline reproducible
Finally, treat the pipeline definition as code subject to the same review as everything else. Protected branches, required reviews on workflow files, and infrastructure-as-code for the runners themselves. A pipeline you can rebuild from version control is a pipeline you can reason about, audit, and recover. The build system deserves at least the same rigor as the code it ships — usually more.