How to Secure Your CI/CD Pipeline: Vulnerability Scanning and Access Control
Harden your CI/CD pipeline against supply chain attacks. Covers runner security, artifact signing, RBAC, pipeline secrets management, and audit logging.
Your CI/CD pipeline has more access than any developer. It touches your source code, builds your binaries, handles your secrets, and deploys to production. If an attacker compromises your pipeline, they own everything.
Step 1: Secure Pipeline Secrets
1.1 Never Store Secrets in Code
# ❌ NEVER do this
env:
DATABASE_URL: "postgres://admin:P@ssw0rd@prod-db:5432/main"
# ✅ Use GitHub Secrets or a vault
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
1.2 Use Environment-Scoped Secrets
# GitHub Actions — environment protection
jobs:
deploy-prod:
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
steps:
- name: Deploy
env:
# These secrets are only accessible when deploying to 'production'
AWS_ACCESS_KEY: ${{ secrets.PROD_AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.PROD_AWS_SECRET_KEY }}
run: ./deploy.sh
1.3 Rotate Secrets Automatically
# AWS Secrets Manager — automatic rotation
aws secretsmanager rotate-secret \
--secret-id prod/database \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456:function:rotate-db-secret \
--rotation-rules '{"AutomaticallyAfterDays": 30}'
Step 2: Lock Down Pipeline Permissions
2.1 GitHub Actions OIDC (No Long-lived Credentials)
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: us-east-1
# No access keys needed! Uses OIDC federation
2.2 Least Privilege for Pipeline Service Accounts
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"
],
"Resource": "arn:aws:ecr:us-east-1:123456:repository/myapp"
},
{
"Effect": "Allow",
"Action": ["ecs:UpdateService"],
"Resource": "arn:aws:ecs:us-east-1:123456:service/myapp-prod/*"
}
]
}
Step 3: Verify Build Integrity
3.1 Sign Your Artifacts
# Cosign — sign container images
cosign sign --key cosign.key myregistry.com/myapp:v1.2.3
# Verify before deployment
cosign verify --key cosign.pub myregistry.com/myapp:v1.2.3
3.2 Generate SBOM (Software Bill of Materials)
# Syft — generate SBOM
syft myregistry.com/myapp:v1.2.3 -o spdx-json > sbom.json
# Grype — scan SBOM for vulnerabilities
grype sbom:sbom.json --fail-on high
Step 4: Protect Branch and Deployment Rules
# Branch protection via GitHub API
gh api repos/{owner}/{repo}/branches/main/protection \
--method PUT \
--field required_status_checks='{"strict":true,"contexts":["security-scan","tests"]}' \
--field enforce_admins=true \
--field required_pull_request_reviews='{"required_approving_review_count":2,"dismiss_stale_reviews":true}' \
--field restrictions=null
Required Checks Before Merge
| Check | Purpose | Blocks Merge? |
|---|---|---|
| Unit Tests | Code correctness | ✅ Yes |
| SAST Scan | Code vulnerabilities | ✅ Yes |
| Dependency Audit | Known CVEs | ✅ Yes (high/critical) |
| Container Scan | Image vulnerabilities | ✅ Yes |
| Code Review | Human approval (2+) | ✅ Yes |
| License Check | OSS compliance | ⚠️ Warning only |
Step 5: Monitor Pipeline Activity
audit-logging:
runs-on: ubuntu-latest
steps:
- name: Log Deployment
run: |
curl -X POST "${{ secrets.AUDIT_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d '{
"event": "deployment",
"environment": "production",
"commit": "${{ github.sha }}",
"actor": "${{ github.actor }}",
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"workflow": "${{ github.workflow }}",
"run_id": "${{ github.run_id }}"
}'
Pipeline Security Checklist
- No secrets in source code (use vault/environment secrets)
- Environment-scoped secrets with approval gates
- OIDC federation (no long-lived credentials)
- Least-privilege service account permissions
- Container image signing (Cosign)
- SBOM generation and scanning
- Branch protection with required checks
- 2+ reviewer requirement for production deployments
- Audit logging for all pipeline activities
- Self-hosted runner hardening (if applicable)
:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For DevOps maturity assessments, visit garnetgrid.com. :::