Alternar tema

Guia completo de Docker Secrets: práticas recomendadas para proteger senhas e chaves de API em contêineres

Easton editorial illustration: registry transfer crane

O celular não parava de vibrar: eram 24 mensagens, todas alertas de anomalia no banco de dados. Conectei ao servidor por SSH e vi que o número de conexões havia disparado. Alguém estava fazendo uma varredura por força bruta.

Ao verificar os logs, descobri que o invasor tinha usado a senha correta — a senha do MySQL que havia sido colocada no docker-compose.yml três meses antes. No mês anterior, um estagiário tinha feito fork do projeto para um repositório público pessoal, e foi assim que a senha vazou.

Mesmo sem vazamento do código, qualquer pessoa capaz de executar docker inspect consegue ver todas as variáveis de ambiente em texto simples. Neste artigo, vamos conversar sobre o gerenciamento de senhas em contêineres: quais práticas perigosas evitar e quais ferramentas realmente protegem informações sensíveis.

Por que variáveis de ambiente não são seguras?

Três práticas inseguras muito comuns

Vou começar pelos erros que eu mesmo já cometi. Veja se você também fez algo parecido.

Primeira: colocar a senha diretamente no Dockerfile

FROM node:18
ENV DATABASE_PASSWORD=MyS3cr3tP@ssw0rd
ENV API_KEY=sk-1234567890abcdef

Essa é a pior opção. Cada instrução do Dockerfile cria uma camada da imagem. Mesmo que depois você use ENV DATABASE_PASSWORD="" para substituir o valor, a senha continuará escondida nas camadas antigas. Qualquer pessoa que tenha acesso à imagem poderá encontrá-la com docker history <nome_da_imagem>.

Não acredita? Eu também não acreditava, até um colega me mostrar. Ele baixou uma imagem interna da empresa e, com poucos comandos, extraiu uma Access Key da AWS. A chave tinha sido deixada três anos antes por um funcionário que já havia saído, e ninguém se lembrara de removê-la.

Segunda: colocar a senha no docker-compose.yml

version: '3.8'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: SuperSecretPassword123
      MYSQL_DATABASE: myapp

Usei essa opção por muito tempo. O motivo era simples: conveniência. Bastava executar docker-compose up -d para colocar tudo no ar, o que agiliza bastante o desenvolvimento e a depuração.

Qual é o problema? Esse arquivo precisa ser enviado ao Git. Mesmo que o repositório seja privado, basta uma permissão mal configurada ou alguém fazer fork para um repositório público, como aconteceu comigo, para todas as senhas vazarem.

Terceira: usar um arquivo .env, mas incluí-lo no controle de versão

# arquivo .env
DB_PASSWORD=password123
API_SECRET=abcdef123456

À primeira vista, parece uma solução mais inteligente: você separa as informações sensíveis em um arquivo .env e usa ${DB_PASSWORD} no docker-compose.

O problema é que muita gente — inclusive eu, no passado — também envia o .env ao Git, pensando que “o restante da equipe precisa conseguir executar o projeto”. Na prática, o resultado não é diferente de gravar a senha diretamente no docker-compose.

O correto é adicionar .env ao .gitignore e fornecer um modelo .env.example. Mas, sinceramente, quantas pessoas pensam nisso logo no início?

Três grandes riscos das variáveis de ambiente

Mesmo que o .env esteja no .gitignore e o código nunca vaze, variáveis de ambiente continuam trazendo riscos. Por quê?

Risco 1: docker inspect mostra tudo

Experimente este comando:

docker inspect <ID_do_contêiner> | grep -A 20 "Env"

Todas as variáveis de ambiente, inclusive as senhas, aparecem em texto simples.

O que isso significa? Qualquer pessoa com acesso ao daemon do Docker — equipe de operações, DevOps ou desenvolvedores com acesso ao servidor — consegue ver suas senhas. Não é preciso quebrar criptografia nem dominar uma técnica avançada: basta executar um comando.

Trabalhei em uma startup em que todos tinham acesso SSH ao servidor de produção, por conveniência. Certo dia, um desenvolvedor frontend recém-contratado ficou curioso para saber “o que havia dentro do contêiner” e executou docker inspect. A senha do banco de dados apareceu na tela. Por sorte, era alguém da própria equipe. E se fosse uma pessoa mal-intencionada?

Risco 2: processos dentro do contêiner podem ler os valores

Não é só o Docker que enxerga as variáveis de ambiente: todos os processos dentro do contêiner também podem lê-las. No Linux, as variáveis de ambiente de um processo ficam no arquivo /proc/<PID>/environ.

Isso significa que, se qualquer dependência da aplicação tiver uma vulnerabilidade que permita injeção de código, o invasor poderá ler as variáveis de ambiente e obter as senhas diretamente.

