GitHub Actions Integration¶
Problem Statement¶
Managing secrets in GitHub Actions workflows presents several challenges:
- Manual secret creation through the GitHub UI is tedious and error-prone
- Difficult to track which secrets exist and when they were created
- No audit trail for secret rotation and updates
- Synchronizing secrets across multiple repositories is time-consuming
- No standardized way to bootstrap secrets for new projects
SecretZero solves this by automating GitHub Actions secret provisioning with a declarative configuration, complete audit trail, and support for repository, environment, and organization-level secrets.
Prerequisites¶
- SecretZero installed with GitHub support:
pip install secretzero[cicd] - GitHub Personal Access Token with appropriate scopes
- Repository admin access (for repository secrets)
- Organization admin access (for organization secrets)
- Environment admin access (for environment-specific secrets)
Authentication Setup¶
1. Generate GitHub Personal Access Token¶
Create a token at GitHub Settings → Developer settings → Personal access tokens:
Required Scopes:
- repo - For repository secrets
- admin:org - For organization secrets (optional)
- workflow - For updating workflow files (optional)
2. Configure Environment¶
For GitHub Enterprise:
Configuration¶
Basic Repository Secrets¶
Create Secretfile.yml:
version: '1.0'
metadata:
project: my-project
owner: engineering-team
variables:
github_org: myorganization
github_repo: myrepo
environment: production
providers:
github:
kind: github
auth:
kind: token
config:
token: ${GITHUB_TOKEN}
# Optional: For GitHub Enterprise
# api_url: ${GITHUB_API_URL}
secrets:
# API Key
- name: api_key
kind: random_string
config:
length: 32
charset: alphanumeric
targets:
# Local development
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
# GitHub Actions repository secret
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
secret_name: API_KEY
# Database Password
- name: database_password
kind: random_password
config:
length: 32
special: true
exclude_characters: '"@/\`'
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
secret_name: DATABASE_PASSWORD
# JWT Secret
- name: jwt_secret
kind: random_string
config:
length: 64
charset: alphanumeric
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
secret_name: JWT_SECRET
Environment-Specific Secrets¶
Scope secrets to GitHub environments (production, staging, etc.):
secrets:
# Production API Key
- name: api_key_prod
kind: random_string
config:
length: 32
charset: alphanumeric
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
environment: production
secret_name: API_KEY
# Staging API Key
- name: api_key_staging
kind: random_string
config:
length: 32
charset: alphanumeric
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
environment: staging
secret_name: API_KEY
Organization-Level Secrets¶
Share secrets across multiple repositories:
secrets:
- name: shared_api_key
kind: random_string
config:
length: 32
charset: alphanumeric
targets:
- provider: github
kind: github_secret
config:
org: ${github_org}
secret_name: SHARED_API_KEY
visibility: all # Options: all, private, selected
# For 'selected' visibility:
# selected_repository_ids: [123456, 789012]
Step-by-Step Instructions¶
1. Validate Configuration¶
secretzero validate
# Expected output:
# ✓ Configuration is valid
# ✓ Found 3 secrets
# ✓ GitHub provider configured correctly
2. Test Provider Connectivity¶
secretzero test
# Expected output:
# ✓ Testing GitHub provider...
# ✓ Authentication successful
# ✓ Repository access confirmed
# ✓ All providers ready
3. Preview Changes¶
secretzero sync --dry-run
# Expected output:
# [DRY RUN] Would create:
# ✓ api_key → GitHub Secret (API_KEY) in myorganization/myrepo
# ✓ database_password → GitHub Secret (DATABASE_PASSWORD) in myorganization/myrepo
# ✓ jwt_secret → GitHub Secret (JWT_SECRET) in myorganization/myrepo
4. Sync Secrets to GitHub¶
secretzero sync
# Expected output:
# ✓ Generated api_key
# ✓ Created GitHub Secret: API_KEY
# ✓ Generated database_password
# ✓ Created GitHub Secret: DATABASE_PASSWORD
# ✓ Generated jwt_secret
# ✓ Created GitHub Secret: JWT_SECRET
# ✓ Updated .gitsecrets.lock
5. Verify in GitHub¶
Check your secrets:
# Via GitHub CLI
gh secret list --repo myorganization/myrepo
# Or visit:
# https://github.com/myorganization/myrepo/settings/secrets/actions
Using Secrets in GitHub Actions¶
Basic Workflow¶
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy application
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
run: |
echo "Deploying with secrets..."
./deploy.sh
Environment-Specific Workflow¶
name: Deploy to Production
on:
release:
types: [published]
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production # Links to environment secrets
steps:
- uses: actions/checkout@v4
- name: Deploy to production
env:
API_KEY: ${{ secrets.API_KEY }} # Uses production environment secret
run: |
echo "Deploying to production..."
./deploy.sh production
Using Organization Secrets¶
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build with shared secrets
env:
SHARED_API_KEY: ${{ secrets.SHARED_API_KEY }} # Organization secret
run: |
echo "Building..."
npm run build
Advanced Scenarios¶
Multi-Repository Setup¶
Sync secrets to multiple repositories:
version: '1.0'
variables:
github_org: myorganization
repositories:
- frontend
- backend
- api
providers:
github:
kind: github
auth:
kind: token
config:
token: ${GITHUB_TOKEN}
secrets:
- name: shared_api_key
kind: random_string
config:
length: 32
targets:
# Sync to frontend repository
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: frontend
secret_name: API_KEY
# Sync to backend repository
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: backend
secret_name: API_KEY
# Sync to API repository
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: api
secret_name: API_KEY
Automatic Secret Rotation in CI/CD¶
Create a workflow to rotate secrets automatically:
# .github/workflows/rotate-secrets.yml
name: Rotate Secrets
on:
schedule:
# Run every 90 days
- cron: '0 0 */90 * *'
workflow_dispatch: # Allow manual trigger
jobs:
rotate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install SecretZero
run: pip install secretzero[cicd]
- name: Rotate secrets
env:
GITHUB_TOKEN: ${{ secrets.ADMIN_GITHUB_TOKEN }}
run: |
secretzero rotate --force
secretzero sync
- name: Commit lockfile changes
run: |
git config user.name "SecretZero Bot"
git config user.email "bot@example.com"
git add .gitsecrets.lock
git commit -m "chore: rotate secrets"
git push
Compliance and Audit Trail¶
Track secret lifecycle with rotation policies:
version: '1.0'
metadata:
project: secure-app
compliance:
- soc2
- iso27001
policies:
critical_rotation:
kind: rotation
require_rotation_period: true
max_age: 90d
severity: error
enabled: true
secrets:
- name: production_api_key
kind: random_string
rotation_period: 90d # Enforce 90-day rotation
config:
length: 32
targets:
- provider: github
kind: github_secret
config:
owner: myorganization
repo: production-app
environment: production
secret_name: API_KEY
Check compliance:
Bootstrap New Projects¶
Automate secret setup for new projects:
#!/bin/bash
# bootstrap-project.sh
PROJECT_NAME=$1
GITHUB_ORG="myorganization"
# Create repository
gh repo create "$GITHUB_ORG/$PROJECT_NAME" --private
# Generate Secretfile
cat > Secretfile.yml << EOF
version: '1.0'
variables:
github_org: $GITHUB_ORG
github_repo: $PROJECT_NAME
providers:
github:
kind: github
auth:
kind: token
config:
token: \${GITHUB_TOKEN}
secrets:
- name: api_key
kind: random_string
config:
length: 32
targets:
- provider: github
kind: github_secret
config:
owner: \${github_org}
repo: \${github_repo}
secret_name: API_KEY
EOF
# Provision secrets
secretzero sync
echo "✓ Project $PROJECT_NAME created with secrets provisioned"
Best Practices¶
1. Use Environment Secrets for Production¶
Protect production secrets with environment protection rules:
targets:
- provider: github
kind: github_secret
config:
owner: myorg
repo: myrepo
environment: production # Requires environment approval rules
secret_name: API_KEY
In GitHub, configure environment protection rules: - Settings → Environments → production → Protection rules - Required reviewers - Wait timer - Deployment branches
2. Rotate Secrets Regularly¶
Implement automatic rotation:
secrets:
- name: api_key
kind: random_string
rotation_period: 90d # Rotate every 90 days
config:
length: 32
targets:
- provider: github
kind: github_secret
config:
owner: myorg
repo: myrepo
secret_name: API_KEY
3. Use Organization Secrets for Shared Resources¶
Centralize shared secrets:
secrets:
- name: npm_token
kind: static
config:
default: ${NPM_TOKEN}
targets:
- provider: github
kind: github_secret
config:
org: myorganization
visibility: private # All private repos
secret_name: NPM_TOKEN
4. Track Changes in Lockfile¶
Commit .gitsecrets.lock for audit trail:
5. Use Descriptive Secret Names¶
Follow naming conventions:
secrets:
- name: production_database_password # Clear and descriptive
targets:
- provider: github
kind: github_secret
config:
secret_name: PRODUCTION_DATABASE_PASSWORD # Matches convention
6. Never Commit GitHub Token¶
Add to .gitignore:
# Never commit tokens
.env
.env.*
**/*token*
**/*secret*
# Commit lockfile for tracking
!.gitsecrets.lock
Troubleshooting¶
Authentication Failed¶
Problem: Error: GitHub authentication failed
Solutions:
# Verify token is set
echo $GITHUB_TOKEN
# Check token scopes
gh auth status
# Regenerate token with correct scopes
# Visit: https://github.com/settings/tokens
# For GitHub Enterprise, verify API URL
export GITHUB_API_URL=https://github.mycompany.com/api/v3
Permission Denied¶
Problem: Error: You do not have permission to set secrets
Solutions:
- Repository secrets require admin access to the repository
- Organization secrets require owner role in the organization
- Environment secrets require admin access to the repository
Secret Not Appearing in Workflow¶
Problem: Workflow can't access the secret
Solutions:
# 1. Check secret name matches exactly (case-sensitive)
env:
API_KEY: ${{ secrets.API_KEY }} # Must match GitHub secret name
# 2. For environment secrets, specify environment in job
jobs:
deploy:
environment: production # Required to access environment secrets
steps:
- name: Use secret
env:
API_KEY: ${{ secrets.API_KEY }}
# 3. Verify secret visibility for organization secrets
# Settings → Secrets → Organization secrets → SHARED_KEY → Repository access
Rate Limiting¶
Problem: Error: API rate limit exceeded
Solutions:
# Use authenticated requests (increases rate limit)
export GITHUB_TOKEN=your_token
# Check current rate limit
curl -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/rate_limit
# Wait for rate limit reset or use multiple tokens
Secrets Not Syncing¶
Problem: secretzero sync completes but secrets don't appear
Solutions:
# 1. Verify provider configuration
secretzero test
# 2. Check lockfile for sync status
cat .gitsecrets.lock
# 3. Force re-sync
secretzero sync --force
# 4. Enable debug logging
secretzero sync --verbose
Complete Example¶
Full production-ready configuration:
version: '1.0'
metadata:
project: production-app
owner: platform-team
compliance:
- soc2
variables:
github_org: mycompany
github_repo: production-app
providers:
github:
kind: github
auth:
kind: token
config:
token: ${GITHUB_TOKEN}
policies:
production_rotation:
kind: rotation
require_rotation_period: true
max_age: 90d
severity: error
enabled: true
secrets:
# Database credentials
- name: database_url
kind: random_password
rotation_period: 90d
config:
length: 32
special: true
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
environment: production
secret_name: DATABASE_URL
# API keys
- name: stripe_api_key
kind: static
rotation_period: 90d
config:
default: ${STRIPE_API_KEY}
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
environment: production
secret_name: STRIPE_API_KEY
# JWT signing key
- name: jwt_secret
kind: random_string
rotation_period: 90d
config:
length: 64
charset: base64
targets:
- provider: github
kind: github_secret
config:
owner: ${github_org}
repo: ${github_repo}
environment: production
secret_name: JWT_SECRET_KEY
Deploy:
# Validate
secretzero validate
# Check compliance
secretzero policy
# Preview
secretzero sync --dry-run
# Deploy
secretzero sync
# Verify
gh secret list --repo mycompany/production-app
Next Steps¶
- GitLab CI/CD - Similar integration for GitLab
- Multi-Cloud Setup - Sync secrets to cloud providers
- Compliance Scenarios - Implement compliance requirements