DevOps

Variáveis de Ambiente e Secrets em Pipelines Já leu

13 min de leitura

Variáveis de Ambiente e Secrets em Pipelines
Em abril de 2022, a empresa de segurança GitGuardian publicou um relatório revelando que mais de seis milhões de secrets foram expostos em repositórios públicos do GitHub apenas naquele ano — um aumento de 25% em relação

Em abril de 2022, a empresa de segurança GitGuardian publicou um relatório revelando que mais de seis milhões de secrets foram expostos em repositórios públicos do GitHub apenas naquele ano — um aumento de 25% em relação ao ano anterior. Chaves de API da AWS, senhas de banco de dados, tokens de autenticação, credenciais de serviços de pagamento. A maioria deles ainda estava ativa no momento da descoberta.

A causa mais comum não é negligência deliberada. É a falta de um processo claro para gerenciar informações sensíveis em pipelines de CI/CD. Um desenvolvedor adiciona uma chave diretamente no arquivo de workflow para "testar rapidamente", o teste funciona, o commit vai para o repositório e a chave fica exposta para sempre no histórico — mesmo que seja removida em um commit posterior.

Entender como gerenciar secrets corretamente não é opcional. É uma competência fundamental de qualquer profissional de DevOps.


A Distinção entre Variáveis de Ambiente e Secrets

Antes de entrar nas ferramentas, é importante estabelecer uma distinção conceitual que muitas equipes ignoram.

Variáveis de ambiente são configurações que variam entre ambientes mas não são sensíveis. O nome do ambiente (NODE_ENV=production), a porta da aplicação (PORT=3000), a URL base de uma API pública, o nível de log (LOG_LEVEL=info) — todas essas informações podem viver em arquivos de configuração versionados sem risco.

Secrets são informações sensíveis cuja exposição representa um risco de segurança. Senhas de banco de dados, chaves de API, tokens OAuth, certificados, chaves SSH, chaves de criptografia — essas informações nunca devem aparecer em código, em logs ou em qualquer lugar que não seja um sistema de gerenciamento de secrets com controle de acesso e auditoria.

A confusão entre os dois conceitos é a origem de muitos vazamentos. Não basta proteger as senhas se as URLs de conexão com o banco contêm a senha embutida. Não basta não commitar o .env se o pipeline imprime variáveis de ambiente nos logs de debug.


Secrets no GitHub Actions

O GitHub oferece três níveis de secrets, cada um com escopo e visibilidade diferentes.

Repository secrets — disponíveis apenas no repositório onde foram criados. Adequados para projetos individuais ou segredos específicos de um repositório.

Environment secrets — vinculados a um ambiente específico (staging, production). Só ficam disponíveis quando o job está rodando naquele ambiente. Permitem ter a mesma chave com valores diferentes por ambiente.

Organization secrets — disponíveis para múltiplos repositórios dentro de uma organização. Adequados para segredos compartilhados como tokens de acesso a registros de container ou chaves de integração com ferramentas externas.

Criando um secret via GitHub CLI:

# Secret de repositório
gh secret set DATABASE_URL --body "postgresql://user:senha@host:5432/db"

# Secret de ambiente específico
gh secret set DATABASE_URL \
  --env production \
  --body "postgresql://user:senha_prod@host-prod:5432/db"

# A partir de um arquivo
gh secret set CHAVE_PRIVADA_SSH < ~/.ssh/id_ed25519

# Listando secrets existentes (apenas os nomes — valores nunca são exibidos)
gh secret list
gh secret list --env production

Usando Secrets em Workflows

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      # Forma 1: como variável de ambiente do step
      - name: Conecta ao banco
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: npm run db:migrate

      # Forma 2: como input de uma action
      - name: Login no registro Docker
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # Forma 3: escrevendo em arquivo temporário
      - name: Configura chave SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.CHAVE_SSH_PRIVADA }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H ${{ secrets.SERVIDOR_HOST }} >> ~/.ssh/known_hosts

