Segurança em containers Docker no DevOps

1. Princípios Fundamentais de Segurança em Containers

1.1. Isolamento vs. Compartilhamento: entendendo os riscos do kernel compartilhado

Diferente de máquinas virtuais, containers compartilham o kernel do host. Isso significa que uma vulnerabilidade no kernel pode comprometer todos os containers em execução. O isolamento fornecido por namespaces e cgroups é eficaz, mas não absoluto. Um atacante que escape do container pode acessar o host e, consequentemente, outros containers.

# Verificar namespaces ativos de um container
docker inspect --format '{{.State.Pid}}' meu-container
ls -la /proc/<PID>/ns/

1.2. O modelo de confiança zero aplicado a containers

No modelo Zero Trust, nenhum container é confiável por padrão. Toda comunicação deve ser autenticada e autorizada. Isso implica:

  • Não assumir que a rede interna é segura
  • Criptografar tráfego entre containers (mTLS)
  • Validar identidade antes de qualquer comunicação
# Exemplo de rede isolada no Docker
docker network create --internal rede-isolada
docker run --network rede-isolada --name app-segura nginx:alpine

1.3. Superfície de ataque reduzida com imagens mínimas e imutáveis

Imagens mínimas como alpine (5MB) ou distroless (sem shell, sem package manager) reduzem drasticamente a superfície de ataque. Imagens imutáveis garantem que o mesmo hash sempre produza o mesmo ambiente.

# Comparação de tamanho de imagens base
docker images | grep -E "ubuntu|alpine|distroless"
ubuntu:22.04        ~77MB
alpine:3.19         ~7MB
gcr.io/distroless/base-debian12  ~25MB (sem shell)

2. Imagens Seguras: Da Construção ao Registry

2.1. Boas práticas de Dockerfile: usuários não-root e camadas mínimas

O princípio do menor privilégio exige que containers nunca executem como root. Combine isso com camadas mínimas para reduzir vulnerabilidades.

# Dockerfile seguro
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder /app /app
WORKDIR /app
EXPOSE 3000
CMD ["node", "server.js"]

2.2. Multi-stage builds para eliminar ferramentas de build e secrets

Multi-stage builds permitem que ferramentas de compilação e secrets fiquem apenas em estágios intermediários, não na imagem final.

# Multi-stage build com segurança
FROM golang:1.21 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app .

FROM scratch
COPY --from=build /app /app
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/app"]

2.3. Scan de vulnerabilidades (Trivy, Snyk) antes do push para o Registry

Integrar scanners no pipeline CI/CD evita que imagens vulneráveis cheguem à produção.

# Scan com Trivy no pipeline
trivy image --severity HIGH,CRITICAL --exit-code 1 minha-imagem:latest

# Exemplo de saída
# Total: 3 (HIGH: 2, CRITICAL: 1)
# CVE-2024-XXXXX (CRITICAL) - pacote libssl1.1

3. Controle de Acesso e Gerenciamento de Secrets

3.1. Evitando secrets em variáveis de ambiente e camadas da imagem

Secrets em variáveis de ambiente ficam visíveis em docker inspect e logs. Prefira montar arquivos de secrets ou usar ferramentas especializadas.

# ❌ Errado: secret em variável de ambiente
docker run -e DB_PASSWORD=senha123 meu-app

# ✅ Correto: secret montado como arquivo
docker run --secret db_password meu-app

3.2. Uso de Docker Secrets e ferramentas externas

Docker Swarm oferece secrets nativos. Para Kubernetes, use Secrets do Kubernetes ou HashiCorp Vault.

# Docker Swarm Secret
echo "minha-senha-segura" | docker secret create db_password -
docker service create --secret db_password --name app meu-app

# HashiCorp Vault (exemplo conceitual)
vault kv put secret/db password=supersecret

3.3. RBAC no Docker Registry e autenticação para pull/push

Configure autenticação e autorização no registry para evitar acesso não autorizado.

# Configurar autenticação básica no Docker Registry
docker run -d -p 5000:5000 --name registry \
  -v /auth:/auth \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  registry:2

# Login
docker login localhost:5000 -u admin -p senha

4. Hardening do Host e do Docker Daemon

4.1. Configuração segura do Docker daemon

Habilite TLS para comunicação com o daemon, use rootless mode e perfis de segurança.