No ano passado, descobriram um backdoor em uma biblioteca Node.js que lia credenciais da AWS presentes nas variáveis de ambiente e as enviava para um servidor externo. Dezenas de milhares de projetos foram afetados.

Risco 3: os logs podem expor segredos

Esse risco é ainda mais discreto.

Muitas aplicações imprimem as configurações durante a inicialização ou despejam variáveis de ambiente nos logs quando ocorre um erro. Em geral, os arquivos de log têm controles de acesso mais permissivos e podem ser enviados a sistemas centralizados, como o ELK.

Já vi uma equipe centralizar todos os logs de contêineres em um cluster Elasticsearch e, durante uma auditoria de segurança, descobrir centenas de senhas de banco de dados nos registros. Os desenvolvedores tinham o hábito de usar console.log(process.env) durante a depuração e depois se esqueciam de apagar essa linha.

O pior é que, depois que uma senha entra nos logs, fica difícil removê-la por completo: backups, arquivos históricos e outros sistemas passam a guardar cópias.

Casos reais

Esse tipo de incidente é bastante comum, embora muitas empresas não o divulguem. Vou contar alguns casos que conheço.

Caso 1: vazamento em repositório público do GitHub

Uma análise de 2023 mostrou que havia mais de 1 milhão de repositórios públicos no GitHub com chaves de API, senhas de banco de dados e outras informações sensíveis. Não é que todos esses desenvolvedores fossem irresponsáveis. Muitas vezes, trata-se de um descuido momentâneo: uma configuração de teste que não foi removida ou um .env enviado sem querer.

Um amigo que trabalha com machine learning contou que a equipe dele publicou o código de treinamento no GitHub e, junto com ele, enviou as credenciais da AWS. Em menos de 24 horas, alguém usou essas credenciais para criar mais de dez instâncias com GPU e minerar criptomoedas. A conta passou de 20 mil dólares.

Caso 2: vazamento no histórico do pipeline de CI/CD

Ferramentas de CI/CD, como Jenkins e GitLab CI, normalmente preservam logs de variáveis de ambiente no histórico de builds. Sem um mascaramento adequado, esses logs viram verdadeiros cofres de senhas.

Participei de um projeto com implantação automática pelo GitLab CI. Antes de sair da empresa, um colega deu acesso ao repositório a uma equipe terceirizada. Uma pessoa dessa equipe examinou os logs de builds antigos e encontrou os dados de conexão do banco de produção. Por sorte, eles estavam tentando ajudar na otimização e nos avisaram. E se a intenção fosse outra?

Caso 3: varredura de imagens no Docker Hub

Um relatório de segurança da Alibaba Cloud mostrou que, entre as imagens públicas do Docker analisadas, 76% tinham vulnerabilidades, e várias continham credenciais gravadas diretamente.

Algumas empresas publicam imagens internas no Docker Hub por conveniência, pensando que “ninguém vai perder tempo vasculhando a imagem de uma empresa pequena”. Hoje, porém, há muitos crawlers e scanners automatizados dedicados exatamente a isso. Poucos minutos depois do envio, sua imagem já pode ter sido examinada.

Não estou dizendo isso para assustar você. Esses problemas realmente acontecem e são mais comuns do que parecem. A boa notícia é que existem maneiras de evitá-los.

O que é Docker Secrets e como usá-lo?

Como Docker Secrets funciona

Agora vamos à solução.

Docker Secrets é o mecanismo oficial do Docker para gerenciar segredos. Em termos simples, a ideia central é: o segredo deixa de ser uma string em texto simples e passa a ser um arquivo criptografado.

O fluxo funciona assim:

  1. Você cria um secret, e o Docker o armazena criptografado no log Raft do cluster
  2. Quando um contêiner precisa desse secret, o Docker o transfere por uma conexão TLS criptografada
  3. O secret é montado como arquivo no diretório /run/secrets/ dentro do contêiner
  4. Esse diretório usa tmpfs, um sistema de arquivos em memória que não grava os dados em disco
  5. Quando o contêiner para, o secret é removido automaticamente da memória

O ponto principal é: o secret não aparece nas variáveis de ambiente, na saída de docker inspect nem em imagens criadas com docker commit.

Parece abstrato? Tudo bem. Na primeira vez que li a documentação do Docker, também fiquei confuso. Depois que você testa, o conceito fica bem mais claro.

Há uma limitação importante: Docker Secrets só funciona nativamente no modo Swarm. No começo, isso me incomodou bastante. Se eu estava desenvolvendo localmente, em uma única máquina, por que não poderia usar secrets?

Mais tarde, descobri que o docker-compose também oferece file secrets. Os recursos são mais limitados, mas pelo menos funciona. Em um cluster de produção, você pode usar diretamente o modo Swarm ou o Kubernetes.

Tutorial prático: MySQL + WordPress

Vamos montar um exemplo completo. Suponha que você precise implantar um blog WordPress e proteger a senha de root e a senha de usuário do MySQL.

