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
latestem 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
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
ENVno Dockerfile, nem em arquivos copiados). Qualquer um com acesso à imagem as lê comdocker history. - No Compose, use o recurso
secrets:, que monta o valor como arquivo em/run/secrets/, e mantenha o.envfora do Git e com permissão600. - No Kubernetes, Secrets são apenas codificados em base64, não criptografados. Ative a criptografia em repouso (no k3s,
secrets-encryption: truenoconfig.yaml; confira comk3s 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.