# /etc/docker/daemon.json
{
  "tls": true,
  "tlscacert": "/etc/docker/ca.pem",
  "tlscert": "/etc/docker/server-cert.pem",
  "tlskey": "/etc/docker/server-key.pem",
  "userns-remap": "default",
  "selinux-enabled": true,
  "live-restore": true
}

4.2. Limitação de recursos com cgroups e ulimits

Evite ataques de negação de serviço limitando CPU, memória e número de processos.

# Limitar recursos do container
docker run -d --name app \
  --memory="512m" \
  --cpus="0.5" \
  --ulimit nofile=1024:2048 \
  --pids-limit=100 \
  nginx:alpine

4.3. Uso de runtime seccomp e perfis de segurança personalizados

Seccomp restringe chamadas de sistema que o container pode fazer.

# Perfil seccomp personalizado (apenas chamadas essenciais)
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {"names": ["read", "write", "open", "close", "exit", "exit_group"], "action": "SCMP_ACT_ALLOW"}
  ]
}

# Aplicar ao container
docker run --security-opt seccomp=perfil-seguro.json nginx:alpine

5. Healthchecks e Resiliência como Camada de Segurança

5.1. Healthchecks para detecção de falhas e auto-recuperação

Healthchecks evitam que containers com falha continuem recebendo tráfego.

# Dockerfile com healthcheck
FROM nginx:alpine
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

5.2. Prevenção de containers zumbis e loops de restart

Configure restart policies adequadas e evite loops infinitos.

# Política de restart segura
docker run --restart=on-failure:5 --memory="256m" meu-app

5.3. Integração com orquestradores (Kubernetes liveness/readiness probes)

No Kubernetes, probes garantem que apenas containers saudáveis recebam tráfego.

# Kubernetes deployment com probes
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: app
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5

6. Monitoramento, Logs e Auditoria

6.1. Logs centralizados e seguros

Evite expor dados sensíveis em logs. Use drivers de log seguros e filtre informações confidenciais.

# Configurar driver de log json-file com rotação
docker run --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  --log-opt labels=environment \
  meu-app

# No docker-compose.yml
logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

6.2. Detecção de anomalias com Falco

Falco monitora chamadas de sistema e detecta comportamentos suspeitos.

# Instalar Falco
docker run -d --name falco \
  --privileged \
  -v /var/run/docker.sock:/host/var/run/docker.sock \
  -v /proc:/host/proc:ro \
  falcosecurity/falco

# Regra personalizada (exemplo)
- rule: Shell em container
  desc: Detecta shell interativo em container
  condition: spawned_process and container and proc.name = bash
  output: "Shell detectado (user=%user.name container=%container.name)"
  priority: WARNING

6.3. Rastreamento de atividades do container

Eventos do Docker daemon fornecem auditoria detalhada.

# Monitorar eventos do Docker
docker events --filter 'type=container' --filter 'event=start' --filter 'event=stop'

# Kubernetes audit logs (exemplo de policy)
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
  resources:
  - group: ""
    resources: ["pods", "secrets"]

7. Integração Contínua e Segurança no Pipeline DevOps

7.1. Pipeline CI/CD com gate de segurança

Integre scanning e assinatura de imagens no pipeline.

# Pipeline conceitual (GitHub Actions)
jobs:
  security:
    steps:
    - name: Scan de vulnerabilidades
      run: trivy image --severity HIGH,CRITICAL --exit-code 1 minha-imagem

    - name: Assinar imagem com Cosign
      run: |
        cosign sign --key cosign.key minha-imagem:latest
        cosign verify --key cosign.pub minha-imagem:latest

    - name: Push para registry
      run: docker push minha-imagem:latest

7.2. Políticas de imagem permitidas

No Kubernetes, use ImagePolicyWebhook para restringir quais imagens podem ser implantadas.

# ImagePolicyWebhook (exemplo de configuração)
apiVersion: v1
kind: ImagePolicy
spec:
  imageRules:
  - match:
      prefix: "registry-seguro.com/"
    deny:
      conditions:
      - expression: "!image.endsWith(':signed')"

7.3. Implantação automatizada com verificação de compliance

Use OPA/Gatekeeper para aplicar políticas de segurança automaticamente.

# OPA Gatekeeper - proibir containers privilegiados
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: prohibit-privileged-containers
spec:
  match:
    kinds:
    - apiGroups: [""]
      kinds: ["Pod"]
  parameters:
    exemptImages: ["gcr.io/k8s-prow/*"]

Referências