O GitHub Actions mascara automaticamente os valores de secrets nos logs. Se um secret tiver o valor minha-senha-secreta e esse valor aparecer em qualquer linha de log, ele será substituído por ***. No entanto, esse mascaramento tem limitações — valores muito curtos ou que aparecem como parte de strings maiores podem não ser mascarados corretamente.


O GITHUB_TOKEN: O Secret Automático

Todo workflow do GitHub Actions tem acesso automático a um token especial chamado GITHUB_TOKEN. Esse token é gerado pelo GitHub no início de cada execução de workflow e expirado ao final. Ele tem permissões para interagir com o repositório onde o workflow está rodando.

As permissões padrão do GITHUB_TOKEN podem ser configuradas no nível do repositório ou sobrescritas por workflow:

# Permissões mínimas para o workflow inteiro
permissions:
  contents: read
  packages: write
  pull-requests: write

jobs:
  build:
    runs-on: ubuntu-latest
    # Permissões específicas para este job
    permissions:
      contents: read
      packages: write

    steps:
      - name: Login no GHCR usando o token automático
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

O princípio de menor privilégio se aplica diretamente aqui: cada workflow deve ter apenas as permissões que realmente precisa. Um workflow que apenas executa testes não precisa de permissão para escrever no repositório.


Variáveis de Ambiente por Ambiente

O GitHub Actions permite definir variáveis de ambiente em diferentes níveis de escopo, do mais amplo ao mais específico:

# Nível do workflow — disponível em todos os jobs
env:
  NODE_VERSION: '20.x'
  REGISTRY: ghcr.io

jobs:
  build:
    runs-on: ubuntu-latest
    # Nível do job — disponível em todos os steps deste job
    env:
      NODE_ENV: production
      APP_PORT: 3000

    steps:
      - name: Build
        # Nível do step — disponível apenas neste step
        env:
          BUILD_TARGET: dist
        run: npm run build -- --outDir $BUILD_TARGET

Para variáveis que mudam entre ambientes, o recurso de environment variables do GitHub Environments é o mais adequado:

No repositório, em Settings → Environments, cria-se o ambiente e adicionam-se variáveis não-sensíveis diretamente ali. Elas ficam disponíveis nos jobs que referenciam aquele ambiente:

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # carrega variáveis e secrets deste ambiente

    steps:
      - name: Deploy
        env:
          # Variável definida no ambiente 'production' do GitHub
          API_URL: ${{ vars.API_URL }}
          # Secret definido no ambiente 'production'
          API_KEY: ${{ secrets.API_KEY }}
        run: ./scripts/deploy.sh

Gerenciadores Externos de Secrets

Para organizações com requisitos mais avançados de segurança ou com infraestrutura multi-cloud, os secrets do GitHub podem não ser suficientes. Os gerenciadores externos oferecem funcionalidades como rotação automática de credenciais, auditoria detalhada e integração com sistemas de identidade corporativos.

HashiCorp Vault é a solução open source mais consolidada. Integra com GitHub Actions via action oficial:

- name: Importa secrets do Vault
  uses: hashicorp/vault-action@v3
  with:
    url: https://vault.minha-empresa.com
    token: ${{ secrets.VAULT_TOKEN }}
    secrets: |
      secret/data/producao/banco password | DATABASE_PASSWORD;
      secret/data/producao/api key | API_KEY;
      secret/data/producao/redis url | REDIS_URL

AWS Secrets Manager é a solução gerenciada da AWS. Cada secret tem controle de acesso via IAM e suporte nativo a rotação automática:

- name: Configura credenciais AWS
  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: Importa secrets do AWS Secrets Manager
  run: |
    SECRET=$(aws secretsmanager get-secret-value \
      --secret-id producao/minha-api \
      --query SecretString \
      --output text)

    # Extrai valores individuais do JSON
    DATABASE_URL=$(echo $SECRET | jq -r '.database_url')
    API_KEY=$(echo $SECRET | jq -r '.api_key')

    # Exporta para os steps seguintes de forma segura
    echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
    echo "API_KEY=$API_KEY" >> $GITHUB_ENV
    echo "::add-mask::$DATABASE_URL"
    echo "::add-mask::$API_KEY"

