Secrets e variáveis no GitHub Actions
Vazar um segredo de produção em um workflow do GitHub Actions é um dos erros mais comuns — e mais custosos — de segurança em times de desenvolvimento. Entender a hierarquia de secrets, usar environments corretamente e nunca imprimir secrets em logs são práticas que protegem sua infraestrutura.
Hierarquia de secrets no GitHub
Como os secrets são organizados por escopo:
bash
# Três níveis de secrets no GitHub:
# 1. Organization secrets — compartilhados entre repositórios:
# GitHub Org → Settings → Secrets and variables → Actions
# Exemplo: DOCKER_HUB_TOKEN, SLACK_WEBHOOK
# 2. Repository secrets — apenas para um repositório:
# Repo → Settings → Secrets and variables → Actions → New secret
# Exemplo: SSH_PRIVATE_KEY, DATABASE_URL
# 3. Environment secrets — vinculados a um ambiente (production/staging):
# Repo → Settings → Environments → production → Add secret
# Exemplo: PROD_DATABASE_URL (só disponível no job que usa environment: production)
# Hierarquia de prioridade (do mais específico ao mais geral):
# Environment > Repository > Organization
# Variáveis (não-secretas, visíveis nos logs):
# Repo → Settings → Secrets and variables → Actions → Variables
# Exemplo: NODE_VERSION, AWS_REGION, APP_NAME
# No workflow — usar secrets:
# ${{ secrets.NOME_DO_SECRET }}
# ${{ vars.NOME_DA_VARIAVEL }} (variáveis não-secretas)Usar secrets com segurança nos workflows
Boas práticas para evitar vazamento de secrets:
yaml
# .github/workflows/deploy.yml
name: Deploy
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # ← ativa os secrets do ambiente production
steps:
- uses: actions/checkout@v4
# ✅ CORRETO — passar secret como variável de ambiente:
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: ./scripts/deploy.sh
# ❌ ERRADO — nunca interpolar secret diretamente no script inline:
# - run: echo ${{ secrets.DATABASE_URL }} # vaza no log!
# ❌ ERRADO — nunca usar secret em contexto de shell não-confiável:
# - run: curl -u ${{ secrets.API_KEY }} http://... # expansão no shell
# ✅ CORRETO — GitHub mascara automaticamente os valores de secrets nos logs:
- name: Testar conexão (o valor do secret fica mascarado nos logs)
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
run: |
echo "Conectando em $SSH_HOST" # exibe "Conectando em ***"
# O GitHub mascara automaticamente o valor do secret
# ✅ Criar arquivo de configuração a partir de secrets:
- name: Criar .env de produção
run: |
cat > /tmp/.env.production << EOF
DATABASE_URL=${{ secrets.DATABASE_URL }}
REDIS_URL=${{ secrets.REDIS_URL }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
EOFEnvironments com aprovação manual
Proteger o ambiente de produção com revisores obrigatórios:
yaml
# Configurar environment com proteção:
# GitHub → Repo → Settings → Environments → New environment
# Environment: production
# ✓ Required reviewers: adicionar @username ou @team
# ✓ Wait timer: 0 minutes (ou X para delay obrigatório)
# ✓ Deployment branches: "Selected branches" → main only
# No workflow:
jobs:
deploy-staging:
environment: staging # sem proteção — deploy automático
runs-on: ubuntu-latest
steps:
- run: echo "Deploy em staging"
deploy-production:
needs: deploy-staging
environment: production # ← pausa aqui para aprovação manual
runs-on: ubuntu-latest
steps:
- run: echo "Deploy em produção (após aprovação)"
# Fluxo:
# 1. Push em main
# 2. Tests passam
# 3. Deploy em staging automaticamente
# 4. GitHub envia notificação para os revisores
# 5. Revisor acessa a URL de aprovação e clica "Approve and deploy"
# 6. Deploy em produção executa
# Secrets específicos do environment:
# ${{ secrets.DATABASE_URL }} no job de production usa o valor
# configurado em Environments → production → SecretsOpenID Connect: credenciais temporárias sem secrets
Autenticar no AWS sem armazenar access keys permanentes:
yaml
# OIDC elimina a necessidade de armazenar AWS_ACCESS_KEY_ID no GitHub.
# O GitHub Actions se autentica diretamente no AWS com token temporário.
# .github/workflows/deploy-aws.yml:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # necessário para OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Configurar credenciais AWS via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: sa-east-1
# Nenhum access key necessário — token OIDC é gerado automaticamente
# A partir daqui, aws CLI está autenticado com credenciais temporárias:
- name: Push imagem para ECR
run: |
aws ecr get-login-password --region sa-east-1 | \
docker login --username AWS --password-stdin 123456789.dkr.ecr.sa-east-1.amazonaws.com
docker push 123456789.dkr.ecr.sa-east-1.amazonaws.com/minha-api:latest
# Na AWS — configurar IAM Role para GitHub Actions:
# Trust policy do Role:
# {
# "Principal": {
# "Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
# },
# "Condition": {
# "StringEquals": {
# "token.actions.githubusercontent.com:sub": "repo:org/repo:ref:refs/heads/main"
# }
# }
# }Auditoria de secrets e boas práticas
Detectar secrets vazados e prevenir exposição acidental:
bash
# Ferramentas para detectar secrets no código:
# 1. git-secrets (AWS) — instalar e configurar:
brew install git-secrets # ou apt
git secrets --install # instalar hooks no repositório
git secrets --register-aws # patterns do AWS
# 2. gitleaks — detectar secrets em commits:
# .github/workflows/security.yml:
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # histórico completo
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 3. .gitignore — sempre ignorar:
# .env
# .env.local
# .env.production
# *.pem
# *.key
# credentials.json
# 4. Se um secret vazar em um commit:
# (a) Rotacionar IMEDIATAMENTE — assume comprometido
# (b) Remover do histórico: git filter-repo --path .env --invert-paths
# (c) Force push (após comunicar o time)
# AVISO: remover do histórico não garante que foi deletado de forks ou caches
# 5. Rotação programada de secrets:
# Use secrets com expiração (tokens de 90 dias)
# Configure lembretes no calendário para renovar$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS para APIs em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.