tutorial: configurar mTLS entre Traefik e serviços em Docker Compose usando smallstep CA e certificados automáticos — passo a passo

Docker & Containers · Por · Publicado em · 12 min de leitura

close-up de tela com código fonte colorido em editor, foco seletivo e linhas de sintaxe destacadas
Visual técnico adequado para tutorial de mTLS entre Traefik e serviços Docker. Foto: Godfrey_atima / Pixabay

Este é um tutorial: configurar mTLS entre Traefik e serviços em Docker Compose usando smallstep CA e certificados automáticos. Você verá arquitetura, arquivos, comandos e verificação ponta a ponta. O Traefik autenticará no backend via certificado de cliente, e os serviços exigirão mTLS para aceitar conexões, com emissão e renovação automatizadas pela step-ca.

Arquitetura: como o mTLS entre Traefik e serviços funciona

O Traefik atuará como proxy reverso e se conectará aos serviços de backend por TLS, apresentando um certificado de cliente e validando o certificado do servidor. O backend exigirá um certificado cliente válido para aceitar a conexão.

Usaremos uma Autoridade Certificadora local (smallstep step-ca) para emitir: (1) um certificado de cliente para o Traefik e (2) um certificado de servidor para cada serviço. A renovação é automática com o step CLI.

Esta abordagem protege a conexão interna, reduz ataques de man-in-the-middle e autentica ambos os lados da conexão. Se estiver avaliando sua PKI, compare alternativas em smallstep vs HashiCorp Vault para mTLS em Docker Compose.

  • Traefik valida o certificado do servidor (CA raiz confiável).
  • Serviço valida o certificado de cliente do Traefik (mTLS obrigatório).
  • Certificados emitidos e renovados pela step-ca via step CLI.
  • Roteamento HTTP externo continua simples; mTLS é aplicado apenas no upstream Traefik→serviços.
Componente Certificados/Arquivos Finalidade
step-ca root_ca.crt, intermediária, ca.json Emitir e renovar certificados
Traefik traefik-client.crt, traefik-client.key, root_ca.crt mTLS como cliente; confiar no backend
Serviço (ex.: Nginx) server.crt, server.key, root_ca.crt TLS do servidor e exigência de cliente

Pré-requisitos e escopo do ambiente

Você precisa de Docker e Docker Compose instalados, acesso shell e portas livres 80 e 9000. Vamos criar uma rede Docker única, com três serviços: step-ca, Traefik e um backend Nginx como exemplo.

Os domínios usados são locais (app.localhost) e nomes internos (app.internal). Adapte para sua realidade. Não exponha a CA na internet; mantenha-a acessível apenas na rede interna do Compose.

  • Docker e Compose atualizados.
  • Portas 80 (Traefik) e 9000 (step-ca) livres.
  • Editor de texto para criar arquivos.
  • Conhecimento básico de YAML e TLS (SAN, CA).

Como iniciar uma smallstep CA no Docker e obter o fingerprint?

A smallstep CA (step-ca) será nossa PKI interna. Vamos inicializar o repositório da CA em um volume Docker, gerar root/intermediária e guardar a senha. O fingerprint da raiz será usado para bootstrap seguro pelos clientes.

Os comandos abaixo criam o volume, inicializam a CA e imprimem o fingerprint da raiz.

# 1) Crie o volume de dados da CA
docker volume create step-ca

# 2) Gere uma senha e inicialize a estrutura de diretórios
# (a senha protege as chaves privadas da CA)
docker run --rm -v step-ca:/home/step alpine sh -c '
  set -e; mkdir -p /home/step/secrets;
  head -c 16 /dev/urandom | base64 > /home/step/secrets/password
'

# 3) Inicialize a CA (nome, DNS interno e provisionador de login)
docker run --rm -it -v step-ca:/home/step smallstep/step-ca \
  step ca init \
    --deployment-type standalone \
    --name "Local Dev CA" \
    --dns "step-ca" \
    --address ":9000" \
    --provisioner "[email protected]" \
    --password-file /home/step/secrets/password