O comando ::add-mask:: instrui o GitHub Actions a mascarar aquele valor específico nos logs — essencial quando o valor vem de uma fonte externa e o GitHub não sabe automaticamente que deve ser mascarado.


O Arquivo .env em Desenvolvimento

Em desenvolvimento local, o arquivo .env é a forma mais prática de gerenciar configurações. A regra de ouro é simples: o .env nunca vai para o repositório.

Estrutura recomendada para projetos com múltiplos ambientes:

.env                  # Não versionado — valores locais do desenvolvedor
.env.example          # Versionado — template com todas as chaves, sem valores
.env.test             # Pode ser versionado — apenas valores de teste, sem dados reais
.env.production       # Nunca versionado — gerenciado pelo pipeline

O .env.example serve como documentação viva do que o sistema precisa para funcionar:

# .env.example
# Banco de dados
DATABASE_URL=postgresql://usuario:senha@localhost:5432/nome_do_banco

# Cache
REDIS_URL=redis://localhost:6379

# Autenticação
JWT_SECRET=gere-um-valor-aleatorio-aqui
JWT_EXPIRES_IN=7d

# Serviço de email
SMTP_HOST=smtp.exemplo.com
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=

# Integrações externas
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# Configurações da aplicação
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug

A biblioteca dotenv carrega esse arquivo automaticamente em desenvolvimento:

// src/config/env.js
require('dotenv').config();

const config = {
  database: {
    url: process.env.DATABASE_URL,
  },
  redis: {
    url: process.env.REDIS_URL,
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d',
  },
  server: {
    port: parseInt(process.env.PORT || '3000'),
    env: process.env.NODE_ENV || 'development',
  },
};

// Valida que todas as variáveis obrigatórias estão presentes
const required = ['DATABASE_URL', 'REDIS_URL', 'JWT_SECRET'];
const missing = required.filter(key => !process.env[key]);

if (missing.length > 0) {
  throw new Error(
    `Variáveis de ambiente obrigatórias não definidas: ${missing.join(', ')}`
  );
}

module.exports = config;

Essa validação na inicialização da aplicação evita que a aplicação suba silenciosamente com configurações incompletas e falhe de forma inesperada em runtime.


Auditoria e Rotação de Secrets

Secrets que nunca são rotacionados são um risco crescente. Um secret que foi exposto acidentalmente há seis meses pode ainda estar sendo explorado silenciosamente.

Boas práticas de ciclo de vida de secrets:

Rotação periódica — secrets de alto valor devem ser rotacionados regularmente, mesmo que não haja suspeita de comprometimento. O AWS Secrets Manager e o HashiCorp Vault suportam rotação automática.

Revogação imediata — quando um desenvolvedor sai da empresa ou quando um secret é acidentalmente exposto, a revogação deve ser imediata. Processos que dependem de secrets pessoais — chaves SSH de desenvolvedores individuais, tokens gerados por contas pessoais — são um risco operacional.

Auditoria de uso — saber quem acessou qual secret e quando é fundamental para investigação de incidentes. Gerenciadores como Vault e AWS Secrets Manager mantêm logs de acesso detalhados.

Scanning no código — ferramentas como o GitGuardian e o git-secrets verificam automaticamente se secrets foram commitados acidentalmente:

# Instala o git-secrets
brew install git-secrets   # macOS
# ou compila da fonte no Linux

# Configura para verificar padrões da AWS
git secrets --register-aws

# Adiciona como hook pre-commit
git secrets --install

# Verifica o repositório inteiro em busca de secrets já commitados
git secrets --scan-history

Integrado ao pipeline de CI:

