创建加密的机密
GitHub Actions 工作流通常需要访问敏感信息,例如 API 密钥、密码、证书和令牌。 GitHub 提供加密的机密,用于安全地存储和访问此敏感数据,而无需在代码或工作流文件中公开这些数据。
了解 GitHub 机密
GitHub 机密是可在 GitHub 组织中不同级别创建的加密环境变量。 创建后,机密将加密,只能在授权上下文中的工作流执行期间解密。
GitHub 机密的关键特征:
- 加密存储:所有机密都使用行业标准加密进行加密
- 受控访问:只有经过授权的工作流才能访问机密
- 日志中屏蔽:机密值在工作流日志中自动屏蔽
- 不可变:创建后,无法查看机密值,仅替换
机密范围和层次结构
存储库级密钥
存储库机密仅适用于该特定存储库中的工作流:
# Using repository secret in workflow
name: Deploy Application
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_CONNECTION_STRING }}
run: |
echo "Deploying with API key starting with: ${API_KEY:0:8}..."
./deploy.sh
组织级机密
组织机密可以在具有受控访问权限的多个存储库之间共享:
# Organization secret with repository access control
name: Shared CI Pipeline
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run integration tests
env:
# This secret is available to authorized repositories
SHARED_TEST_API_KEY: ${{ secrets.INTEGRATION_TEST_API_KEY }}
run: |
npm test -- --api-key="$SHARED_TEST_API_KEY"
环境级机密
环境机密为部署环境提供精细的控制:
name: Multi-Environment Deploy
on:
push:
branches: [main, develop]
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
env:
# Environment-specific secrets
DEPLOYMENT_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
API_ENDPOINT: ${{ secrets.STAGING_API_URL }}
run: ./deploy.sh staging
deploy-production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to production
env:
DEPLOYMENT_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}
API_ENDPOINT: ${{ secrets.PRODUCTION_API_URL }}
run: ./deploy.sh production
创建和管理机密
存储库机密设置
导航到存储库设置:
- 转到 GitHub 上的存储库
- 单击 “设置 ”选项卡
- 选择 机密和变量>操作
创建新的存储库机密:
Name: PRODUCTION_API_KEY Value: your-actual-api-key-value在工作流中使用:
env: API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
组织机密管理
# Example of organization secret usage with access policies
name: Organization-wide CI
on: [push]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- name: Security vulnerability scan
env:
# Organization secret with controlled repository access
SECURITY_SCAN_TOKEN: ${{ secrets.ORG_SECURITY_SCAN_TOKEN }}
run: |
security-scanner --token="$SECURITY_SCAN_TOKEN" .
组织机密访问策略:
- 所有存储库:可用于组织中的所有存储库
- 专用存储库:仅适用于专用存储库
- 所选存储库:仅适用于专门选择的存储库
使用保护规则的环境机密
name: Protected Production Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.production.com
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy with environment protection
env:
# Protected by environment rules (approvals, wait timers)
PROD_DEPLOY_TOKEN: ${{ secrets.PRODUCTION_DEPLOY_TOKEN }}
PROD_DB_PASSWORD: ${{ secrets.PRODUCTION_DATABASE_PASSWORD }}
run: |
echo "Deploying to production environment..."
./scripts/deploy-production.sh
机密信息的安全最佳实践
机密命名约定
# Good: Clear, descriptive names
secrets:
PRODUCTION_API_KEY
STAGING_DATABASE_URL
AWS_ACCESS_KEY_ID
AZURE_CLIENT_SECRET
DOCKER_REGISTRY_TOKEN
# Avoid: Vague or generic names
secrets:
KEY
PASSWORD
TOKEN
SECRET
最低特权原则
# Good: Specific secrets for specific purposes
name: Database Migration
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- name: Run database migration
env:
# Read-only database connection for migrations
DB_MIGRATION_URL: ${{ secrets.DB_MIGRATION_CONNECTION }}
run: |
migrate up --database-url="$DB_MIGRATION_URL"
backup:
runs-on: ubuntu-latest
steps:
- name: Create database backup
env:
# Backup-specific credentials with limited scope
BACKUP_ACCESS_KEY: ${{ secrets.DB_BACKUP_ACCESS_KEY }}
run: |
backup-db --credentials="$BACKUP_ACCESS_KEY"
机密轮换和生命周期管理
name: Secret Health Check
on:
schedule:
- cron: "0 6 * * 1" # Weekly on Monday at 6 AM
jobs:
check-secret-health:
runs-on: ubuntu-latest
steps:
- name: Test API key validity
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
run: |
# Test if API key is still valid
response=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $API_KEY" \
https://api.example.com/health)
if [ "$response" != "200" ]; then
echo "API key may be expired or invalid"
# Create issue or notify team
gh issue create --title "API Key Health Check Failed" \
--body "The production API key failed health check. Response code: $response"
else
echo "API key is healthy"
fi
条件机密用法
name: Flexible Secret Usage
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run tests
env:
# Use different secrets based on event type
API_KEY: ${{ github.event_name == 'push' && secrets.INTEGRATION_API_KEY || secrets.TESTING_API_KEY }}
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.PROD_DB_URL || secrets.TEST_DB_URL }}
run: |
echo "Running tests with appropriate credentials..."
npm test
高级机密模式
多值机密 (JSON 配置)
name: Complex Configuration
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Parse complex secret
env:
# Store complex configuration as JSON in secret
AWS_CONFIG: ${{ secrets.AWS_DEPLOYMENT_CONFIG }}
run: |
# Parse JSON secret
echo "$AWS_CONFIG" | jq -r '.access_key_id' > /tmp/aws_key
echo "$AWS_CONFIG" | jq -r '.secret_access_key' > /tmp/aws_secret
echo "$AWS_CONFIG" | jq -r '.region' > /tmp/aws_region
# Configure AWS CLI
aws configure set aws_access_key_id "$(cat /tmp/aws_key)"
aws configure set aws_secret_access_key "$(cat /tmp/aws_secret)"
aws configure set default.region "$(cat /tmp/aws_region)"
# Clean up temporary files
rm -f /tmp/aws_*
密钥继承和组合
name: Composed Secrets
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Build connection string
env:
DB_HOST: ${{ secrets.DATABASE_HOST }}
DB_USER: ${{ secrets.DATABASE_USER }}
DB_PASS: ${{ secrets.DATABASE_PASSWORD }}
DB_NAME: ${{ secrets.DATABASE_NAME }}
run: |
# Compose connection string from individual secrets
CONNECTION_STRING="postgresql://$DB_USER:$DB_PASS@$DB_HOST:5432/$DB_NAME"
# Use composed string (never log it)
echo "Connecting to database..."
psql "$CONNECTION_STRING" -c "SELECT version();"
机密验证和测试
name: Secret Validation
jobs:
validate-secrets:
runs-on: ubuntu-latest
steps:
- name: Validate API credentials
env:
API_KEY: ${{ secrets.API_KEY }}
API_SECRET: ${{ secrets.API_SECRET }}
run: |
# Test API credentials without exposing values
if [ -z "$API_KEY" ] || [ -z "$API_SECRET" ]; then
echo "Missing required API credentials"
exit 1
fi
# Test key format (without revealing the key)
if [[ ${#API_KEY} -lt 32 ]]; then
echo "API key appears to be invalid (too short)"
exit 1
fi
# Test authentication
response=$(curl -s -w "%{http_code}" -o /dev/null \
-H "Authorization: Bearer $API_KEY" \
https://api.example.com/auth/test)
if [ "$response" = "200" ]; then
echo "API credentials validated successfully"
else
echo "API credential validation failed (HTTP $response)"
exit 1
fi
常见陷阱和安全注意事项
避免机密泄露
# DON'T: Never echo or log secrets directly
- name: Bad secret usage
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
echo "Using API key: $API_KEY" # This will expose the secret!
# DO: Use secrets safely without exposure
- name: Safe secret usage
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
# Use the secret without logging it
curl -H "Authorization: Bearer $API_KEY" https://api.example.com/data
echo "API request completed successfully"
正确的错误处理
- name: Secure error handling
env:
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
run: |
# Set error handling to avoid secret leaks
set +x # Disable command echoing
if ! psql "postgresql://user:$DATABASE_PASSWORD@host/db" -c "SELECT 1"; then
# Log error without exposing secret
echo "Database connection failed"
exit 1
fi
echo "Database connection successful"
机密范围管理
# Good: Limit secret scope to specific steps
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
# No secrets available here
- name: Deploy application
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} # Secret only in this step
run: |
./deploy.sh
- name: Run post-deploy tests
# No secrets available here
run: |
./test.sh
适当的机密管理对于维护 CI/CD 管道的安全性至关重要。 始终遵循最低特权原则,使用描述性命名,并实现适当的验证,以确保机密在启用强大的自动化功能的同时保持安全。