Segurança em containers Docker: práticas essenciais

Docker não isola containers do host da mesma forma que uma VM. Um container rodando como root com acesso ao socket Docker pode comprometer todo o servidor. As práticas mais importantes: nunca expor /var/run/docker.sock para containers não confiáveis, nunca rodar processos como root dentro de containers quando evitável, configurar limites de recursos e usar imagens de fontes verificadas.

Rodar containers como usuário não-root

Containers que rodam como root têm permissões iguais ao root do host se o container for comprometido. Use usuários não-root sempre que possível:

bash
# No Dockerfile — criar usuário e trocar antes do CMD
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --production
# Cria usuário com UID fixo e sem home
RUN addgroup -g 1001 -S appgroup && adduser -u 1001 -S appuser -G appgroup
USER appuser
CMD ["node", "server.js"]

# No docker-compose.yml — forçar usuário via Compose
services:
  app:
    image: minha-app:latest
    user: "1001:1001"

Nunca expor o Docker socket em containers não confiáveis

Montar /var/run/docker.sock dentro de um container dá controle total sobre o Docker daemon, e por extensão, sobre todo o servidor:

bash
# PERIGO — nunca faça isso para containers de usuários finais ou workloads não confiáveis:
volumes:
  - /var/run/docker.sock:/var/run/docker.sock   # controle total do Docker!

# QUANDO é necessário (ex.: Portainer, Traefik, CI runners):
# Restrinja com read-only quando possível:
volumes:
  - /var/run/docker.sock:/var/run/docker.sock:ro

# Alternativa mais segura: Portainer Agent (proxy com permissões restritas)
# ao invés de montar o socket diretamente

Filesystem read-only e capabilities

Containers com filesystem somente leitura e capabilities reduzidas têm superfície de ataque muito menor:

yaml
services:
  app:
    image: minha-app:latest
    read_only: true           # filesystem imutável (exceto volumes montados)
    tmpfs:
      - /tmp                  # área de escrita temporária se necessário
    security_opt:
      - no-new-privileges:true # processo não pode ganhar mais privilégios que o pai
    cap_drop:
      - ALL                   # remove todas as capabilities Linux
    cap_add:
      - NET_BIND_SERVICE      # adiciona de volta apenas o que for necessário

Limitar recursos para isolar falhas

Sem limites de recursos, um container com bug (memory leak, loop infinito) pode consumir toda a CPU e RAM do host, derrubando os demais serviços:

yaml
services:
  app:
    image: minha-app:latest
    deploy:
      resources:
        limits:
          memory: 512M    # container morto (OOMKilled) se ultrapassar
          cpus: '1.0'     # máximo 1 core (pode usar frações)
        reservations:
          memory: 256M    # garante pelo menos 256MB disponíveis
          cpus: '0.25'    # reserva 0.25 core

  # Limite de processos (previne fork bombs)
  worker:
    image: worker:latest
    ulimits:
      nproc: 100          # máximo 100 processos dentro do container

Usar imagens verificadas e atualizadas

Imagens desatualizadas contêm vulnerabilidades conhecidas. Práticas para manter imagens seguras:

bash
# Usar imagens oficiais (Docker Hub "Official Image" badge)
# Preferir variantes slim/alpine (menor superfície)
FROM node:20-alpine    # menor footprint que node:20
FROM python:3.11-slim  # melhor que python:3.11 (full)

# Verificar vulnerabilidades com docker scout (CLI)
docker scout cves nome_da_imagem:tag

# Atualizar imagens regularmente
docker compose pull    # baixa versões atualizadas das imagens fixadas

# Verificar digest para garantir integridade
docker pull postgres:16@sha256:abc123...  # imagem imutável via digest

Não quer configurar manualmente?

Implante o Docker em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

Menos isolado. VMs têm hypervisor como barreira, uma VM comprometida não acessa outras VMs nem o host facilmente. Containers compartilham o kernel do host, um exploit de kernel pode escalar para o host. Para workloads de alta segurança, use VMs com Docker dentro, ou Kata Containers (VMs leves que executam containers).

docker run --privileged dá ao container acesso completo ao hardware e todas as syscalls do host, evite completamente em produção. Use apenas para ferramentas de diagnóstico de sistema (nsenter, strace em debug) e nunca em containers expostos à internet. Quase sempre existe alternativa com cap_add específico.

docker scout cves <imagem> (CLI do Docker, requer conta Docker Hub). Grype (open-source, roda localmente: grype nome_da_imagem). Trivy (open-source da Aqua Security, muito usado em CI: trivy image nome_da_imagem). Integre em seu pipeline de CI para bloquear deploys com CVEs críticos.

Crie redes customizadas no Compose e coloque apenas os containers que precisam se comunicar na mesma rede. Exemplo: app e db na rede backend, nginx e app na rede frontend. Containers em redes diferentes não se comunicam por padrão, isolamento sem configuração adicional.

Para pequenas equipes: arquivo .env não commitado no git. Para times maiores: Docker Secrets (nativo no Swarm), Vault (HashiCorp), ou variáveis de ambiente injetadas via CI/CD (GitHub Actions secrets, etc.). Nunca coloque secrets diretamente no Dockerfile ou no docker-compose.yml commitado.