Containers mais seguros: imagens mínimas, sem root, varredura com Trivy e secrets bem guardados no Docker e no Kubernetes.

Para que serve

Este artigo reúne as práticas de segurança em containers que mais reduzem risco com menos esforço, valendo tanto para Docker quanto para Kubernetes: usar imagens pequenas, rodar processos sem root, procurar vulnerabilidades com o Trivy e tratar senhas e chaves como secrets, e não como variáveis espalhadas em arquivos. Um container comprometido rodando como root, com a imagem cheia de ferramentas e senhas no ambiente, é um atalho para o atacante chegar ao host e aos bancos de dados.

Para a parte de rede (o Docker publica portas passando por cima do UFW), veja o artigo Docker e firewall: por que o Docker ignora o UFW e como corrigir.

Pré-requisitos

  • Docker ou Podman instalado, e/ou um cluster Kubernetes (k3s) com kubectl.
  • Acesso aos Dockerfiles e manifestos das suas aplicações.

Etapa 1: usar imagens mínimas e com versão fixa

Cada pacote a mais na imagem é uma vulnerabilidade em potencial e uma ferramenta a mais para quem invadir. Use multi-stage build: compile em uma imagem completa e copie só o resultado para uma imagem enxuta (distroless, Alpine ou mesmo scratch para binários estáticos).

# Dockerfile de exemplo para uma aplicação Go
FROM golang:1.25 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/app

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]
  • Evite a tag latest em produção. Use uma versão (nginx:1.28-alpine) ou, melhor, o digest (nginx@sha256:...), que garante a mesma imagem sempre.
  • Prefira imagens oficiais ou do próprio projeto. Desconfie de imagens de terceiros desconhecidos no Docker Hub.
  • Reconstrua as imagens periodicamente para receber correções da imagem base.

Etapa 2: rodar como usuário não-root

No Dockerfile, crie um usuário e use a instrução USER. Se a imagem pronta já roda como root, você pode forçar outro usuário no Compose (user: "1000:1000") quando a aplicação permitir.

No Kubernetes, declare o securityContext em cada Deployment. Este bloco atende ao perfil restricted do Kubernetes:

spec:
  template:
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: app
          image: registry.seudominio.com.br/app:1.4.2
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: tmp
          emptyDir: {}

Com readOnlyRootFilesystem, a aplicação só grava onde você montar um volume (como o /tmp acima). Se o processo precisar escutar em porta abaixo de 1024, mude a porta interna (ex.: 8080) em vez de devolver privilégios.

Etapa 3: aplicar Pod Security Admission por namespace

O Kubernetes valida os pods contra os padrões privileged, baseline e restricted com rótulos no namespace. Comece em modo de aviso para ver o que quebraria:

kubectl label namespace site \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/audit=restricted

Quando os avisos sumirem, passe a impor: kubectl label namespace site pod-security.kubernetes.io/enforce=restricted --overwrite. Namespaces de componentes de sistema (Longhorn, MetalLB) costumam precisar de privileged; não aplique restricted neles.

Etapa 4: escanear vulnerabilidades com o Trivy

Atenção à origem do Trivy: em março de 2026 o projeto sofreu um ataque à cadeia de suprimentos (CVE-2026-33634). Com credenciais roubadas, o invasor publicou uma versão maliciosa do Trivy (v0.69.4) e trocou quase todas as tags das GitHub Actions trivy-action e setup-trivy por código que roubava credenciais de CI. Instale pelo repositório oficial assinado, use a versão indicada no comunicado da Aqua Security e, em pipelines, fixe actions pelo hash do commit, não pela tag. Se você rodou o Trivy em CI entre 19 e 20 de março de 2026, siga as orientações do comunicado e troque os segredos que o pipeline tinha.
# Debian/Ubuntu: repositório oficial
apt install -y wget gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor -o /usr/share/keyrings/trivy.gpg
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" > /etc/apt/sources.list.d/trivy.list
apt update && apt install -y trivy
trivy --version

Usos mais comuns:

# imagem: só falhas altas e críticas que já têm correção
trivy image --severity HIGH,CRITICAL --ignore-unfixed nginx:1.28-alpine

# Dockerfile e manifestos: erros de configuração (root, privileged, sem limits)
trivy config ./k8s/

# senhas e chaves esquecidas no código
trivy fs --scanners secret .

# resumo do cluster inteiro (usa o kubeconfig atual)
trivy k8s --report summary

No pipeline de build, use --exit-code 1 para barrar imagens com falhas críticas corrigíveis.

Etapa 5: tratar secrets corretamente

  • Nunca coloque senhas na imagem (nem em ENV no Dockerfile, nem em arquivos copiados). Qualquer um com acesso à imagem as lê com docker history.
  • No Compose, use o recurso secrets:, que monta o valor como arquivo em /run/secrets/, e mantenha o .env fora do Git e com permissão 600.
  • No Kubernetes, Secrets são apenas codificados em base64, não criptografados. Ative a criptografia em repouso (no k3s, secrets-encryption: true no config.yaml; confira com k3s secrets-encrypt status) e limite com RBAC quem pode ler Secrets.
  • Para versionar no Git, use ferramentas que cifram antes do commit (SOPS com age, Sealed Secrets) ou que buscam o valor num cofre (External Secrets Operator com Vault/OpenBao).
  • Prefira montar secrets como arquivo a passá-los como variável de ambiente: variáveis aparecem em dumps de erro e em ferramentas de diagnóstico.

Como saber se funcionou

kubectl -n site exec deploy/app -- id            # uid diferente de 0
kubectl -n site exec deploy/app -- touch /teste  # deve falhar: read-only
trivy image --severity CRITICAL registry.seudominio.com.br/app:1.4.2
kubectl get ns --show-labels | grep pod-security

Problemas comuns

Aplicação falha com "permission denied" ao rodar sem root

Ela tenta gravar em pastas da imagem que pertencem ao root. Ajuste a propriedade no Dockerfile (COPY --chown) ou monte um volume na pasta de escrita. Para volumes, o fsGroup ajusta o grupo dos arquivos.

Imagem oficial exige root (ex.: escuta na porta 80)

Muitos projetos publicam variantes sem root (como nginxinc/nginx-unprivileged, que escuta na 8080). Procure por elas antes de abrir exceções.

Trivy mostra centenas de vulnerabilidades

Filtre por HIGH,CRITICAL com --ignore-unfixed e troque a imagem base por uma mínima; normalmente a lista cai drasticamente.

Perguntas frequentes

Rootless Docker ou Podman resolvem tudo?

Eles reduzem o impacto de uma fuga do container, porque o daemon ou o processo não é root no host. Ainda assim, vale aplicar as demais práticas. Veja Podman como alternativa ao Docker.

Montar o docker.sock em um container é perigoso?

Sim. Quem controla o socket do Docker controla o host como root. Só faça isso com ferramentas confiáveis e, se possível, através de um proxy de socket com permissões limitadas.

Com que frequência devo escanear?

A cada build e, para o que está em produção, pelo menos semanalmente, porque novas falhas são publicadas todos os dias para imagens que não mudaram.

Leitura complementar

Precisa de ajuda?

Se você suspeita que um container foi comprometido, isole o servidor e siga o artigo O que fazer se você suspeitar que seu servidor foi invadido. Para acesso de emergência pelo IPMI/iLO, abra um ticket.

Esta resposta lhe foi útil? 0 Usuários acharam útil (0 Votos)

Leia também