CI/CD pipelines have privileged access to production systems. They pull source code, build artifacts, push to registries, and deploy to clusters. A compromised pipeline is a direct path to production compromise.
CI/CD Threat Vectors
- Poisoned pipeline execution: Malicious PR modifies workflow to exfiltrate secrets
- Dependency confusion: Attacker publishes a package with the same name as an internal one
- Leaked secrets: API keys printed in logs, exposed in artifacts, or committed to Git
- Unpinned actions: Third-party GitHub Actions updated with malicious code
- Over-permissioned runners: CI runners with cluster-admin access
Hardening GitHub Actions
# Secure GitHub Actions workflow
name: Secure Build
on:
push:
branches: [main]
permissions:
contents: read # Minimal! Not write
packages: write # Only for pushing images
id-token: write # For OIDC auth (no static secrets)
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # Pin to exact SHA in production
- name: Secret Scan
run: gitleaks detect --source . --verbose
- name: Build
run: docker build -t myapp .
- name: Sign Image
run: cosign sign ghcr.io/myorg/myapp:latest
- name: Deploy via OIDC (no secrets!)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123:role/deploy
aws-region: us-east-1
# No AWS_ACCESS_KEY_ID! OIDC temporary credentials only