Etapa 1: crie os arquivos de secret

Primeiro, grave as senhas em arquivos locais. Atenção: não envie esses arquivos ao Git.

echo "MyRootPassword123" > db_root_password.txt
echo "MyUserPassword456" > db_password.txt

Etapa 2: inicialize o Swarm, caso ainda não esteja ativo

docker swarm init

Sim, é só esse comando. Se o modo Swarm ainda não estiver ativo, execute-o. Você também pode usar Swarm em uma única máquina; não é obrigatório ter um cluster com vários servidores.

Etapa 3: crie os Docker secrets

docker secret create mysql_root_password db_root_password.txt
docker secret create mysql_password db_password.txt

Depois da criação, exclua imediatamente os arquivos locais com as senhas:

rm db_root_password.txt db_password.txt

Essa etapa é importante. Assim que o Docker lê os arquivos e armazena os valores de forma criptografada, os arquivos locais em texto simples devem ser destruídos.

Verifique se os secrets foram criados:

docker secret ls

Você verá uma saída parecida com esta:

ID                          NAME                  CREATED         UPDATED
abc123...                   mysql_root_password   5 seconds ago   5 seconds ago
def456...                   mysql_password        3 seconds ago   3 seconds ago

Observe que você não consegue ver o conteúdo do secret, apenas o nome e os metadados. Esse é justamente o objetivo: depois da criação, nem mesmo um administrador consegue recuperar o texto simples.

Etapa 4: crie o docker-compose.yml

version: '3.8'

secrets:
  mysql_root_password:
    external: true
  mysql_password:
    external: true

services:
  db:
    image: mysql:8.0
    secrets:
      - mysql_root_password
      - mysql_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
      MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
      MYSQL_USER: wordpress
      MYSQL_DATABASE: wordpress
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    ports:
      - "8080:80"
    secrets:
      - mysql_password
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password
      WORDPRESS_DB_NAME: wordpress

volumes:
  db_data:

Preste atenção a alguns pontos:

  1. Configuração secrets no nível superior: declara quais secrets serão usados. external: true indica que o secret já existe e não será criado pelo compose
  2. services.db.secrets: define quais secrets o serviço poderá usar
  3. MYSQL_ROOT_PASSWORD_FILE: a imagem oficial do MySQL aceita variáveis de ambiente com o sufixo _FILE e lê a senha de um arquivo em vez de recebê-la diretamente na variável

Esse sufixo _FILE é importante, mas nem todas as imagens oferecem suporte a ele. Se a imagem não oferecer, você precisará ler o arquivo no script de inicialização:

# exemplo de entrypoint.sh
export DB_PASSWORD=$(cat /run/secrets/db_password)
# depois, inicialize sua aplicação

Etapa 5: faça a implantação

docker stack deploy -c docker-compose.yml myapp

Observe que usamos docker stack deploy, e não docker-compose up. No modo Swarm, é preciso usar o comando stack.

Etapa 6: verifique o resultado

Entre no contêiner do MySQL para confirmar que o secret foi montado corretamente:

docker exec -it <ID_do_contêiner> sh
ls -la /run/secrets/

Você verá algo parecido:

total 8
-r--r--r-- 1 root root 18 Dec 18 10:00 mysql_password
-r--r--r-- 1 root root 18 Dec 18 10:00 mysql_root_password

Tente ler o arquivo:

cat /run/secrets/mysql_root_password

A senha será exibida. O ponto principal é que essa senha existe apenas na memória, não é gravada em disco nem aparece nas variáveis de ambiente.

Confirme que docker inspect não mostra a senha:

docker inspect <ID_do_contêiner> | grep -i password

A saída terá apenas um caminho como MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password. O valor da senha não aparece.

Esse é o poder do Docker Secrets: a aplicação dentro do contêiner consegue ler a senha, mas o valor em texto simples não fica exposto externamente.

Limitações do Docker Secrets e alternativas

Para ser sincero, Docker Secrets não é perfeito. Estas são as principais limitações:

Limitação 1: exige o modo Swarm

Essa é a mais incômoda. Se você só executa contêineres localmente em uma única máquina, precisa usar docker swarm init para trabalhar com secrets. Embora o Swarm funcione em um único nó, isso pode parecer exagerado para um ambiente simples.

Além disso, depois de habilitar o Swarm, você passa a usar docker stack deploy em vez do conhecido docker-compose up. É preciso mudar alguns hábitos de linha de comando.

Limitação 2: o conteúdo não pode ser alterado depois da criação

Depois que um Docker Secret é criado, seu conteúdo fica somente leitura. Para mudar uma senha, você precisa remover o secret e criá-lo novamente:

docker secret rm mysql_password
echo "NewPassword789" | docker secret create mysql_password -

Depois disso, ainda é necessário atualizar todos os serviços que usam esse secret:

docker service update --secret-rm mysql_password --secret-add mysql_password myapp_db

