Segurança em containers Docker para Devs
1. Princípios fundamentais de segurança em containers
1.1. O modelo de segurança do Docker: isolamento vs. compartilhamento do kernel
Diferente de máquinas virtuais, containers Docker compartilham o kernel do host. Isso significa que uma falha de segurança no kernel pode comprometer todos os containers. O Docker utiliza namespaces para isolar processos, rede e sistema de arquivos, e cgroups para limitar recursos. Entender essa arquitetura é o primeiro passo para escrever código seguro.
# Verificar namespaces ativos de um container
docker inspect --format '{{.State.Pid}}' meu-container
ls -la /proc/<PID>/ns/
1.2. A superfície de ataque de uma imagem: da base ao runtime
Cada camada de uma imagem Docker adiciona potencial vulnerabilidade. Uma imagem base desatualizada, pacotes desnecessários ou configurações inseguras aumentam a superfície de ataque. Como dev, você controla desde a escolha da imagem até como o container é executado.
1.3. Boas práticas de namespace e cgroups para desenvolvedores
Sempre execute containers com privilégios mínimos. Evite --privileged e prefira capacidades específicas quando necessário.
# Exemplo: container com privilégios mínimos
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE -p 8080:8080 minha-app
2. Construção de imagens seguras (Secure Build)
2.1. Escolha de imagens base oficiais e minimalistas
Prefira imagens oficiais e minimalistas como Alpine (5MB) ou Google Distroless. Elas reduzem drasticamente a superfície de ataque.
# Dockerfile inseguro
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3 nodejs curl vim
# Dockerfile seguro
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY . .
USER node
CMD ["node", "app.js"]
2.2. Uso de multi-stage builds para reduzir o tamanho
Multi-stage builds eliminam ferramentas de build desnecessárias da imagem final.
# Exemplo de multi-stage build
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /server
USER nobody
EXPOSE 8080
CMD ["/server"]
2.3. Fixação de versões e verificação de assinaturas
Sempre use tags específicas (nunca latest) e ative Docker Content Trust para verificar assinaturas.
# Fixar versão da imagem base
FROM node:20.11.0-alpine3.19
# Ativar Docker Content Trust
export DOCKER_CONTENT_TRUST=1
docker pull node:20.11.0-alpine3.19
3. Gerenciamento de vulnerabilidades com scanning
3.1. Integração do Trivy no pipeline CI/CD
Trivy é uma ferramenta open-source que escaneia imagens Docker por vulnerabilidades conhecidas.
# Instalar Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Escanear imagem
trivy image --severity HIGH,CRITICAL minha-app:latest
# Integração em pipeline GitHub Actions
name: Security Scan
on: [push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t minha-app .
- name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'minha-app'
format: 'table'
exit-code: '1'
3.2. Interpretação de relatórios de vulnerabilidades
CVEs (Common Vulnerabilities and Exposures) são identificadores únicos para vulnerabilidades. Priorize correção de CVEs com severidade CRITICAL e HIGH. Falsos positivos ocorrem quando a vulnerabilidade existe na base image mas não é acessível no runtime.
3.3. Estratégias de remediação
- Rebuild: Atualizar pacotes na imagem atual
- Patch: Aplicar correções específicas
- Substituição: Trocar para uma base image mais recente ou diferente
# Exemplo de rebuild com atualização de pacotes
FROM alpine:3.19
RUN apk update && apk upgrade --no-cache
COPY --from=builder /app/server /server
CMD ["/server"]
4. Execução segura de containers (Secure Runtime)
4.1. Uso de usuários não-root
Nunca execute containers como root. Crie um usuário específico no Dockerfile.
# Dockerfile seguro
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --chown=appuser:appgroup . /app
WORKDIR /app
CMD ["node", "app.js"]
# Ou no runtime
docker run --user 1000:1000 minha-app
4.2. Limitação de capabilities
Capabilities Linux são privilégios específicos. Remova todas e adicione apenas as necessárias.
# Remover todas as capabilities e adicionar apenas NET_BIND_SERVICE
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE minha-app
4.3. Filesystem read-only e volumes temporários
Proteja o filesystem do container contra escrita não autorizada.
# Container com filesystem read-only
docker run --read-only --tmpfs /tmp --tmpfs /var/run minha-app
5. Proteção de segredos e variáveis sensíveis
5.1. Evitando segredos em Dockerfiles
Nunca hardcode senhas ou chaves. O cache de camadas do Docker pode expor esses dados.
# ERRADO - segredo exposto no cache
FROM alpine
ENV DB_PASSWORD=minha_senha
# CERTO - usar variáveis em tempo de execução
docker run -e DB_PASSWORD=minha_senha minha-app
5.2. Uso de Docker secrets (Swarm)
Para ambientes orquestrados, use Docker Secrets.
# Criar secret
echo "minha_senha" | docker secret create db_password -
# Usar no serviço
docker service create --secret db_password --name meu-servico minha-imagem
5.3. Integração com cofres externos
Para produção, integre com HashiCorp Vault ou AWS Secrets Manager.
# Exemplo com Vault (pseudo-código)
from hvac import Client
client = Client(url='http://vault:8200')
client.auth.approle.login(role_id=ROLE_ID, secret_id=SECRET_ID)
secret = client.read('secret/data/db')['data']['data']['password']
6. Boas práticas de rede e isolamento
6.1. Redes definidas pelo usuário
Use redes bridge customizadas para isolar containers entre si.
# Criar rede isolada
docker network create --driver bridge minha-rede
# Conectar containers à rede
docker run --network minha-rede --name api minha-app
docker run --network minha-rede --name db postgres:16-alpine
6.2. Exposição mínima de portas
Exponha apenas portas necessárias e restrinja o binding.
# Expor apenas porta 8080 no localhost
docker run -p 127.0.0.1:8080:8080 minha-app
6.3. Políticas de rede
Evite links obsoletos (--link) e use redes definidas pelo usuário com comunicação via DNS.
7. Auditoria e monitoramento de segurança
7.1. Análise de logs do daemon Docker
Monitore eventos do daemon Docker para detectar atividades suspeitas.
# Visualizar eventos do Docker
docker events --filter 'event=create' --filter 'event=start'
# Logs do daemon
journalctl -u docker.service
7.2. Ferramentas de detecção
Docker Bench Security verifica conformidade com benchmarks de segurança.
# Executar Docker Bench Security
docker run --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-security
Falco monitora chamadas de sistema em tempo real.
7.3. Checklist final para produção
# Checklist de segurança
1. Imagem base oficial e minimalista
2. Usuário não-root
3. Capabilities mínimas
4. Filesystem read-only
5. Secrets gerenciados externamente
6. Rede isolada
7. Scan de vulnerabilidades no CI/CD
8. Logs ativos
9. Atualizações regulares
10. Docker Bench Security em execução
Referências
- Docker Security Documentation — Documentação oficial sobre segurança no Docker Engine
- Docker Bench Security — Ferramenta oficial para verificar conformidade com benchmarks CIS
- Trivy Documentation — Documentação completa do scanner de vulnerabilidades Trivy
- OWASP Docker Security Cheat Sheet — Guia prático de segurança Docker da OWASP
- CIS Docker Benchmark — Benchmark oficial de segurança para Docker do Center for Internet Security
- Google Distroless Images — Imagens base minimalistas mantidas pelo Google
- Falco Security — Ferramenta de detecção de anomalias em containers CNCF