Skip to content
On this page

GitHub Actions密钥管理

密钥管理是GitHub Actions安全的核心组成部分,用于存储和管理敏感信息,如API密钥、密码、证书等。本文档将详细介绍如何安全地管理密钥以及最佳实践。

密钥基础概念

什么是密钥(Secrets)

密钥是加密存储的敏感值,只能在GitHub Actions工作流中访问。密钥在日志中会被自动屏蔽,不会以明文形式显示。

yaml
# .github/workflows/example.yml
name: Example with Secrets

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Use secret in step
        run: |
          echo "Using API key for deployment..."
          # 实际使用中,密钥不会在日志中显示
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}

密钥的加密和存储

GitHub使用AES-256加密算法对密钥进行加密存储,并且只有在工作流执行时才解密。密钥在传输过程中使用TLS加密。

创建和管理密钥

在仓库设置中创建密钥

密钥可以通过GitHub界面创建:

  1. 进入仓库设置(Settings)
  2. 选择"Secrets and variables"
  3. 选择"Actions"
  4. 点击"New repository secret"
  5. 输入密钥名称和值

使用GitHub CLI创建密钥

bash
# 安装GitHub CLI后
gh secret set API_KEY -b"your-api-key-value"
gh secret set DATABASE_URL -b"postgresql://user:pass@host:port/db"
gh secret set SSH_PRIVATE_KEY -b"$(cat ~/.ssh/id_rsa)"

在工作流中使用密钥

yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy using secrets
        run: |
          # 使用密钥进行部署操作
          curl -X POST \
            -H "Authorization: Bearer ${{ secrets.API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"app":"my-app","version":"1.0.0"}' \
            https://api.example.com/deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}

密钥作用域

仓库级密钥

仓库级密钥只能在特定仓库中使用:

yaml
# .github/workflows/repo-specific.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Use repository secret
        run: echo "Using repo-specific secret"
        env:
          REPO_SECRET: ${{ secrets.REPO_SECRET }}

环境级密钥

环境级密钥只在特定环境中可用:

yaml
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging  # 环境级密钥在此环境中可用
    steps:
      - name: Deploy to staging
        run: echo "Deploying with environment secrets"
        env:
          STAGING_API_KEY: ${{ secrets.STAGING_API_KEY }}
  
  deploy-production:
    runs-on: ubuntu-latest
    environment: production  # 环境级密钥在此环境中可用
    steps:
      - name: Deploy to production
        run: echo "Deploying with production secrets"
        env:
          PROD_API_KEY: ${{ secrets.PROD_API_KEY }}

组织级密钥

组织级密钥可以在组织的所有仓库中使用(需要适当权限):

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Use organization secret
        run: echo "Using organization-wide secret"
        env:
          ORG_SECRET: ${{ secrets.ORG_SECRET }}

密钥安全最佳实践

1. 最小权限原则

只为工作流提供必需的密钥:

yaml
# 好的做法:只提供必需的密钥
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build only
        run: npm run build
        # 不需要任何密钥,因为构建不需要敏感信息

  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy with minimal secrets
        run: ./deploy.sh
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}  # 只提供部署必需的密钥

2. 密钥轮换

定期更换密钥:

yaml
# 在密钥描述中记录轮换日期
# "API_KEY - Last rotated: 2023-12-01, next rotation: 2024-03-01"

jobs:
  security-check:
    runs-on: ubuntu-latest
    steps:
      - name: Check secret age
        run: |
          # 检查密钥是否需要轮换的逻辑
          echo "Checking if secrets need rotation..."

3. 密钥验证

在使用密钥前验证其存在性:

yaml
jobs:
  validate-secrets:
    runs-on: ubuntu-latest
    steps:
      - name: Validate required secrets
        run: |
          if [ -z "$API_KEY" ]; then
            echo "Error: API_KEY is not set"
            exit 1
          fi
          
          if [ -z "$DATABASE_PASSWORD" ]; then
            echo "Error: DATABASE_PASSWORD is not set"
            exit 1
          fi
          
          echo "All required secrets are present"
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}

密钥类型和用途

API密钥