É um processo um pouco trabalhoso. Com variáveis de ambiente, bastaria alterar o valor e reiniciar o contêiner.

Limitação 3: não funciona com contêineres independentes

Se você inicia o contêiner diretamente com docker run, não pode usar Docker Secrets. O recurso exige um serviço implantado com docker service create ou docker stack.

Isso não é muito prático em testes rápidos. Às vezes, você só quer subir temporariamente um contêiner de banco de dados e descobre que precisa configurar todo o fluxo do Swarm.

Alternativa para desenvolvimento em uma máquina: file secrets do docker-compose

Felizmente, o docker-compose oferece uma alternativa: file secrets. Ela não exige o modo Swarm e monta diretamente um arquivo local como secret.

A configuração é parecida com esta:

version: '3.8'

secrets:
  db_password:
    file: ./secrets/db_password.txt

services:
  db:
    image: mysql:8.0
    secrets:
      - db_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password

Depois, crie o arquivo local com a senha:

mkdir secrets
echo "MyPassword123" > secrets/db_password.txt

Atenção: adicione o diretório secrets/ ao .gitignore!

Essa opção é mais segura que variáveis de ambiente: a senha não aparece em docker inspect nem é incluída na imagem. Ainda assim, ela não é tão segura quanto o Docker Secrets de verdade, porque:

  • O arquivo com a senha continua em texto simples no disco local
  • Não há transmissão criptografada
  • Não há gerenciamento centralizado

Para um ambiente de desenvolvimento local, costuma ser suficiente. Em produção, o mais seguro é usar Swarm ou Kubernetes.

Técnicas avançadas

Depois de dominar as operações básicas, vale conhecer algumas técnicas úteis.

Técnica 1: personalizar o caminho de montagem do secret

Por padrão, o secret é montado em /run/secrets/<secret_name>, mas você pode personalizar o caminho e o nome do arquivo:

services:
  myapp:
    image: myapp:latest
    secrets:
      - source: db_password
        target: /app/config/database.pwd
        mode: 0400

Nesse exemplo, mode: 0400 define a permissão do arquivo para que apenas o proprietário possa lê-lo, aumentando a segurança.

Técnica 2: gerenciar vários ambientes

Use senhas diferentes em desenvolvimento, teste e produção, mas mantenha o mesmo nome para o secret:

# ambiente de produção
docker secret create db_password prod_password.txt

# ambiente de teste (em outro cluster Swarm)
docker secret create db_password test_password.txt

O código da aplicação só precisa ler /run/secrets/db_password, sem saber qual é o valor. Assim, o código permanece idêntico; apenas o cluster de destino muda.

Técnica 3: fazer a rotação do secret

Trocar senhas periodicamente é uma boa prática. A rotação de um Docker secret exige algumas etapas, mas não é difícil:

# 1. Crie a nova senha
echo "NewPassword" | docker secret create db_password_v2 -

# 2. Atualize o serviço: adicione o novo secret e remova o antigo
docker service update \
  --secret-rm db_password \
  --secret-add source=db_password_v2,target=/run/secrets/db_password \
  myapp_db

# 3. Depois de confirmar que o serviço funciona, remova o secret antigo
docker secret rm db_password

Observe o detalhe: o novo secret se chama db_password_v2, mas, por meio do parâmetro target, o caminho de montagem continua sendo /run/secrets/db_password. Assim, não é preciso alterar o código da aplicação.

Técnica 4: criar o secret a partir de stdin

Não quer deixar rastros no disco? Crie o secret diretamente pela entrada padrão:

echo "MySecretPassword" | docker secret create db_password -

O - no final indica que o valor será lido de stdin. Assim, nem sequer é necessário criar um arquivo temporário.

Uma opção ainda mais segura é digitar ou colar o valor manualmente, sem deixá-lo no histórico do shell:

docker secret create db_password -
# cole a senha e pressione Ctrl+D para concluir

Comparação com outras ferramentas de gerenciamento de segredos

Matriz comparativa de quatro ferramentas

Docker Secrets não é a única opção. Dependendo do tamanho do projeto e da infraestrutura, talvez você precise de outra ferramenta. Esta tabela resume as diferenças:

FerramentaCenário indicadoPrincipal vantagemPrincipal desvantagemDificuldade inicial
Docker SecretsCluster Docker SwarmSuporte nativo, sem custo adicional e configuração simplesFunciona apenas com Swarm, tem recursos básicos e não é multiplataforma⭐ Baixa
Kubernetes SecretsAmbiente K8s de produçãoSuporte nativo no ecossistema K8s e integração com o ciclo de vida dos PodsO etcd não é criptografado por padrão; exige configuração adicional para ficar seguro⭐⭐ Média
HashiCorp VaultEmpresas, ambientes multicloud e requisitos rigorosos de conformidadeConjunto completo de recursos, credenciais dinâmicas, permissões granulares, logs de auditoria e vários backendsArquitetura complexa, implantação e manutenção próprias e curva de aprendizado acentuada⭐⭐⭐ Alta
AWS Secrets ManagerAmbiente totalmente baseado na AWS, com serviços como RDS e LambdaIntegração profunda com a AWS, rotação automática de senhas do RDS e serviço gerenciadoDependência da AWS, dificuldade em ambientes multicloud e custo de uso⭐⭐ Média