- name: Verifica secrets no código
  run: |
    pip install detect-secrets
    detect-secrets scan --baseline .secrets.baseline
    detect-secrets audit .secrets.baseline

Um Workflow Completo com Gerenciamento Seguro de Secrets

Reunindo todas as práticas em um workflow realista:

name: Deploy Seguro

on:
  push:
    branches: [main]

permissions:
  contents: read
  packages: write
  id-token: write  # necessário para OIDC com AWS

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      # Autenticação sem chaves estáticas via OIDC
      - name: Configura credenciais AWS via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
          aws-region: us-east-1

      # Importa secrets do gerenciador externo
      - name: Importa secrets de produção
        run: |
          SECRETS=$(aws secretsmanager get-secret-value \
            --secret-id producao/minha-api \
            --query SecretString --output text)

          DB_URL=$(echo $SECRETS | jq -r '.database_url')
          REDIS_URL=$(echo $SECRETS | jq -r '.redis_url')

          # Mascara os valores nos logs
          echo "::add-mask::$DB_URL"
          echo "::add-mask::$REDIS_URL"

          # Exporta para os steps seguintes
          echo "DATABASE_URL=$DB_URL" >> $GITHUB_ENV
          echo "REDIS_URL=$REDIS_URL" >> $GITHUB_ENV

      # Build da imagem com o secret disponível apenas durante o build
      - name: Constrói imagem Docker
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          secrets: |
            database_url=${{ env.DATABASE_URL }}

      # Deploy usando a imagem publicada
      - name: Deploy no ECS
        run: |
          aws ecs update-service \
            --cluster producao \
            --service minha-api \
            --force-new-deployment

      # Limpa variáveis sensíveis do ambiente
      - name: Limpa variáveis sensíveis
        if: always()
        run: |
          echo "DATABASE_URL=" >> $GITHUB_ENV
          echo "REDIS_URL=" >> $GITHUB_ENV

O Que Vem a Seguir

No próximo artigo será abordado o deploy automático para servidores com GitHub Actions — coberturas completas de estratégias de deploy, zero-downtime deployment, rollback automatizado e como estruturar o pipeline para que a promoção entre ambientes seja segura e auditável.


Referências para Aprofundamento

Segurança e boas práticas - GitHub Docs — Encrypted Secrets — Documentação oficial do GitHub sobre criação, uso e limitações dos secrets em Actions. - GitGuardian — State of Secrets Sprawl — Relatório anual sobre exposição de secrets em repositórios públicos. Leitura importante para dimensionar o problema. - OWASP — Secrets Management Cheat Sheet — Guia completo da OWASP sobre boas práticas de gerenciamento de secrets em diferentes contextos.

Ferramentas - HashiCorp Vault Documentation — Documentação completa do Vault, incluindo guias de instalação, políticas de acesso e integração com CI/CD. - AWS Secrets Manager — Developer Guide — Guia oficial do AWS Secrets Manager cobrindo criação, rotação e acesso a secrets. - detect-secrets — GitHub — Ferramenta open source do Yelp para detectar secrets em código. Mantém um baseline de falsos positivos conhecidos.

OIDC e autenticação sem chaves estáticas - GitHub Docs — Configuring OpenID Connect in AWS — Guia oficial para configurar autenticação OIDC entre GitHub Actions e AWS, eliminando a necessidade de chaves de acesso estáticas.

Comentários

Mais em DevOps

Instalação Manual do MySQL no Debian, Arch, Fedora e openSUSE
Instalação Manual do MySQL no Debian, Arch, Fedora e openSUSE

Este guia cobre a instalação do MySQL 9.7.0 puro (sem MariaDB, sem pacotes de...

Trabalhando com arquivos CSV
Trabalhando com arquivos CSV

JSON domina as APIs modernas, mas o CSV domina o mundo real dos dados. Planil...

Instalando e Rodando Seus Primeiros Containers
Instalando e Rodando Seus Primeiros Containers

O artigo anterior explicou o que são containers e por que eles existem. Este...