Olá! Vamos continuar nossa jornada pelas melhores práticas de segurança no Docker.
Introdução
Na lição anterior, você aprendeu a proteger o ambiente de execução dos seus contêineres, configurando limites de CPU e memória. Isso garante que seus serviços sejam resilientes e não sofram com o problema do "vizinho barulhento".
Hoje, vamos abordar outro pilar da segurança: o gerenciamento de dados sensíveis. Em qualquer aplicação, lidamos com senhas de banco de dados, chaves de API, tokens e outros segredos. A forma mais comum de gerenciar isso no desenvolvimento local é através de arquivos .env, mas essa abordagem tem suas fragilidades.
O objetivo desta lição é aprender a gerenciar dados sensíveis para desenvolvimento local usando os secrets do Docker Compose. Você entenderá por que esse mecanismo é mais seguro do que usar apenas variáveis de ambiente e como implementá-lo em seus projetos.
1. O Problema: Exposição de Segredos
Como desenvolvedor back-end, você certamente já usou arquivos .env para fornecer configurações a uma aplicação. O Docker Compose se integra perfeitamente com eles, substituindo variáveis no seu docker-compose.yml.
# docker-compose.yml
services:
db:
image: postgres:15
environment:
# A senha é lida de um arquivo .env
POSTGRES_PASSWORD: ${DB_PASSWORD}
# Este arquivo deve estar no .gitignore!
DB_PASSWORD=mySuperSecretPassword123
Embora seja melhor do que hardcodar a senha diretamente no docker-compose.yml, essa abordagem ainda expõe o segredo como uma variável de ambiente dentro do contêiner.
Para entender os riscos associados a isso, o artigo a seguir do blog da Phase descreve o problema de forma clara e objetiva.
Managing Secrets in Docker Compose — A Developer's Guide
Leia a primeira parte deste guia, "Managing Secrets in Docker Compose — A Developer's Guide", para entender por que confiar apenas em variáveis de ambiente para segredos pode ser arriscado.
Leia a seção introdutória até "Better Secret Management with Docker Compose". Foque nos pontos que explicam como as variáveis de ambiente podem ser inspecionadas e os exemplos de incidentes de segurança que ocorreram devido a essa exposição.
Como o artigo aponta, qualquer processo dentro do contêiner (ou qualquer pessoa com acesso ao Docker) pode facilmente ler essas variáveis usando docker inspect ou docker exec <container_id> env. Isso viola o Princípio do Menor Privilégio, pois dados sensíveis ficam mais acessíveis do que o necessário.
2. A Solução: Docker Compose secrets
Para mitigar esses riscos, o Docker Compose oferece um mecanismo nativo chamado secrets. Em vez de injetar segredos como variáveis de ambiente, ele os disponibiliza como arquivos dentro de um sistema de arquivos temporário e em memória (tmpfs) no contêiner.