Em resumo:

  • Projeto pequeno com Docker Swarm → Docker Secrets é suficiente, simples e gratuito
  • Kubernetes em produção → Comece com K8s Secrets e use External Secrets Operator para integrar um Vault externo
  • Empresa grande, multicloud e com requisitos elevados de segurança → Use Vault; o investimento inicial compensa no longo prazo
  • Infraestrutura totalmente na AWS → Secrets Manager é a opção mais prática e funciona muito bem com RDS e Lambda

Não existe uma ferramenta universalmente melhor, apenas a mais adequada ao seu cenário. Minha própria trajetória foi começar com file secrets do docker-compose em projetos pequenos, migrar para Docker Swarm + Docker Secrets quando a equipe cresceu e, agora que a empresa migrou para K8s, avaliar a adoção do Vault.

Cada migração tem um custo, mas a segurança também melhorou gradualmente.

Recomendações para escolher

Como escolher na prática? Veja algumas referências para a decisão.

Se você é…

Desenvolvedor independente ou projeto pequeno:

  • Desenvolvimento local: file secrets do docker-compose é suficiente
  • Alguns serviços implantados em uma VPS: Docker Swarm + Docker Secrets
  • Orçamento limitado e pouca disposição para manter infraestrutura: evite o Vault, pois ele é pesado demais

Startup com 10 a 50 pessoas:

  • Usa Docker: Docker Secrets
  • Usa K8s: K8s Secrets + External Secrets Operator, em uma adoção gradual que permite migrar para Vault depois
  • Está totalmente na AWS: Secrets Manager é a opção mais prática

Empresa grande:

  • Ambiente multicloud: Vault é praticamente a única opção
  • Necessidade de auditoria e conformidade: Vault
  • Equipe de segurança dedicada: Vault
  • Orçamento suficiente e tolerância à curva de aprendizado: Vault

Versão simplificada da árvore de decisão:

Você usa K8s?
 └─ Sim → Use K8s Secrets; para recursos avançados, use Vault
 └─ Não → Usa Docker Swarm?
      └─ Sim → Docker Secrets
      └─ Não → Está na AWS?
           └─ Sim → AWS Secrets Manager
           └─ Não → file secrets do docker-compose (desenvolvimento) / Vault (produção)

Casos práticos

Vou contar duas trajetórias reais de escolha que conheço.

Caso 1: a evolução de uma startup

A empresa SaaS de um amigo começou com cinco pessoas e todos os serviços Docker em uma única VPS. No início, eles usavam docker-compose + file secrets, com arquivos de senha armazenados no servidor e gerenciados manualmente.

Seis meses depois, a empresa recebeu investimento-anjo, a equipe cresceu para 15 pessoas e os serviços foram divididos em uma arquitetura de microsserviços. Nesse momento, adotaram Docker Swarm e migraram para Docker Secrets. O principal benefício foi a praticidade: eles deixaram de distribuir arquivos de senha manualmente entre servidores e passaram a gerenciar os secrets de forma centralizada no cluster Swarm.

Um ano depois, após a rodada Série A, migraram para K8s quando contrataram profissionais de DevOps. Começaram a usar K8s Secrets, mas também adotaram AWS RDS, S3 e outros serviços. As credenciais relacionadas à AWS passaram a ser gerenciadas pelo Secrets Manager, e o External Secrets Operator sincronizava esses valores com os secrets do K8s.

Agora, eles avaliam uma migração completa para Vault. A empresa está adotando uma estratégia multicloud e considera usar GCP para backups, o que torna a dependência do Secrets Manager da AWS menos adequada.

Percebeu? A arquitetura de segurança evolui junto com o negócio; não é preciso começar pela solução mais complexa.

Caso 2: uma empresa tradicional começa pela solução completa

Outro amigo trabalha com tecnologia em um banco. No ano passado, a empresa iniciou uma modernização baseada em contêineres. Como o setor financeiro tem requisitos rigorosos de conformidade, eles adotaram o Vault desde o início.

Embora a curva de aprendizado tenha sido alta, os benefícios foram claros:

  • Logs de auditoria completos, com registro de cada acesso a um segredo
  • Suporte a credenciais dinâmicas, como acessos temporários ao banco que expiram automaticamente
  • Integração com o LDAP existente para controle de permissões

Foram necessários dois meses-pessoa para implantação e treinamento, mas a equipe resolveu de uma vez os principais problemas de gerenciamento de segredos. Depois disso, o custo de manutenção ficou baixo.

