Securing Secrets in a CI/CD Pipeline
Securing Secrets in a CI/CD Pipeline
The Problem with Secrets
Here’s a scary truth: secrets are the #1 cause of breaches in modern software teams. API keys hardcoded in a .env file, passwords sitting in a GitHub repo, tokens copy-pasted into a pipeline config — it happens more than anyone admits.
A CI/CD pipeline is particularly risky because it touches everything — your source code, your cloud infrastructure, your production environment. If an attacker gets into your pipeline, they’ve essentially got the keys to the kingdom.
So how do you do it right?
Rule #1 — Never, Ever Hardcode Secrets
This sounds obvious, but it’s violated constantly.
bash
# ❌ The wrong way — spotted in the wild, more than you'd think
DB_PASSWORD="supersecret123"
AWS_SECRET_KEY="AKIAIOSFODNN7EXAMPLE"The moment that hits a Git commit, it’s permanent — even if you delete it later, it lives in Git history. Scanners like truffleHog and git-secrets exist specifically to find these.
The fix? Treat secrets like radioactive material. They should never touch your codebase.
Use a Secrets Manager
This is the gold standard. Instead of storing secrets in files or env vars, your pipeline fetches them at runtime from a dedicated vault.
Popular options:
- HashiCorp Vault — battle-tested, very flexible
- AWS Secrets Manager / Parameter Store — great if you’re on AWS
- Azure Key Vault / GCP Secret Manager — same idea, different cloud
- Doppler — developer-friendly, works across clouds
yaml
# ✅ The right way — fetch at runtime, never store
- name: Get DB password
run: |
SECRET=$(aws secretsmanager get-secret-value \
--secret-id prod/db/password --query SecretString)The secret is fetched, used, and gone. It never lives in your repo or your logs.
Use Your CI/CD Platform’s Built-in Secret Storage
Every major CI/CD tool has native secret management — use it.
- GitHub Actions → Repository Secrets (Settings → Secrets)
- GitLab CI → CI/CD Variables (masked + protected)
- CircleCI → Environment Variables (masked in logs)
- Jenkins → Credentials Manager
yaml
# GitHub Actions example
- name: Deploy
env:
API_KEY: ${{ secrets.PROD_API_KEY }} # ✅ Injected, never exposed
run: ./deploy.shThese secrets are masked in logs — even if someone accidentally prints them, they show up as ***. Not perfect, but a solid safety net.
Rotate Secrets Regularly
Static secrets are ticking time bombs. If a key leaked 6 months ago and you never rotated it, you might already be compromised and not know it.
Best practices:
- Short-lived tokens over long-lived ones wherever possible
- Automate rotation using your secrets manager’s built-in scheduling
- OIDC (OpenID Connect) for cloud auth — your pipeline gets a temporary token per run, no static credentials needed at all
yaml
# GitHub Actions OIDC — no AWS keys stored anywhere ✅
- uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActions
aws-region: us-east-1This is the modern approach — zero stored credentials.
Least Privilege — Give Pipelines Only What They Need
Your CI pipeline probably doesn’t need admin access to your entire AWS account. Lock it down.
- A build pipeline needs to read source and push artifacts — nothing else
- A deploy pipeline needs access to one environment — not all of them
- Separate staging and production secrets entirely
If a pipeline gets compromised, least privilege is the difference between “an attacker touched our dev environment” and “an attacker wiped our production database.”
Scan for Leaked Secrets Automatically
Add secret scanning to your pipeline so mistakes get caught before they ship.
yaml
- name: Scan for secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: mainTools like TruffleHog, Gitleaks, and detect-secrets run in seconds and can block a merge if they find something suspicious.
The Simple Mental Model
Think of secrets like house keys:
- You don’t write them on the front door (no hardcoding)
- You keep them in a secure lockbox (secrets manager)
- You give copies to only who needs them (least privilege)
- You change the locks periodically (rotation)
- You have a camera at the door to catch anyone who copies them (secret scanning)
Do all five, and your pipeline goes from a liability to a fortress.