ワークフローでシークレットを使用する

完了

シークレットは、ワークフローの実行時にランナーに自動的に渡されません。 アクションでシークレットを使用できるようにするには、ワークフロー ファイルの入力または環境変数としてシークレットを設定する必要があります。 これを実現するには、次の例に示すようにシークレット コンテキストを使用できます。

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"

クロスシェル シークレットの使用

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

これらのパターンと制限事項を理解すると、さまざまな実行コンテキストとリポジトリ構成にわたって機能を維持しながら、シークレットを安全に処理する堅牢なワークフローを構築できます。