# 4) Descubra o fingerprint da raiz (copie para usar no Compose)
docker run --rm -v step-ca:/home/step smallstep/step-ca \
  step certificate fingerprint /home/step/certs/root_ca.crt

# Guarde o fingerprint impresso (ex.: 7f:ab:...): vamos usar como STEP_FINGERPRINT.

Como emitir e renovar certificados automáticos no serviço backend (Nginx)?

O backend precisa de dois itens: (a) um certificado de servidor emitido pela CA e (b) a configuração do Nginx para exigir certificado de cliente. Usaremos uma imagem customizada com o step CLI para emitir e renovar o certificado, recarregando o Nginx quando o arquivo mudar.

Crie os arquivos abaixo no diretório ./app. A renovação roda em segundo plano e dispara reload do Nginx sem interromper conexões ativas.

# file: app/Dockerfile
FROM nginx:alpine
RUN apk add --no-cache curl tar ca-certificates bash \
 && update-ca-certificates
# Instala o step CLI (verifique a URL mais recente conforme sua política interna)
RUN curl -fsSL https://dl.smallstep.com/cli/releases/latest/step_linux_amd64.tar.gz \
  | tar -xz -C /usr/local/bin --strip-components=1 'step_*/bin/step'
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

# file: app/nginx.conf
server {
  listen 443 ssl http2;
  server_name app.internal;

  ssl_certificate     /etc/nginx/certs/server.crt;
  ssl_certificate_key /etc/nginx/certs/server.key;

  # Exigir certificado de cliente (mTLS)
  ssl_client_certificate /step/certs/root_ca.crt; # CA raiz confiável
  ssl_verify_client on;

  # End-point exemplo
  location / {
    add_header Content-Type text/plain;
    return 200 'ok from app (mTLS required)\n';
  }
}

# file: app/entrypoint.sh
#!/usr/bin/env bash
set -euo pipefail
: "${STEP_FINGERPRINT:?Defina STEP_FINGERPRINT no compose}"
: "${STEP_PROVISIONER:?Defina STEP_PROVISIONER no compose}"

# Bootstrap confiança na CA interna (via DNS step-ca na rede Docker)
step ca bootstrap --ca-url https://step-ca:9000 --fingerprint "$STEP_FINGERPRINT"

# Emite o certificado de servidor (SAN: app.internal)
mkdir -p /etc/nginx/certs
step ca certificate "app.internal" \
  /etc/nginx/certs/server.crt /etc/nginx/certs/server.key \
  --provisioner "$STEP_PROVISIONER" --no-password --san "app.internal"

# Renova automaticamente e recarrega o Nginx quando necessário
step ca renew /etc/nginx/certs/server.crt /etc/nginx/certs/server.key \
  --daemon --exec "nginx -s reload"

# Inicia o Nginx em primeiro plano
exec nginx -g 'daemon off;'

Como configurar o Traefik para mTLS com os backends?

O Traefik precisa de: (a) um certificado de cliente para apresentar ao backend e (b) confiar na CA raiz para validar o certificado do servidor. Usaremos uma sidecar com step CLI para emitir/renovar o certificado de cliente e um arquivo dinâmico do Traefik para definir o serversTransport com mTLS.

Para estratégias de roteamento graduais, veja exemplos de canário em Traefik com Canary releases no Docker Compose.

  • O Traefik usa serversTransport para mTLS no upstream.
  • O router expõe app.localhost via HTTP e encaminha por HTTPS+mTLS ao backend.
  • A sidecar mantém o cert de cliente renovado; reiniciar o Traefik aplica o novo cert.
# file: traefik/dynamic.yml
http:
  serversTransports:
    mtls-to-app:
      serverName: app.internal
      rootCAs:
        - /step/certs/root_ca.crt
      certificates:
        - certFile: /certs/traefik-client.crt
          keyFile: /certs/traefik-client.key

  routers:
    app:
      rule: Host(`app.localhost`)
      entryPoints: ["web"]
      service: app

  services:
    app:
      loadBalancer:
        servers:
          - url: https://app:443
        serversTransport: mtls-to-app

