Usar segredos em um fluxo de trabalho

Concluído

Os segredos não são passados automaticamente para os executores quando os fluxos de trabalho são executados. Para disponibilizar um segredo para uma ação, você deve configurá-lo como uma entrada ou variável de ambiente no arquivo do fluxo de trabalho. Para fazer isso, você pode usar o contexto de segredos conforme ilustrado no seguinte exemplo:

Usar segredos efetivamente em fluxos de trabalho do GitHub Actions requer entender como acessá-los com segurança, aplicá-los adequadamente e trabalhar dentro de suas limitações. Esta seção aborda padrões práticos e técnicas para integrar segredos em seus fluxos de trabalho de CI/CD.

Acessando segredos em fluxos de trabalho

Os segredos não estão disponíveis automaticamente para as etapas de fluxo de trabalho. Você deve expô-las explicitamente por meio de entradas ou variáveis de ambiente usando o secrets contexto.

Padrões básicos de uso de segredo

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"

Uso de segredo entre shells

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}")

Padrões avançados de uso de segredo

Uso de segredo condicional

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"

Validação secreta e verificações de integridade

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

Composição e transformação secretas

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

Trabalhando com lógica condicional

Usando informações confidenciais em instruções condicionais

Como os segredos não podem ser referenciados diretamente em if condições, use variáveis de ambiente como intermediários:

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

Gestão de múltiplas condições

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 != '' }}"

Práticas recomendadas de segurança em fluxos de trabalho

Minimizar o escopo de exposição de informações confidenciais

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"

Tratamento seguro de erros com segredos

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

Detecção de rotação de segredo

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

Noções básicas sobre limitações e soluções alternativas

Limitações de tamanho do segredo

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

Limitações do repositório fork

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

Entender esses padrões e limitações ajuda você a criar fluxos de trabalho robustos que lidam com segredos com segurança, mantendo a funcionalidade em diferentes contextos de execução e configurações de repositório.