yaml
jobs:
  api-call:
    runs-on: ubuntu-latest
    steps:
      - name: Make API call
        run: |
          curl -X GET \
            -H "Authorization: Bearer ${{ secrets.API_KEY }}" \
            -H "Content-Type: application/json" \
            https://api.example.com/data

数据库凭证

yaml
jobs:
  database-operation:
    runs-on: ubuntu-latest
    steps:
      - name: Connect to database
        run: |
          # 使用数据库凭证连接数据库
          psql -h ${{ secrets.DB_HOST }} \
               -U ${{ secrets.DB_USER }} \
               -d ${{ secrets.DB_NAME }} \
               -c "SELECT version();"
        env:
          PGPASSWORD: ${{ secrets.DB_PASSWORD }}

SSH密钥

yaml
jobs:
  deploy-via-ssh:
    runs-on: ubuntu-latest
    steps:
      - name: Setup SSH key
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H "${{ secrets.HOST }}" >> ~/.ssh/known_hosts
          
          # 使用SSH进行部署
          ssh ${{ secrets.USER }}@${{ secrets.HOST }} "cd ${{ secrets.REMOTE_PATH }} && git pull"

Docker凭证

yaml
jobs:
  docker-build-push:
    runs-on: ubuntu-latest
    steps:
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Build and push
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker push myapp:${{ github.sha }}

密钥的条件使用

环境条件

yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy based on environment
        run: |
          if [ "$GITHUB_REF" = "refs/heads/main" ]; then
            # 生产环境使用生产密钥
            API_KEY="${{ secrets.PROD_API_KEY }}"
          elif [ "$GITHUB_REF" = "refs/heads/develop" ]; then
            # 开发环境使用开发密钥
            API_KEY="${{ secrets.DEV_API_KEY }}"
          else
            # 其他环境使用测试密钥
            API_KEY="${{ secrets.TEST_API_KEY }}"
          fi
          
          echo "Using API key for environment: $GITHUB_REF"
        env:
          API_KEY: ${{ secrets.ENV_API_KEY }}

事件条件