Cenários diferentes exigem escolhas diferentes. Para uma empresa pequena, adotar Vault pode custar mais do que entrega; para uma empresa grande, Docker Secrets talvez seja limitado demais.

Checklist de práticas recomendadas para produção

Medidas de segurança para aplicar agora

Mesmo que você não use Docker Secrets, estas medidas devem ser tomadas imediatamente:

✅ Verifique se há informações sensíveis no histórico do Git

# procure palavras-chave que podem indicar senhas
git log -p -S 'password' -S 'secret' -S 'api_key'

# verifique se um arquivo .env já foi enviado ao repositório
git log --all --full-history -- .env

Se encontrar algo, use git filter-repo ou BFG Repo-Cleaner para remover completamente os dados do histórico. Depois, troque imediatamente todas as senhas expostas.

✅ Configure o .gitignore

# .gitignore
.env
*.env
secrets/
db_password.txt
*_password.txt
*_secret.txt
*.pem
*.key

Não conte com a sorte: inclua essas regras.

✅ Ative o GitHub Secret Scanning

Se você usa GitHub, abra as configurações do repositório e ative Security → Secret scanning alerts. Se uma chave for enviada por engano, o GitHub emitirá um alerta automático.

O plano gratuito oferece esse recurso apenas para repositórios públicos; repositórios privados exigem uma conta Pro, Team ou Enterprise. Como repositórios públicos precisam ainda mais dessa proteção, ative-a o quanto antes.

✅ Troque todas as credenciais que podem ter vazado

Faça um inventário de:

  • Senhas de bancos de dados
  • Chaves de API, como AWS, OpenAI e Stripe
  • JWT secret
  • OAuth client secrets
  • Chaves privadas SSL

Troque tudo o que for possível. Mesmo depois de um vazamento, ainda dá tempo de reduzir o dano.

Etapas para implementar Docker Secrets

Decidiu usar Docker Secrets? Siga esta ordem:

Etapa 1: faça um inventário das informações sensíveis

Liste tudo o que precisa ser protegido:

✓ Senha de root do MySQL
✓ Senha do banco de dados da aplicação
✓ Senha do Redis
✓ Chave de assinatura JWT
✓ Chaves de API de terceiros (pagamento, SMS etc.)
✓ Chave privada do certificado SSL
✓ OAuth client secret

Etapa 2: escolha a ferramenta

Com base na comparação anterior, escolha Docker Secrets, K8s Secrets ou Vault. Não fique paralisado pela decisão: adote uma opção agora e migre depois, se necessário.

Etapa 3: crie os secrets

# se você usa Docker Swarm
docker swarm init
docker secret create db_password <(echo "senha")
docker secret create api_key <(echo "chave")