Subir tudo com Docker Compose (Traefik, step-ca e backend)

O docker-compose.yml integra a CA, o Traefik, a sidecar que gerencia o certificado de cliente e o backend Nginx. Antes de subir, substitua o valor de STEP_FINGERPRINT pelo fingerprint obtido na inicialização da CA.

No primeiro uso, a sidecar e o backend emitem certificados; a renovação ocorre em background. Para aplicar certificados renovados no Traefik, reinicie o container quando desejar.

# file: docker-compose.yml
version: "3.9"

services:
  step-ca:
    image: smallstep/step-ca:latest
    command: ["step-ca", "/home/step/config/ca.json"]
    volumes:
      - step-ca:/home/step
    ports:
      - "9000:9000"  # somente para laboratório; evite expor em produção
    networks: [internal]

  traefik-client:
    image: smallstep/step-ca:latest  # contém o step CLI
    depends_on:
      - step-ca
    environment:
      STEP_FINGERPRINT: "COLE_AQUI_O_FINGERPRINT_DA_RAIZ"
      STEP_PROVISIONER: "[email protected]"
    volumes:
      - step-ca:/step:ro
      - traefik-certs:/certs
    entrypoint: ["/bin/sh","-c"]
    command: >-
      step ca bootstrap --ca-url https://step-ca:9000 --fingerprint $STEP_FINGERPRINT &&
      step ca certificate traefik-client /certs/traefik-client.crt /certs/traefik-client.key \
        --provisioner $STEP_PROVISIONER --no-password --kty RSA &&
      step ca renew /certs/traefik-client.crt /certs/traefik-client.key --daemon
    networks: [internal]

  traefik:
    image: traefik:v2.10
    depends_on:
      - traefik-client
    command:
      - "--providers.docker=true"
      - "--providers.file.directory=/etc/traefik/dynamic"
      - "--providers.file.watch=true"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/dynamic.yml:/etc/traefik/dynamic/dynamic.yml:ro
      - traefik-certs:/certs:ro
      - step-ca:/step:ro
    networks: [internal]

  app:
    build: ./app
    depends_on:
      - step-ca
    environment:
      STEP_FINGERPRINT: "COLE_AQUI_O_FINGERPRINT_DA_RAIZ"
      STEP_PROVISIONER: "[email protected]"
    volumes:
      - step-ca:/step:ro  # para confiar na raiz da CA
    networks: [internal]

volumes:
  step-ca:
  traefik-certs:
  
networks:
  internal:
    driver: bridge

Como confirmar que funcionou? Testes rápidos de verificação

Verifique que o Traefik responde em HTTP e encaminha para o backend por HTTPS+mTLS. Depois, tente acessar o backend diretamente sem cliente mTLS para ver a falha esperada.

Se algo falhar, confira logs dos containers e valide nomes (serverName, SAN app.internal) e o fingerprint.

  • Teste via Traefik deve retornar 200 com a mensagem do backend.
  • Acesso direto ao backend sem cliente mTLS deve falhar (handshake).
  • Logs do Nginx indicam ssl client verify successful ao passar pelo Traefik.
# 1) Suba o ambiente
docker compose up -d --build

# 2) Acesse via Traefik (host header para roteamento)
curl -sS -H 'Host: app.localhost' http://127.0.0.1/
# Esperado: ok from app (mTLS required)

# 3) Teste o backend sem cliente mTLS (deve falhar)
docker compose run --rm alpine sh -lc "apk add --no-cache curl >/dev/null && curl -ksS https://app:443 || echo 'falha esperada (mTLS)'"

# 4) Opcional: veja logs
docker compose logs -f app traefik

Dicas de produção e manutenção de certificados