yaml
jobs:
  conditional-secrets:
    runs-on: ubuntu-latest
    steps:
      - name: Use secrets conditionally
        run: |
          if [ "$GITHUB_EVENT_NAME" = "push" ]; then
            echo "Push event - using deployment secrets"
            DEPLOY_SECRET="${{ secrets.DEPLOY_SECRET }}"
          elif [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
            echo "PR event - using test secrets"
            DEPLOY_SECRET="${{ secrets.TEST_SECRET }}"
          fi
        env:
          GITHUB_EVENT_NAME: ${{ github.event_name }}

密钥安全检测

检测密钥泄露

yaml
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 获取完整历史以检查所有提交
      
      - name: Scan for secrets in code
        run: |
          # 使用truffleHog或其他工具扫描密钥泄露
          # truffleHog --regex --entropy=False .
          echo "Scanning for potential secret leaks..."
          
          # 检查代码中是否包含密钥字面量
          if git grep -l "secret\|password\|token\|key" -- "*.yml" "*.yaml" "*.json" "*.js" "*.py"; then
            echo "Potential secrets found in code. Please review."
            exit 1
          fi

验证密钥格式

yaml
jobs:
  validate-secret-format:
    runs-on: ubuntu-latest
    steps:
      - name: Validate secret format
        run: |
          # 验证API密钥格式
          API_KEY="${{ secrets.API_KEY }}"
          if [[ ! "$API_KEY" =~ ^[A-Za-z0-9_\-]{32,}$ ]]; then
            echo "API key format is invalid"
            exit 1
          fi
          
          # 验证邮箱格式的密钥
          EMAIL_TOKEN="${{ secrets.EMAIL_TOKEN }}"
          if [[ ! "$EMAIL_TOKEN" =~ ^[A-Za-z0-9+/]{20,}={0,2}$ ]]; then
            echo "Email token format is invalid"
            exit 1
          fi
          
          echo "All secrets have valid formats"

密钥管理工具

使用密钥管理服务

yaml
jobs:
  use-external-secret:
    runs-on: ubuntu-latest
    steps:
      - 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: Get secret from AWS Secrets Manager
        run: |
          SECRET_VALUE=$(aws secretsmanager get-secret-value \
            --secret-id my-app/production/api-key \
            --query SecretString \
            --output text)
          
          echo "SECRET_VALUE=$SECRET_VALUE" >> $GITHUB_ENV

密钥的分层管理

yaml
# 使用GitHub Variables存储非敏感配置
# 使用GitHub Secrets存储敏感信息

env:
  APP_VERSION: ${{ vars.APP_VERSION }}      # 非敏感
  LOG_LEVEL: ${{ vars.LOG_LEVEL }}          # 非敏感
  API_ENDPOINT: ${{ vars.API_ENDPOINT }}    # 非敏感

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy with layered configuration
        run: |
          echo "Deploying version $APP_VERSION"
          echo "API endpoint: $API_ENDPOINT"
          echo "Log level: $LOG_LEVEL"
          # 使用敏感密钥
        env:
          API_KEY: ${{ secrets.API_KEY }}     # 敏感信息
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # 敏感信息

密钥轮换策略

自动化轮换

yaml
# 可以创建一个定期运行的工作流来检查密钥轮换需求
name: Secret Rotation Check

on:
  schedule:
    - cron: '0 0 1 * *'  # 每月1号执行
  workflow_dispatch:

jobs:
  check-rotation:
    runs-on: ubuntu-latest
    steps:
      - name: Check secret age
        run: |
          echo "Checking if secrets need rotation..."
          # 实现密钥轮换检查逻辑
          # 可以通过API调用或外部服务来管理密钥轮换

密钥版本管理

yaml
# 使用版本化的密钥名称
jobs:
  use-versioned-secret:
    runs-on: ubuntu-latest
    steps:
      - name: Use current version of secret
        run: |
          # 使用最新版本的密钥
          echo "Using API key version 2"
        env:
          API_KEY: ${{ secrets.API_KEY_V2 }}
      
      - name: Fallback to previous version
        run: |
          # 如果新版本不可用,回退到旧版本
          echo "Using fallback secret"
        env:
          API_KEY: ${{ secrets.API_KEY_V1 }}
        if: env.API_KEY_V2 == ''

审计和监控

密钥访问日志

yaml
jobs:
  audit-secret-access:
    runs-on: ubuntu-latest
    steps:
      - name: Log secret access
        run: |
          echo "Workflow: $GITHUB_WORKFLOW"
          echo "Run ID: $GITHUB_RUN_ID"
          echo "Actor: $GITHUB_ACTOR"
          echo "Repository: $GITHUB_REPOSITORY"
          echo "Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
          echo "Secret access logged"
        env:
          # 实际的密钥不会被记录,只记录访问行为
          AUDIT_LOG_TOKEN: ${{ secrets.AUDIT_LOG_TOKEN }}

密钥使用监控

yaml
# 创建专门的监控工作流
name: Secret Usage Monitoring

on:
  workflow_run:
    workflows: ["CI/CD Pipeline"]
    types: [completed]

jobs:
  monitor-secrets:
    runs-on: ubuntu-latest
    steps:
      - name: Monitor secret usage
        run: |
          echo "Workflow ${{ github.event.workflow_run.name }} completed"
          echo "Status: ${{ github.event.workflow_run.conclusion }}"
          # 记录和监控密钥使用情况

最佳实践总结

1. 分类管理

yaml
# 将密钥按用途分类
# - 部署密钥:DEPLOY_TOKEN, SSH_PRIVATE_KEY
# - API密钥:API_KEY, API_SECRET
# - 数据库密钥:DB_PASSWORD, DB_SSL_CERT
# - 服务密钥:SERVICE_ACCOUNT_KEY, ACCESS_TOKEN

2. 文档记录

yaml
# 在密钥描述中记录以下信息:
# - 密钥用途
# - 创建日期
# - 预期轮换日期
# - 相关服务/系统
# - 访问权限要求

3. 安全检查清单

yaml
# 密钥安全检查清单:
# [ ] 密钥不在代码中硬编码
# [ ] 密钥不在提交消息中
# [ ] 密钥不在日志中明文显示
# [ ] 密钥具有最小必要权限
# [ ] 密钥定期轮换
# [ ] 密钥访问受到监控

通过遵循这些密钥管理最佳实践,可以确保GitHub Actions工作流的安全性,防止敏感信息泄露,并建立可靠的自动化流程。