GitHub Actions and SOC 2: Securing Your CI/CD Pipeline for Compliance
Your CI/CD pipeline is in SOC 2 scope. Learn which GitHub Actions security controls map to CC8 change management, how to manage secrets correctly, and how to generate deployment audit evidence.
CI/CD Pipelines Are in SOC 2 Scope
Many engineering teams treat their CI/CD pipeline as infrastructure rather than a control point. For SOC 2, this is incorrect. Your pipeline is the mechanism through which code changes reach production. CC8 (change management) evaluates how you control and authorize changes — and that evaluation includes your deployment process.
If a developer can bypass code review and deploy directly to production, that's a finding. If your pipeline doesn't require approval before production deployments, that's a finding.
CC8 and the Deployment Pipeline
CC8.1 requires that changes are authorized, tested, and reviewed before deployment. In GitHub Actions, this maps to three mechanisms:
The [GitHub branch protection SOC 2 guide](/blog/github-branch-protection-soc2) covers branch protection in depth. This guide focuses on the pipeline and deployment layer.
Environment Protection Rules
GitHub Actions environments let you define protection rules that gate deployments. Configure a production environment with required reviewers:
jobs:
deploy-production:
runs-on: ubuntu-latest
environment:
name: production
url: https://app.example.com
steps:
- name: Deploy to production
run: ./scripts/deploy.sh production
In GitHub repository settings under Environments → production, configure:
- Required reviewers: Engineering managers or ops leads who can approve deployments
- Deployment branches: Restrict to
mainorrelease/*only
When a workflow reaches the production environment step, it pauses and sends a notification to required reviewers. Deployment only proceeds when a reviewer approves. This approval event is logged in the GitHub Actions audit log — every production deployment has a timestamped record of who requested it, who approved it, and what commit was deployed.
Secrets Management: Actions Secrets vs. AWS Secrets Manager
GitHub Actions Secrets are appropriate for automation credentials and non-production configuration. For production secrets — database credentials, API keys, encryption keys — use AWS Secrets Manager with OIDC authentication:
jobs:
deploy:
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials (OIDC — no stored keys)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- name: Retrieve secret from Secrets Manager
run: |
DB_PASS=$(aws secretsmanager get-secret-value --secret-id prod/db/password --query SecretString --output text)
echo "::add-mask::$DB_PASS"
echo "DB_PASS=$DB_PASS" >> $GITHUB_ENV
This approach means no AWS access keys are stored in GitHub (OIDC-based authentication), secret access is logged in AWS CloudTrail, and secrets can be rotated in Secrets Manager without updating GitHub. See [AWS Secrets Manager and SOC 2](/blog/aws-secrets-manager-soc2).
Build Artifact Security
CC6.1 requires that access to systems is restricted to authorized individuals. For a CI/CD pipeline, this extends to build artifacts — container images, compiled binaries. An attacker who can substitute a build artifact can deploy malicious code to production.
Container image signing with cosign:
- name: Sign container image
env:
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY \
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
SBOM generation for supply chain visibility:
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
format: spdx-json
Security Scanning in the Pipeline
CC7.1 requires detection of vulnerabilities. Your CI pipeline is the natural place to run automated scanning:
# SAST
- name: Run CodeQL analysis
uses: github/codeql-action/analyze@v3
with:
languages: javascript, python
# Dependency scanning
- name: Dependency audit
run: npm audit --audit-level=high# Container scanning
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
exit-code: '1'
severity: CRITICAL,HIGH
A critical vulnerability that causes a build to fail — and the subsequent fix — is a documented remediation event that maps to CC7.1.
Audit Log Evidence from GitHub Actions
Export your GitHub organization audit log to a durable store for SOC 2 evidence:
Key events for CC8 audits:
workflows.completed_workflow_run— every workflow run with resultenvironment.approve_deployment_request— production deployment approvalsprotected_branch.policy_override— any branch protection bypass (should be zero)
SOC 2 Controls Checklist for GitHub Actions
- [ ] Production environment has required reviewers configured
- [ ] Branch protection requires pull request reviews before merge
- [ ] Required status checks (tests, security scans) must pass before merge
- [ ] No AWS access keys stored in GitHub Secrets (OIDC instead)
- [ ] Production secrets retrieved from AWS Secrets Manager
- [ ] Container images signed before push to ECR
- [ ] SAST and dependency scanning run on every PR
- [ ] Organization audit log exported to durable storage
- [ ] Alerts configured for branch protection policy changes