Em produção, mantenha a step-ca acessível apenas em rede privada, com backups das chaves e rotação planejada. Ajuste lifetimes curtos e renovações frequentes para reduzir risco.

Para aplicar certificados renovados no Traefik, agende um reload ou reinício controlado. O Nginx já recarrega automático no hook de renovação. Testes de renovação devem ser feitos fora do horário crítico. Para gestão de logs de containers, veja práticas em retenção e compressão de logs com journald + logrotate.

  • Isolar a CA (rede privada, restrições de firewall).
  • SANs coerentes com DNS/Service Discovery.
  • Clock sincronizado (NTP) para evitar invalidez por tempo.
  • Automatize reload do Traefik em janela segura.
  • Monitore expiração de certificados (alertas).

Quais erros de mTLS aparecem e como resolver?

Falhas comuns incluem SAN incorreto, fingerprint errado no bootstrap, cadeia de certificados incompleta e problemas de DNS. Use logs e testes diretos para isolar a causa.

Se o handshake falha, verifique também se o Traefik está usando o serversTransport correto e se o SNI (serverName) bate com o SAN do certificado do backend.

  • SAN/SNI não batem: ajuste serverName e reemita o cert do backend.
  • Fingerprint incorreto: refaça o bootstrap com o fingerprint certo.
  • Cadeia incompleta: garanta uso da raiz correta no rootCAs/ssl_client_certificate.
  • DNS/Service name errado: confirme app.internal na rede Docker.
  • Tempo (NTP) fora: sincronize relógios para evitar NotBefore/NotAfter.

Onde se aprofunda em alternativas e variações?

Se você precisa comparar motores de emissão de certificados ou ver uma variação com Nginx direto na borda, confira estes materiais relacionados. Para estratégias de roteamento avançadas no Traefik, veja canary e regras de peso.

Gerenciamento de logs e retenção em VPS pequenos também pode ajudar no dia a dia de operação.

Perguntas frequentes

Preciso expor a porta da CA (9000) na internet?

Não, mantenha a step-ca acessível apenas na rede interna do Docker ou VPN. Expor a CA aumenta a superfície de ataque. Os serviços emitem e renovam certificados via DNS interno (step-ca) sem necessidade de acesso externo.

Como o Traefik apresenta o certificado de cliente ao backend?

O Traefik usa um serversTransport com certificados de cliente (certFile/keyFile) e a raiz confiável (rootCAs). Ele se conecta por HTTPS ao backend, apresenta o certificado e valida o certificado do servidor antes de encaminhar o tráfego.

Posso usar vários serviços de backend com mTLS?

Sim. Emita um certificado de servidor para cada serviço (ex.: api.internal, auth.internal) e crie serversTransports específicos quando precisar de SNI diferente. Você pode reutilizar o mesmo certificado cliente do Traefik ou emitir clientes distintos por serviço.

Como renovar certificados sem downtime?

O step CLI renova em background e pode executar um reload suave no serviço (Nginx). Para o Traefik, programe um reload/restart controlado do container quando os arquivos de certificado forem atualizados, evitando janelas críticas e mantendo instâncias redundantes quando possível.

É possível usar domínios reais (externos) com esta CA?

É possível, desde que o DNS aponte para os serviços corretos e você gerencie a confiança da CA nos clientes. Em geral, use a CA interna para tráfego interno (Traefik→serviços) e outra solução para TLS externo público, mantendo escopos separados.

Como diagnosticar falhas de handshake TLS entre containers?

Verifique logs do Traefik e do backend, confirme SAN/SNI e confianças de CA. Tente conexões de teste a partir de um container temporário e valide datas de validade. Erros comuns incluem SAN errado e fingerprint incorreto no bootstrap dos clientes.

Quer levar mTLS ao seu ambiente Docker com segurança?

Se este passo a passo ajudou, você já tem a base para rodar mTLS entre Traefik e serviços com certificados automáticos. Precisa adaptar para múltiplos clusters, rotação e observabilidade? Vamos conversar.

Falar sobre mTLS em Docker

Veja também