# se você usa file secrets do docker-compose
mkdir secrets
echo "senha" > secrets/db_password.txt
echo "chave" > secrets/api_key.txt
chmod 600 secrets/*

Etapa 4: altere o arquivo de configuração

Substitua os campos de environment no docker-compose.yml por secrets:

# antes
services:
  app:
    environment:
      DB_PASSWORD: "hardcoded_password"  # ❌

# depois
services:
  app:
    secrets:
      - db_password
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password  # ✅

Etapa 5: altere o código da aplicação, se necessário

Se o framework não oferecer suporte ao sufixo _FILE, adapte o código para ler o arquivo:

// exemplo em Node.js
const fs = require('fs');
const dbPassword = process.env.DB_PASSWORD_FILE
  ? fs.readFileSync(process.env.DB_PASSWORD_FILE, 'utf8').trim()
  : process.env.DB_PASSWORD;

Etapa 6: teste e valide

Primeiro, execute tudo no ambiente de teste e confirme que:

  • O serviço inicia normalmente
  • A senha é lida corretamente
  • docker inspect não mostra o valor em texto simples
  • As funcionalidades continuam funcionando

Etapa 7: implante em produção

Se tudo estiver certo, faça a implantação em produção. Por segurança, mantenha temporariamente a configuração antiga de variáveis de ambiente como fallback. Remova-a somente depois de confirmar que a nova solução está estável.

Manutenção contínua da segurança

Adotar Docker Secrets não resolve tudo para sempre. A segurança exige manutenção contínua.

Faça a rotação periódica dos segredos — a recomendação é a cada 90 dias

Crie um lembrete no calendário e troque as senhas a cada trimestre. Quando alguém sair da empresa, troque também todas as senhas relacionadas ao acesso dessa pessoa.

Monitore os logs de acesso aos segredos

Se você usa Vault, revise periodicamente os logs de auditoria e procure padrões de acesso anormais.

Docker Secrets e K8s Secrets não oferecem logs de auditoria por padrão, o que é uma limitação. Se você precisa desse controle, combine-os com outras ferramentas, como Falco.

Aplique o princípio do menor privilégio

Não permita que todos os serviços acessem todos os secrets. Cada serviço deve receber apenas o que realmente precisa:

services:
  frontend:
    secrets:
      - api_key  # o frontend só precisa da chave da API

  backend:
    secrets:
      - db_password  # o backend precisa da senha do banco
      - api_key

Use ferramentas de varredura de segredos

Integre estas ferramentas ao CI/CD:

Executá-las em um hook de pre-commit ajuda a impedir muitos envios acidentais.

Cinco cuidados para evitar armadilhas

Por fim, veja alguns erros comuns.

1. Não use ARG para transmitir informações sensíveis

# ❌ errado
ARG DB_PASSWORD=secret
ENV DATABASE_URL=postgres://user:${DB_PASSWORD}@db/myapp

O valor de ARG fica registrado no histórico de build da imagem e pode ser visto com docker history.

2. Não imprima secrets em stdout

# ❌ errado
password = open('/run/secrets/db_password').read()
print(f"Using password: {password}")  # aparecerá no docker logs!

O sistema de logs captura toda a saída stdout. Depois que uma senha entra nos logs, removê-la completamente é muito difícil.

3. Não use o mesmo secret em vários ambientes

Separe as senhas de desenvolvimento, teste e produção. Não reutilize o mesmo valor por conveniência; se o ambiente de teste for comprometido, a produção também ficará em risco.

4. Não copie o secret para outro caminho dentro do contêiner

# ❌ errado
cp /run/secrets/db_password /app/config/password.txt

/run/secrets usa tmpfs, um sistema de arquivos em memória cujo conteúdo desaparece quando o contêiner para. Se você copiar o arquivo para outro caminho, ele poderá ser gravado em disco, o que reduz a segurança.

Leia o valor diretamente de /run/secrets.

5. Não se esqueça de monitorar a expiração e a rotação dos secrets

Se você usa credenciais dinâmicas com prazo de validade, como as oferecidas pelo Vault, configure renovação ou rotação automática. Não espere a senha expirar no meio da noite e derrubar todos os serviços para descobrir o problema.

Conclusão

Depois de tudo isso, a ideia principal cabe em uma frase: não coloque senhas em lugares onde elas podem ser vistas.

Variáveis de ambiente parecem convenientes, mas docker inspect pode expor todos os valores. Instruções ENV no Dockerfile são ainda mais perigosas, pois ficam registradas permanentemente nas camadas da imagem. Muitos incidentes de segurança começam com pequenos atalhos adotados por conveniência.

Docker Secrets não é perfeito: exige o modo Swarm, não permite alterar o conteúdo de um secret e é pouco prático para desenvolvimento em uma única máquina. Ainda assim, resolve o problema central: os segredos são armazenados e transmitidos de forma criptografada e não aparecem nas variáveis de ambiente nem nas imagens. Para um cluster Docker Swarm, é a solução mais simples e direta.

Se você usa Kubernetes, escolha K8s Secrets. Para necessidades corporativas, considere Vault. Se toda a infraestrutura está na AWS, Secrets Manager também é uma boa opção. Não existe a melhor ferramenta para todos, apenas a mais adequada a cada cenário.

O importante é começar. Ainda hoje, verifique:

  • Há senhas no histórico do seu Git?
  • O arquivo .env está no .gitignore?
  • docker inspect consegue mostrar suas senhas?

Se as respostas forem “sim”, “não” e “sim”, reserve um tempo para corrigir isso. Você não precisa implantar Vault de uma vez; até mesmo substituir variáveis de ambiente por file secrets já melhora a situação.

Segurança é um processo contínuo, não uma configuração feita uma única vez. Faça a rotação periódica de senhas, monitore acessos anormais e use ferramentas de varredura no código. Esses hábitos são mais importantes que qualquer ferramenta isolada.

Por fim, compartilhe este artigo com sua equipe. O gerenciamento de segredos não é responsabilidade de uma única pessoa, mas de todos. Padronizar as práticas e fazer revisões periódicas é o que realmente reduz os riscos.

Espero que os alertas das três da manhã não voltem a acordar você.


Recursos de referência:

Processo completo para gerenciar segredos com segurança no Docker

Gerencie senhas e chaves de API de contêineres com segurança, compare Docker, Kubernetes e Vault e siga um checklist completo para produção

⏱️ Estimated time: 1 hr

  1. 1

    Step 1: Entenda o problema de segurança e as práticas erradas

    Problema de segurança:
    • O invasor usou diretamente a senha correta: a senha do MySQL gravada no docker-compose.yml
    • No mês anterior, um estagiário havia feito fork do projeto para um repositório público pessoal
    • O vazamento da senha levou a varreduras por força bruta no banco de dados

    Práticas erradas:
    • Colocar a senha diretamente no arquivo de configuração
    • Achar que usar variáveis de ambiente já resolve a segurança
    • Dizer 'meu repositório é privado, então não há problema'
    • Todas essas práticas criam riscos de segurança

    Erros comuns:
    • Colocar senhas no Dockerfile
    • Colocar senhas no docker-compose.yml
    • Usar senhas em variáveis de ambiente e enviá-las ao Git
    • Usar um arquivo .env e esquecer de adicioná-lo ao .gitignore
  2. 2

    Step 2: Use Docker Secrets para gerenciar segredos

    Solução com Docker Secrets:
    • Crie o segredo com docker secret create: docker secret create mysql_password -
    • Configure secrets no docker-compose.yml: secrets: - mysql_password
    • Acesse o segredo no contêiner pelo caminho /run/secrets/: cat /run/secrets/mysql_password
    • O segredo fica armazenado de forma criptografada no Docker Swarm

    Criação do segredo:
    • echo "your-password" | docker secret create mysql_password -
    • Referencie-o no docker-compose.yml: secrets: - mysql_password
    • Leia-o no contêiner: cat /run/secrets/mysql_password
  3. 3

    Step 3: Compare outras soluções e aplique as práticas recomendadas

    Comparação com outras soluções:
    • Docker Secrets (indicado para Docker Swarm, com armazenamento criptografado)
    • Kubernetes Secrets (indicado para ambientes K8s, com codificação Base64)
    • HashiCorp Vault (gerenciamento corporativo, com suporte a credenciais dinâmicas)
    • AWS Secrets Manager (indicado para ambientes AWS e integrado aos serviços da AWS)
    • Arquivo de variáveis de ambiente (.env, não recomendado para produção)

    Práticas recomendadas:
    • Em produção, use um gerenciador de secrets para informações sensíveis
    • Faça a rotação periódica dos segredos
    • Use uma ferramenta de varredura de segredos, como trufflehog, para verificar o repositório Git
    • Configure o controle de acesso para evitar vazamentos
    • Nunca grave senhas diretamente no código

FAQ

Por que não se deve colocar senhas no Dockerfile ou no docker-compose.yml?
Problema de segurança: o invasor usou diretamente a senha correta — a senha do MySQL gravada no docker-compose.yml. No mês anterior, um estagiário havia feito fork do projeto para um repositório público pessoal, e a senha exposta levou a varreduras por força bruta no banco de dados.

Práticas erradas:
• Colocar a senha diretamente no arquivo de configuração
• Achar que usar variáveis de ambiente já resolve a segurança
• Dizer 'meu repositório é privado, então não há problema'
• Todas essas práticas criam riscos de segurança

Erros comuns:
• Colocar senhas no Dockerfile
• Colocar senhas no docker-compose.yml
• Usar senhas em variáveis de ambiente e enviá-las ao Git
• Usar um arquivo .env e esquecer de adicioná-lo ao .gitignore
Como usar Docker Secrets para gerenciar segredos?
Solução com Docker Secrets:
• Crie o segredo com docker secret create (docker secret create mysql_password -)
• Configure secrets no docker-compose.yml (secrets: - mysql_password)
• Acesse o segredo no contêiner pelo caminho /run/secrets/ (cat /run/secrets/mysql_password)
• O segredo fica armazenado de forma criptografada no Docker Swarm

Criação do segredo:
• echo "your-password" | docker secret create mysql_password -
• Referencie-o no docker-compose.yml: secrets: - mysql_password
• Leia-o no contêiner: cat /run/secrets/mysql_password
Qual é a diferença entre Docker Secrets e outras soluções de gerenciamento de segredos?
Comparação com outras soluções:
• Docker Secrets (indicado para Docker Swarm, com armazenamento criptografado)
• Kubernetes Secrets (indicado para ambientes K8s, com codificação Base64)
• HashiCorp Vault (gerenciamento corporativo, com suporte a credenciais dinâmicas)
• AWS Secrets Manager (indicado para ambientes AWS e integrado aos serviços da AWS)
• Arquivo de variáveis de ambiente (.env, não recomendado para produção)

Recomendação de escolha:
• Em ambientes Docker Swarm, use Docker Secrets
• Em ambientes K8s, use Kubernetes Secrets
• Para necessidades corporativas, use Vault
• Em ambientes AWS, use Secrets Manager
Quais são as práticas recomendadas de gerenciamento de segredos em produção?
Práticas recomendadas:
• Em produção, use um gerenciador de secrets para informações sensíveis
• Faça a rotação periódica dos segredos
• Use uma ferramenta de varredura de segredos, como trufflehog, para verificar o repositório Git
• Configure o controle de acesso para evitar vazamentos
• Nunca grave senhas diretamente no código

Verificações periódicas:
• Use trufflehog para examinar o repositório Git
• Verifique se houve vazamento de segredos
• Configure uma varredura automática no fluxo de CI/CD
• Ao encontrar um vazamento, faça imediatamente a rotação do segredo

24 min de leitura · Publicado em: 18 dez 2025 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog