Secrets Management & Infrastructure Security
GitHub Secrets & OIDC Authentication
4 min read
GitHub provides built-in secrets management for CI/CD. Combined with OIDC (OpenID Connect), you can eliminate long-lived credentials entirely.
GitHub Secrets Overview
| Secret Type | Scope | Use Case |
|---|---|---|
| Repository secrets | Single repo | API keys, deploy tokens |
| Environment secrets | Specific environment | Production vs staging |
| Organization secrets | Multiple repos | Shared credentials |
Managing Repository Secrets
Via GitHub UI
- Go to Settings → Secrets and variables → Actions
- Click New repository secret
- Enter name (e.g.,
AWS_ACCESS_KEY_ID) and value
Via GitHub CLI
# Set a secret
gh secret set AWS_ACCESS_KEY_ID
# Set from file
gh secret set AWS_SECRET_ACCESS_KEY < secret.txt
# List secrets
gh secret list
# Delete secret
gh secret delete AWS_ACCESS_KEY_ID
Using Secrets in Workflows
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: aws s3 sync ./dist s3://my-bucket
Environment-Specific Secrets
# Different secrets per environment
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Uses production secrets
steps:
- name: Deploy
run: ./deploy.sh
env:
API_KEY: ${{ secrets.API_KEY }} # Production API key
Configure environments in Settings → Environments:
- Add required reviewers
- Set deployment branch restrictions
- Add environment-specific secrets
OIDC: The Future of CI/CD Authentication
OIDC eliminates static credentials by using short-lived tokens:
┌──────────────────┐ ┌─────────────────┐
│ GitHub Actions │ │ Cloud Provider │
│ Workflow │ │ (AWS, GCP, etc) │
└────────┬─────────┘ └────────┬────────┘
│ │
│ 1. Request OIDC token │
│ ─────────────────────▶ │
│ │
│ 2. Verify with GitHub │
│ ◀───────────────────── │
│ │
│ 3. Issue short-lived │
│ credentials │
│ ◀───────────────────── │
│ │
│ 4. Use credentials │
│ ─────────────────────▶ │
│ │
OIDC with AWS
# No static credentials needed!
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
# No aws-access-key-id or aws-secret-access-key!
- name: Deploy
run: aws s3 sync ./dist s3://my-bucket
AWS IAM Role Setup
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
}
}
}
]
}
OIDC with Google Cloud
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/github/providers/github'
service_account: 'github-actions@my-project.iam.gserviceaccount.com'
- name: Deploy to Cloud Run
run: gcloud run deploy my-service --image gcr.io/my-project/my-app
Secrets Security Best Practices
1. Never Log Secrets
# GitHub automatically masks secrets, but be careful
- name: Debug
run: |
# BAD: Could leak in error messages
curl -H "Authorization: Bearer ${{ secrets.TOKEN }}" https://api.example.com
# BETTER: Use environment variables
curl -H "Authorization: Bearer $TOKEN" https://api.example.com
env:
TOKEN: ${{ secrets.TOKEN }}
2. Limit Secret Scope
# Only expose secrets to steps that need them
jobs:
build:
steps:
- name: Build
run: npm run build
# No secrets here - just building
deploy:
needs: build
environment: production
steps:
- name: Deploy
run: ./deploy.sh
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
3. Use Environment Protection
# Require approval for production deployments
jobs:
deploy:
environment:
name: production
url: https://myapp.com
# Reviewers must approve before job runs
4. Rotate Secrets Regularly
# Automate secret rotation
gh secret set API_KEY --body "$(generate-new-key)"
# Or use OIDC to avoid rotation entirely
Comparison: GitHub Secrets vs Vault
| Aspect | GitHub Secrets | HashiCorp Vault |
|---|---|---|
| Complexity | Simple | Complex |
| Dynamic secrets | No | Yes |
| Audit logging | Basic | Comprehensive |
| Multi-cloud | GitHub only | Any platform |
| Best for | GitHub-native workflows | Enterprise, multi-cloud |
Next, we'll cover Infrastructure as Code security scanning. :::