A documentação oficial do Docker explica essa abordagem de forma concisa.
Vamos consultar a documentação oficial para entender os fundamentos dos secrets no Compose.
Leia as seções "Manage secrets securely in Docker Compose" e "Use secrets". Preste atenção em onde os segredos são montados por padrão (/run/secrets/) e no processo de duas etapas: definir o segredo no nível superior e referenciá-lo no serviço.
Principais Vantagens dos secrets:
- Não são variáveis de ambiente: O segredo não é exposto a todos os processos do contêiner. Apenas a aplicação que sabe ler o arquivo específico terá acesso.
- Armazenamento em memória: Os arquivos de segredo são escritos em um
tmpfs, o que significa que não são persistidos no disco do contêiner, reduzindo o risco de exposição. - Controle de acesso por serviço: Você concede explicitamente acesso a um segredo para cada serviço, em vez de expô-lo globalmente.
3. Implementando secrets na Prática
Vamos aplicar o conceito em um cenário comum e muito relevante para você: proteger a senha de um banco de dados PostgreSQL.
O vídeo a seguir, do canal Anton Putra, demonstra o processo completo, desde a criação dos arquivos de segredo até a configuração do docker-compose.yml.
Docker Compose Tutorial for Beginners (Networks - Volumes - Secrets - Postgres - Letsencrypt)
Este trecho do "Docker Compose Tutorial for Beginners" mostra um exemplo prático de como proteger a senha do PostgreSQL usando secrets.
Assista à seção de 44:43 a 47:04. Observe os três passos principais: Criar um arquivo local (ex: postgres_password.txt) contendo a senha. Definir o segredo no nível superior do docker-compose.yml, apontando para esse arquivo. Configurar o serviço do PostgreSQL para usar o segredo, utilizando a variável de ambiente especial POSTGRES_PASSWORD_FILE.
Vamos detalhar o processo visto no vídeo:
Passo 1: Crie o arquivo do segredo
Crie um arquivo de texto simples, por exemplo, db_password.txt, e coloque a senha dentro dele. Apenas a senha, sem espaços extras ou quebras de linha.
db_password.txt:
MySecureDbPassword!
Importante: Adicione este arquivo ao seu .gitignore!
# .gitignore
*.txt
Passo 2: Configure o docker-compose.yml
version: '3.8'
services:
db:
image: postgres:15
environment:
# Note que não usamos POSTGRES_PASSWORD
POSTGRES_USER: user
POSTGRES_DB: mydb
# Usamos a convenção _FILE para apontar para o caminho do segredo dentro do contêiner
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
# Concede a este serviço acesso ao segredo 'db_password'
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
api:
image: minha-api-net:latest
# A API também pode precisar do segredo para se conectar ao banco
secrets:
- db_password
environment:
# Sua API leria o arquivo em /run/secrets/db_password para obter a string de conexão
DB_HOST: db
DB_USER: user
DB_NAME: mydb
# Definição do segredo no nível superior
secrets:
db_password:
# Aponta para o arquivo local que contém o segredo
file: ./db_password.txt
volumes:
postgres_data:
A mágica acontece com a variável POSTGRES_PASSWORD_FILE. Muitas imagens oficiais (incluindo PostgreSQL, MySQL, Redis, etc.) seguem essa convenção: se você fornecer uma variável de ambiente com o sufixo _FILE, a imagem lerá o conteúdo do arquivo apontado por essa variável e o usará como o valor do segredo.
Isso permite que você use o mecanismo de secrets do Docker sem precisar modificar o código da sua aplicação (no caso de usar imagens de terceiros) ou implementando uma lógica simples para ler o arquivo, caso seja sua própria aplicação.
4. Controle Fino com a Sintaxe Longa
Para um controle ainda mais granular, especialmente importante em ambientes de produção, mas útil para entender o mecanismo completo, o Docker Compose oferece uma "sintaxe longa" para secrets.
Isso permite especificar:
target: O nome ou caminho completo do arquivo dentro do contêiner.uidegid: O ID do usuário e do grupo que serão donos do arquivo de segredo dentro do contêiner.mode: As permissões do arquivo em notação octal (ex:0400para legível apenas pelo dono).
Retornando ao excelente artigo da Phase, vamos explorar como usar essa sintaxe para fortalecer ainda mais a segurança.
Managing Secrets in Docker Compose — A Developer's Guide
Esta seção do artigo "Managing Secrets in Docker Compose" detalha a sintaxe longa e como usá-la para restringir o acesso ao segredo dentro do próprio contêiner.
Leia a seção "Controlling access to secrets supplied to your services". Ela explica cada campo da sintaxe longa e mostra um exemplo prático de como encontrar o uid e gid corretos para a imagem do PostgreSQL e aplicar permissões restritivas (0440).
Exemplo com Sintaxe Longa:
Imagine que a imagem do PostgreSQL é executada com o usuário postgres (UID 999). Podemos garantir que apenas esse usuário possa ler o arquivo de senha:
services:
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_DB: mydb
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
# Usando a sintaxe longa
- source: db_password
target: db_password # Nome do arquivo dentro de /run/secrets/
uid: '999' # UID do usuário 'postgres'
gid: '999' # GID do grupo 'postgres'
mode: 0400 # Apenas o dono (UID 999) pode ler
secrets:
db_password:
file: ./db_password.txt
Essa configuração impede que qualquer outro processo ou usuário dentro do contêiner, mesmo que comprometido, consiga ler o arquivo de senha.
Teste seu entendimento!
Você está configurando um serviço redis no Docker Compose. A imagem oficial do Redis usa a variável de ambiente REDIS_PASSWORD ou, para usar secrets, pode ser iniciada com um comando que aponta para um arquivo de senha (redis-server --requirepass-file /path/to/password).
Seu arquivo local com a senha é redis_pass.txt. Refatore o seguinte docker-compose.yml para usar secrets de forma segura. Descreva os passos.
docker-compose.yml original:
version: '3.8'
services:
cache:
image: redis:alpine
environment:
REDIS_PASSWORD: "HardcodedPassword123" # RUIM!
Show answer
Aqui estão os passos e o docker-compose.yml resultante:
-
Criar o arquivo de segredo: Crie um arquivo
redis_pass.txtcom o conteúdoMyRedisPassword. Adicioneredis_pass.txtao.gitignore. -
Modificar o
docker-compose.yml:- Remova a variável de ambiente
REDIS_PASSWORD. - Adicione o bloco
secretsno nível superior, apontando pararedis_pass.txt. - Adicione o bloco
secretsao serviçocache, concedendo-lhe acesso. - Use a sintaxe longa para renomear o segredo dentro do contêiner para
redis_passworde defina permissões seguras (o usuário do Redis na imagem alpine tem UID 999). - Adicione um
commandpara instruir o Redis a usar o arquivo de senha.
- Remova a variável de ambiente
docker-compose.yml refatorado:
version: '3.8'
services:
cache:
image: redis:alpine
# Instrua o redis-server a ler a senha do arquivo de segredo
command: redis-server --requirepass-file /run/secrets/redis_password
secrets:
- source: redis-secret
target: redis_password # Nome do arquivo que o comando acima espera
uid: '999'
gid: '999'
mode: 0400
secrets:
redis-secret:
file: ./redis_pass.txt
Conclusão
Hoje você deu um passo importante para tornar seus ambientes de desenvolvimento mais seguros, alinhando-os com as melhores práticas de produção. Gerenciar segredos corretamente desde o início evita dores de cabeça e vulnerabilidades no futuro.
Principais Pontos:
- Usar variáveis de ambiente para segredos é conveniente, mas arriscado, pois elas podem ser facilmente inspecionadas e vazadas.
- Os
secretsdo Docker Compose montam dados sensíveis como arquivos em um sistema de arquivos em memória (tmpfs), o que é inerentemente mais seguro. - Muitas imagens oficiais suportam a convenção
_FILE(ex:POSTGRES_PASSWORD_FILE), facilitando a adoção desecrets. - A sintaxe longa oferece controle granular sobre as permissões de acesso ao arquivo de segredo dentro do contêiner, implementando o Princípio do Menor Privilégio.
- Sempre adicione seus arquivos de segredo locais (ex:
.txt) ao seu.gitignore.
Na próxima lição, vamos explorar um cenário relacionado: como fornecer segredos necessários durante o processo de build da imagem (por exemplo, um token para acessar um repositório de pacotes privado), usando o recurso --secret do Docker BuildKit.