在工作流中使用机密
执行工作流时,不会自动将机密传递到运行器中。 若要使机密可用于作,必须在工作流文件中将机密设置为输入或环境变量。 为此,可以使用机密上下文,如以下示例所示:
在 GitHub Actions 工作流中有效使用机密需要了解如何安全访问机密、适当应用机密以及在其限制范围内工作。 本部分介绍将机密集成到 CI/CD 工作流的实际模式和技术。
访问工作流中的机密
机密不会自动提供给工作流步骤。 必须使用 secrets 上下文通过输入或环境变量显式公开它们。
基本机密使用模式
name: Database Operations
on: [push]
jobs:
database-operations:
runs-on: ubuntu-latest
steps:
# Method 1: Using secrets as environment variables
- name: Connect to database
env:
DB_USERNAME: ${{ secrets.DB_USERNAME }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
DB_HOST: ${{ secrets.DB_HOST }}
run: |
# Use environment variables safely
psql -h "$DB_HOST" -U "$DB_USERNAME" -d myapp <<EOF
SELECT version();
EOF
# Method 2: Passing secrets to actions as inputs
- name: Deploy application
uses: my-org/deploy-action@v2
with:
api-key: ${{ secrets.DEPLOYMENT_API_KEY }}
environment: production
# Method 3: Using secrets in composite actions
- name: Security scan
uses: security-org/scan-action@v1
with:
token: ${{ secrets.SECURITY_SCAN_TOKEN }}
severity-threshold: "high"
跨 shell 机密用法
jobs:
multi-shell-example:
runs-on: ubuntu-latest
steps:
# Bash shell
- name: Bash operations
shell: bash
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
curl -H "Authorization: Bearer $API_TOKEN" \
https://api.example.com/deploy
# PowerShell
- name: PowerShell operations
shell: pwsh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
$headers = @{ Authorization = "Bearer $env:API_TOKEN" }
Invoke-RestMethod -Uri "https://api.example.com/status" -Headers $headers
# Python script
- name: Python operations
shell: python
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
import os
import requests
token = os.environ['API_TOKEN']
headers = {'Authorization': f'Bearer {token}'}
response = requests.get('https://api.example.com/data', headers=headers)
print(f"Status: {response.status_code}")
高级机密使用模式
条件机密用法
name: Environment-Aware Deployment
on:
push:
branches: [main, develop, "feature/*"]
jobs:
deploy:
runs-on: ubuntu-latest
env:
# Set secrets as job-level environment variables for conditional access
PROD_API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
STAGING_API_KEY: ${{ secrets.STAGING_API_KEY }}
DEV_API_KEY: ${{ secrets.DEVELOPMENT_API_KEY }}
steps:
- name: Determine target environment
id: env
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "target=production" >> $GITHUB_OUTPUT
echo "api_key_var=PROD_API_KEY" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/develop" ]]; then
echo "target=staging" >> $GITHUB_OUTPUT
echo "api_key_var=STAGING_API_KEY" >> $GITHUB_OUTPUT
else
echo "target=development" >> $GITHUB_OUTPUT
echo "api_key_var=DEV_API_KEY" >> $GITHUB_OUTPUT
fi
- name: Deploy to environment
run: |
# Use indirect variable reference
API_KEY_VAR="${{ steps.env.outputs.api_key_var }}"
API_KEY="${!API_KEY_VAR}"
echo "Deploying to ${{ steps.env.outputs.target }} environment"
./deploy.sh --environment=${{ steps.env.outputs.target }} --api-key="$API_KEY"
密钥验证和运行状况检查
name: Secret Health Validation
jobs:
validate-secrets:
runs-on: ubuntu-latest
steps:
- name: Validate required secrets
env:
REQUIRED_SECRETS: |
API_KEY=${{ secrets.API_KEY }}
DATABASE_URL=${{ secrets.DATABASE_URL }}
REDIS_URL=${{ secrets.REDIS_URL }}
run: |
echo "$REQUIRED_SECRETS" | while IFS='=' read -r name value; do
if [ -z "$value" ]; then
echo "ERROR: Missing required secret: $name"
exit 1
else
echo "OK: Secret $name is present"
fi
done
- name: Test secret functionality
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
# Test API key
if curl -f -H "Authorization: Bearer $API_KEY" https://api.example.com/health; then
echo "API key is valid"
else
echo "ERROR: API key validation failed"
exit 1
fi
# Test database connection
if pg_isready -d "$DATABASE_URL"; then
echo "Database connection is healthy"
else
echo "ERROR: Database connection failed"
exit 1
fi
机密组合和转换
name: Complex Secret Handling
jobs:
process-secrets:
runs-on: ubuntu-latest
steps:
- name: Compose configuration from secrets
env:
# Individual secret components
DB_HOST: ${{ secrets.DB_HOST }}
DB_PORT: ${{ secrets.DB_PORT }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASSWORD }}
DB_NAME: ${{ secrets.DB_NAME }}
# SSL certificate components
SSL_CERT: ${{ secrets.SSL_CERTIFICATE }}
SSL_KEY: ${{ secrets.SSL_PRIVATE_KEY }}
SSL_CA: ${{ secrets.SSL_CA_CERTIFICATE }}
run: |
# Create connection string
CONNECTION_STRING="postgresql://$DB_USER:$DB_PASS@$DB_HOST:$DB_PORT/$DB_NAME?sslmode=require"
# Write SSL files securely
echo "$SSL_CERT" > /tmp/client-cert.pem
echo "$SSL_KEY" > /tmp/client-key.pem
echo "$SSL_CA" > /tmp/ca-cert.pem
# Set proper permissions
chmod 600 /tmp/client-key.pem
chmod 644 /tmp/client-cert.pem /tmp/ca-cert.pem
# Use composed configuration
psql "$CONNECTION_STRING" \
--set=sslcert=/tmp/client-cert.pem \
--set=sslkey=/tmp/client-key.pem \
--set=sslrootcert=/tmp/ca-cert.pem \
-c "SELECT version();"
# Clean up sensitive files
rm -f /tmp/client-*.pem /tmp/ca-cert.pem
使用条件逻辑
在条件语句中使用机密
由于无法在条件中 if 直接引用机密,因此使用环境变量作为中介:
name: Conditional Secret Usage
jobs:
conditional-deployment:
runs-on: ubuntu-latest
env:
# Make secrets available as environment variables
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
FEATURE_FLAG_API_KEY: ${{ secrets.FEATURE_FLAG_API_KEY }}
MONITORING_TOKEN: ${{ secrets.MONITORING_TOKEN }}
steps:
- name: Deploy if deployment key exists
if: ${{ env.DEPLOY_KEY != '' }}
run: |
echo "Deploying with available deployment key"
./deploy.sh --key="$DEPLOY_KEY"
- name: Enable feature flags if configured
if: ${{ env.FEATURE_FLAG_API_KEY != '' }}
run: |
echo "Configuring feature flags"
feature-flags configure --api-key="$FEATURE_FLAG_API_KEY"
- name: Setup monitoring if token available
if: ${{ env.MONITORING_TOKEN != '' }}
run: |
echo "Setting up monitoring"
monitoring setup --token="$MONITORING_TOKEN"
- name: Fallback for missing secrets
if: ${{ env.DEPLOY_KEY == '' }}
run: |
echo "WARNING: No deployment key available, running in dry-run mode"
./deploy.sh --dry-run
多个条件处理
name: Multi-Condition Secret Logic
jobs:
smart-deployment:
runs-on: ubuntu-latest
env:
PROD_KEY: ${{ secrets.PRODUCTION_KEY }}
STAGING_KEY: ${{ secrets.STAGING_KEY }}
CANARY_ENABLED: ${{ secrets.CANARY_DEPLOYMENT_ENABLED }}
steps:
- name: Production deployment
if: ${{ github.ref == 'refs/heads/main' && env.PROD_KEY != '' }}
run: |
echo "Production deployment with canary: $CANARY_ENABLED"
if [ "$CANARY_ENABLED" = "true" ]; then
./deploy.sh --environment=production --canary --key="$PROD_KEY"
else
./deploy.sh --environment=production --key="$PROD_KEY"
fi
- name: Staging deployment
if: ${{ github.ref == 'refs/heads/develop' && env.STAGING_KEY != '' }}
run: |
echo "🔧 Staging deployment"
./deploy.sh --environment=staging --key="$STAGING_KEY"
- name: Missing configuration warning
if: ${{ (github.ref == 'refs/heads/main' && env.PROD_KEY == '') || (github.ref == 'refs/heads/develop' && env.STAGING_KEY == '') }}
run: |
echo "WARNING: Missing deployment keys for target environment"
echo "Branch: ${{ github.ref }}"
echo "Production key available: ${{ env.PROD_KEY != '' }}"
echo "Staging key available: ${{ env.STAGING_KEY != '' }}"
工作流中的安全最佳做法
最小化机密公开范围
name: Scoped Secret Access
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
# No secrets needed for checkout
- name: Build application
run: |
npm install
npm run build
# No secrets needed for build
- name: Deploy application (secrets only here)
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
# Secret only available in this specific step
./deploy.sh --token="$DEPLOY_TOKEN"
使用机密进行安全的错误处理
name: Safe Secret Error Handling
jobs:
secure-operations:
runs-on: ubuntu-latest
steps:
- name: Safe secret usage with error handling
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
# Disable bash debugging to prevent secret exposure
set +x
# Capture command output without exposing secrets
if output=$(api-call --key="$API_KEY" 2>&1); then
echo "API call successful"
echo "$output" | grep -v "$API_KEY" # Filter out any secret remnants
else
exit_code=$?
echo "ERROR: API call failed with exit code: $exit_code"
# Don't log the actual error which might contain the secret
echo "Check API key validity and network connectivity"
exit $exit_code
fi
密钥轮换检测
name: Secret Rotation Detection
on:
schedule:
- cron: "0 8 * * *" # Daily at 8 AM
jobs:
check-secret-rotation:
runs-on: ubuntu-latest
steps:
- name: Check secret age and validity
env:
API_KEY: ${{ secrets.API_KEY }}
WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
run: |
# Test current secret validity
if curl -f -H "Authorization: Bearer $API_KEY" https://api.example.com/auth/validate; then
echo "Current API key is valid"
else
echo "ERROR: API key validation failed - rotation may be needed"
# Notify team via webhook
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"text": "🔐 API key rotation needed for repository: ${{ github.repository }}"}'
fi
了解限制和解决方法
机密大小限制
name: Large Secret Handling
jobs:
handle-large-secrets:
runs-on: ubuntu-latest
steps:
# For secrets under 48KB - direct usage
- name: Use normal secret
env:
SMALL_CONFIG: ${{ secrets.APPLICATION_CONFIG }}
run: |
echo "$SMALL_CONFIG" > config.json
# For larger secrets - use encrypted storage
- name: Handle large secret
env:
# Store encryption passphrase as secret (under 48KB)
DECRYPTION_KEY: ${{ secrets.LARGE_SECRET_DECRYPTION_KEY }}
run: |
# Download encrypted large secret from repository
curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-o encrypted-config.enc \
https://api.github.com/repos/${{ github.repository }}/contents/secrets/large-config.enc
# Decrypt using the passphrase secret
openssl enc -aes-256-cbc -d -in encrypted-config.enc -out large-config.json -pass pass:"$DECRYPTION_KEY"
# Use the decrypted configuration
cat large-config.json | jq '.database.connection_string'
# Clean up
rm -f encrypted-config.enc large-config.json
分叉存储库限制
name: Fork-Aware Secret Usage
on: [push, pull_request]
jobs:
secure-ci:
runs-on: ubuntu-latest
steps:
- name: Check if secrets are available
env:
# GITHUB_TOKEN is always available, others may not be in forks
HAS_API_KEY: ${{ secrets.API_KEY != '' }}
run: |
echo "Running in fork: ${{ github.event.pull_request.head.repo.fork }}"
echo "Secrets available: $HAS_API_KEY"
- name: Full integration tests (only for main repo)
if: ${{ !github.event.pull_request.head.repo.fork && secrets.API_KEY != '' }}
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
echo "🔐 Running full integration tests with secrets"
npm run test:integration
- name: Limited tests (for forks)
if: ${{ github.event.pull_request.head.repo.fork || secrets.API_KEY == '' }}
run: |
echo "🔓 Running limited tests without secrets"
npm run test:unit
了解这些模式和限制有助于构建可靠的工作流,这些工作流可安全地处理机密,同时在不同的执行上下文和存储库配置中维护功能。