Traefik: Guia completo

Saudações.

Criei esse tutorial para ser um guia prático de uso do Traefik em ambientes diversos, desde Docker em modo Standalone até o modo Swarm avançado.

Vou compartilhar com vocês todos os meus ajustes finos de Traefik!

Links:

Pré-requisitos:

  • Servidor, VPS ou VM com Linux e com Internet;
  • Docker;

É necessário configurar o email do administrador no provedor de certificados LetsEncrypt, vou usar “voce@dominio.com” nos exemplos e você deve alterar para um email funcional.

Bash
# Configurar email do administrador
[ -s /etc/email ] || echo "voce@dominio.com" > /etc/email;

# Ler email do administrador para a variavel de ambiente:
export EMAIL=$(head -1 /etc/email);

echo;
echo "Email do administrador: $EMAIL";
echo;

1 – Antes de HTTP vem o DNS

O DNS é a configuração que precede o uso do Traefik.

No tutorial usarei o domínio fictício dominio.com como exemplo, troque-o pelo seu domínio real e garanta a configuração correta antes de usar.

No DNS o nome completo do servidor – FQDN (Fully Qualified Domain Name) – é apontado para o IP do servidor. “www” é o nome de host, “dominio.com” é o domínio, www.dominio.com é o FQDN (nome completo).

Existem 3 formas de configurar, da mais simples para a mais profissional:

  • FQDN para IPv4 – Registro A: O tipo mais comum, o servidor do Traefik tem seu IPv4 vinculado a um nome como www.servidor.com.br e servidor.com.br (com e sem www devem ter os mesmos valores).
  • FQDN para IPv4 e IPv6 – Registros A AAAA (quad A): O serviço poderá abrir em IPv4 e IPv6, a escolha do cliente.O tipo mais comum, o servidor do Traefik tem seu IPv4 vinculado a um nome como www.servidor.com.br e servidor.com.br (com e sem www devem ter os mesmos valores), valor de exemplo: 45.255.128.2;
  • CAA: Configuração rigorosa de quais provedores na Internet podem emitir certificados assinados na cadeia global de RootCA, opcional, mas recomendo estudo;
  • DNS HTTPS: O DNS pode responder em um único registro os IPs e as capacidades de protocolo HTTP (como HTTP/3+QUIC), o que evita a latência de requisições adicionais no upgrade da conexão, de protocolo legado HTTP/1 e HTTP/2 para H3.

Exemplo de arquivo de zona ideal (mas não incluí DNS-SEC, não faz diferença aqui):

DNS ZONE FILE meudominio.com.br
; Dominio que representa o '@' na zona atual
; nomes que nao terminam com "." recebem esse valor como sufixo
$ORIGIN     meudominio.com.br.

; TTL padrao: de 1 hora,
;             ao trocar de valor no registro,
;              o valor antigo pode operar por mais 1h
$TTL        3600

; Start of Authority (SOA)
; Necessario para que o servidor tenha "Autoridade sobre o dominio"
@           IN    SOA    ns1.meudominio.com.br. admin.meudominio.com.br. (
                         2026061301   ; Serial number (YYYYMMDDNN format)
                         28800        ; Refresh period (8 hours)
                         7200         ; Retry interval (2 hours)
                         1209600      ; Expire time (2 weeks)
                         3600         ; Minimum/Negative caching TTL (1 hour)
                  ) ; fim do valor do SOA

; Name Server (NS) records
; usa TTL padrao $TTL
@           IN    NS     ns1
@           IN    NS     ns2
; IPs dos NS (Glue)
ns1         IN    A      45.255.130.131
ns2         IN    A      45.255.130.132

; Mail Exchanger (MX) - Servidores de e-mail
; TTL personalizado para 2h (7200 segundos) no MX e 30min no IP
@      7200 IN    MX     10 mail
mail   1800 IN    A      45.255.131.2
mail   1800 IN    AAAA   2804:caf9:f131::2
@           IN    TXT    "v=spf1 mx -all"
_dmarc      IN    TXT    "v=DMARC1; p=none; rua=mailto:admin@meudominio.com.br"

; Site principal com ipv4 e ipv6
@           IN    A      45.255.128.2
@           IN    AAAA   2804:caf9:a128::2
; Site com www. nos mesmo valores do dominio
www         IN    A      45.255.128.2
www         IN    AAAA   2804:caf9:a128::2

; HTTPS RR (RFC 9460): anuncia h2/h3 + dicas de IP
@    IN  HTTPS 1 . alpn="h2,h3" ipv4hint="45.255.128.2" ipv6hint="2804:caf9:a128::2"
www  IN  HTTPS 1 . alpn="h2,h3" ipv4hint="45.255.128.2" ipv6hint="2804:caf9:a128::2"

; Alias (CNAME)
; web e' apelido que usa os mesmos valores do WWW
; herdam valores HTTPS, economiza repeticoes
web         IN    CNAME  www
api         IN    CNAME  www


; CAA - Certification Authority Authorization
; Obs: incluir os provedores que voce deseja
;      usar como CloudFlare, GoDaddy, Comodo, Hostinger, Digicert, Sectigo, ...
; Restring quais provedores podem gerar certificados para o dominio
; só a Let's Encrypt pode emitir certificados
@           IN    CAA    0 issue     "letsencrypt.org"
@           IN    CAA    0 issuewild "letsencrypt.org"
@           IN    CAA    0 iodef     "mailto:admin@meudominio.com.br"

Com o DNS perfeito você ja pode receber os visitantes no seu servidor WEB.

2 – Apresentando o Traefik

O Traefik é um software escrito em Go que atua como servidor nos seguintes protocolos:

  • HTTP/1.1 – Roteamento web padrão;
  • HTTP/2 – Multiplexing, server push, etc;
  • H2C: HTTP/2 cleartext suportado a partir da v3.2;
  • HTTP/3 – Baseado em QUIC, menor latência;
  • WebSockets – Upgrade de conexões HTTP para WS/WSS;
  • gRPC – Proxy nativo para chamadas gRPC;
  • HTTPs e TLS/SSL – Realiza o handshake TLS/SSL e negocia certificados de maneira dinâmica em tempo de execução, sem precisar reiniciar o sistema, possui suporte a obtenção automática de certificados via ACME/Lets Encrypt;
  • TLS Termination e TLS Offload – Realiza a negociação TLS completa e entrega a conexão TCP pura para o software no backend;
  • TLSRoute/GRPCRoute: Tem CRDs específicos pra gRPC e TLS puro;
  • TCP – Roteamento L4 puro, útil para bancos, MQTT, etc;
  • UDP – Suporte L4 para DNS, syslog, VoIP, etc.

Sua principal função não é servir diretamente nesses protocolos mas atuar como proxy, tunnel ou midleware entre os usuários na Internet e as aplicações dentro de uma intranet (redes Docker no nosso caso).

Com isso ele é capaz de prover os seguintes recursos:

  • Balance: Distribuir conexões de entrada em vários servidores internos;
  • Fail-Over: Alternar para diferentes servidores quando um deles cair, pela ordem de preferência ou por balanceamento dinâmico;
  • Retry e Recovery: Repetir tentativas de acesso a servidor interno sem impactar a conexão estabelecida com usuário na Internet;
  • Rate Limit: Impor limite de requisições por segundo;
  • Basic Auth: Impor autenticação simples;
  • IP ACL: Discriminar conexões por IP de origem para bloquear ou permitir;
  • Redirecionamento L7: Guiar conexão até servidor específico por características no payload;
  • Compressão: Servir conteúdo comprimido;
  • Circuit breaker: Reação a eventos que podem interromper o serviço;

A configuração é organizada em blocos simples e organizados:

  • Entrypoints: Cada porta aberta é nomeada como um entrypoint a qual serviços podem se associar para receber conexões;
  • Providers: São plugins que o Traefik usa para obter configuração em tempo de execução, como arquivos do usuários, API do Docker (Standalone e Swarm);
  • Logs: Para onde o fluxo de logs será canalizado (arquivos, syslog, plugin) para fins de observabilidade e auditoria.

Sintaxe de configuração:

TipoFormatoQuando usa
Estática (argumento)YAML, TOML, CLI, ENVStart do Traefik: portas, providers, log
Dinâmica via arquivoYAML, TOMLRotas, middlewares, services em arquivo
Dinâmica via providerLabels, CRDs, KV, TagsDocker, K8s, Consul, etc

2.1 – Entrypoints – IPs e portas de entrada

Problema: Você só pode abrir a porta 80 e 443 uma única vez por IP, é por meio do LISTEN na camada 4 que a porta é mapeada ao aplicativo.

Se você precisa rodar vários softwares que servem via HTTP, a primeira solução seria colocar um IP exclusivo para cada aplicação, o que é inviável economicamente.

Ao usar o proxy-reverso HTTP, ele abre a porta 80, 443 (ou qualquer outra) em todos os IP do servidor (IPv4, IPv6, loopbacks, VPNs e túneis) e ao receber a requisição vinda da Internet, ele encaminha diretamente para o software final baseado nas regras de roteamento.

Fluxo de conexões de entrada vindas da Internet até o Traefik:

As conexões chegam até o IP do servidor (DNS=>IP), o Linux recebe as conexões e faz o redirecionamento para o container do Traefik.

O Linux deve enviar as conexões para o IP e porta do Traefik. Isso não significa que o Traefik irá receber e processar tais conexões. É necessário configurar os entrypoints (pontos de entrada) dando a eles nomes e propriedades (com ou sem TLS por exemplo), diagrama de fluxo até o entrypoint:

Os pacotes das conexões são redirecionadas pelo firewall do Linux para o IP do container do Traefik, que por sua vez deve explicitamente possuir um LISTEN configurado no entrypoints da configuração principal (SYN -> SYN/ACK -> ACK). Portas não declaradas são recusadas pelo kernel Linux dentro do namespace do container (SYN -> RST).

2.2 – Config Providers e Routes

O Traefik possui dois níveis de configurações:

  • Estática: É a configuração principal e fixa do serviço, para alterar é necessário reiniciar o traefik, no nosso caso, o container dele (traefik-app), inclui:
    • Logs: Registro de atividades e erros;
    • API: Estatísticas e Dashboard;
    • Ping: Serviço de status e keep-alive para healph-check;
    • Entrypoints: Portas abertas (LISTEN) para recebimento de pacotes e conexões;
    • Providers: Sub-sistema de coleta de configurações dinâmicas;
    • Cert Resolvers: Sub-sistema de obtenção de chaves e certificados x.509;
  • Dinâmica: São as configurações fornecidas em tempo de execução, sofrem alterações de acordo com o ambiente, essas configurações vem de fora do traefik, você pode não usar nenhum ou usar quantos achar necessário:
    • Arquivos (YAML, TOML): Arquivos gerados por outros programas em tempo real, se o arquivo é alterado o Traefik faz a sincronia, remove as linhas que sumiram ou mudaram e sobe as novas linhas;
    • Docker (SOCKET, TCP): O Traefik se conecta via TCP ou Unix Socket do Docker que está rodando no HOST (/var/run/docker.sock) e acompanhar os eventos em tempo real:
      • Container caiu: Retira as configurações aprendidas via labels;
      • Container subiu: Faz a leitura dos labels para obter configs;
    • Servidores: Permitem puxar configurações de sistemas paralelos ou remotos, opções:
      • consul;
      • etcd;
      • redis;
      • boltdb;

As rotas (routes) que são os principais mecanismos que orienta o Traefik a entregar conexões no destino final são aprendidas dinamicamente.

As rotas são preenchidas pelos provedores de configurações, Docker e Arquivos são os mais utilizados.

2.3 – Docker como Provider

O Docker é o melhor provedor de configurações para o Traefik:

  • A criação de um container dispara eventos no docker-engine (processo dockerd) que se propaga pela API (TCP, HTTP e Socket), onde o Traefik está conectado;
  • O Traefik analisa o container:
    • Se o container possui labels “traefik.*” em suas propriedades, se “traefik.enable=true” for encontrado o container é elegível para importação de configurações;
    • Se o container está rodando e pronto para receber conexões:
      • Se houver health-check, aguardar entrar em modo “healthy“;
      • Sem health-check basta que ele esteja no modo “running“;
    • Os labels serão analisados e se não houver erros ou colisões a configuração é importada para os provides e as rotas serão instaladas no fluxo principal;
  • Se houver alguma rota ela é entregue;
  • A ausência de rota, geral ou específica, resulta em erro 404 (not found).

Ao final, teremos:

Na imagem acima, temos:

  • O container do RabbitMQ foi criado com os labels reconhecidos, declarando mq.dominio.com como nome de host e 9000 como porta do container;
  • Docker comunicou o evento que foi lido pelo Traefik que as transformou em rota;
  • Qualquer requisição HTTP para “https://mq.dominio.com” que chegar na porta HTTPs TCP/443 do Traefik será entregue ao container do RabbitMQ na porta tcp/9000 (sem HTTPs).

2.4 – Conexões e IP de origem

Agora as coisas ficam um pouco complicadas. Quem se conecta ao container final é o Traefik, não o cliente original na Internet.

Observe o fluxo direto do cliente na Interent até o container do serviço RabbitMQ:

O cliente com IP de origem “70.14.28.56” se conectou ao IP do servidor “45.255.128.2” que terminou no entrypoint websecure, ele por sua vez fez uma segunda conexão usando como IP de origem o endereço do Traefik na rede Docker, “10.249.255.254” nesse caso.

Como o container RabbitMQ saberá o IP real do cliente que originou a conexão?

Para isso o protocolo HTTP prevê a adição dos cabeçalhos:

  • X-Forwarded-For: Contém a cadeia de IPs pela qual a requisição passou, o IP do cliente original é sempre o primeiro IP à esquerda (o primeiro), exemplos:
    • X-Forwarded-For: 70.14.28.56, cliente original em 70.14.28.56
    • X-Forwarded-For: 70.14.28.56, 45.255.128.2, cliente original em 70.14.28.56, primeiro IP da lista;
  • X-Real-Ip: Contem a versão simples contendo exclusivamente o IP original do cliente, nesse nosso exemplo:
    • X-Real-Ip: 70.14.28.56
  • X-Forwarded-Proto: Protocolo usado pelo cliente para chegar ao Traefik, exemplo:
    • X-Forwarded-Proto: https
  • X-Forwarded-Host: O hostname original requisitado pelo cliente, exemplo:
    • X-Forwarded-Host: mq.dominio.com
  • X-Forwarded-Port: A porta do Entrypoint que recebeu a conexão, exemplo:
    • X-Forwarded-Host: 443
  • Forwarded: Formato moderno completo, nem sempre presente, informa tudo num único cabeçalho, raramente presente.

Assim, o software que recebe a conexão deve obter o IP de origem do cliente na seguinte ordem:

  • IP em X-Real-Ip, se ausente,
  • Primeiro IP em X-Forwarded-For, se ausente,
  • IP de origem da conexão TCP.

Para identificar o VirtualHost:

  • Nome em X-Forwarded-Host, se ausente,
  • Nome em Host.

2.5 – Porta de origem

Agora temos um problema: A porta TCP ou UDP de origem do cliente na Internet não é repassada para o serviço final, mas é retida pelo log de acesso do Traefik (accesslog).

No Brasil, essa porta efêmera é especialmente importante para a Lei de Marco Civil da Internet para ajudar a identificar um usuário em um provedor que faz uso de CGNAT , onde um mesmo IP é usado em vários usuários, distinguindo-os pela “porta virtual“, que nesse caso é a porta de origem alocada pelo CGNAT e registrado no log do provedor.

IP residencial é trocado pelo IP público no CGNAT, esse mapeamento é registrado no log de CGNAT do provedor, o IP e porta que chegou até o Traefik é registrado no Access Log, o container final não tem acesso a todos os detalhes (Somente o IP de origem via cabeçalhos X-Forwarded).

2.6 – TLS Offload sem IP e porta de origem

Agora vem a parte ruim: Conexões TCP+TLS com offload no traefik não possuem essa capacidade de repassar a identificação do cliente original.

Exemplo:

  • O Redis usa o protocolo RESP (Redis Serialization Protocol), baseado em TCP;
  • Ao utilizar Redis + TLS, é necessário usar um certificado, o Traefik pode usar um certificado obtido do Acme/LetsEntrypt para isso;
  • A conexão TCP+TLS será recebida da Internet e o Traefik fará a comunicação criptografada com o cliente;
  • O Traefik se conectará ao Redis usando uma conexão TCP simples, com IP de origem no próprio Traefik;
  • O Redis jamais saberá o IP real do cliente, para ele o Traefik é o cliente.
Prover TLS Offload usa apenas TCP e TLS puro, não há analise do protocolo (conteúdo do payload TCP) no Traefik, o que torna impossível anexar o IP original para o container final, o container enxerga tudo vindo do IP do Traefik. O Traefik não consegue fazer logs detalhados para mapear indubitavelmente o cliente.

Para esses casos é vital que o Traefik seja configurado com ACLs para controlar o acesso e logs para registrar os acessos.

O uso de VPNs que preservam o IP do cliente até o Redis seria mais adequado.

2.7 – Rede única do Traefik

O Traefik faz o aprendizado de containers em apenas 1 rede Docker. Em ambientes simples você nem percebem que existe essa limitação.

Se o container Traefik for ligado a várias redes sem a configuração explicita da rede em que ele deve atuar, ele pode alternar para uma rede em detrimento de outra e os serviços podem ficar indisponíveis.

Para evitar isso, manifeste explicitamente nos argumentos qual a rede (docker network) estão os containers dos serviços (providers.docker.network).

Chegamos à conclusão de que conectar o Traefik em todas as redes das stacks não é a solução, a solução é cada stack se conectar à rede do Traefik.

As redes Docker são isoladas entre si. Ao colocar cada aplicativo para participar da rede onde está o Traefik, rotas HTTP podem ser entregues a esses aplicativos.

É comum você encontrar por ai redes como “network_public“, “traefik“, “network_cloud“, “proxy“.

2.8 – Provedor de configuração próprio

O fato do Traefik não participar de várias redes pode ser contornado com o “file provider” construído por software próprio.

Nesse cenário você desenvolve um gerador de configurações do Traefik sempre que seu ambiente sofre mutação, caberá a você preencher as rotas e entregar ao Traefik para que ele obedeça cegamente.

Exemplo: Software próprio sincroniza o plano de negócios em um banco de dados postgres com arquivos de configuração lidos e monitorados pelo Traefik, quando os arquivos são alterados o carregamento é imediato e as rotas surgem ou somem. Muito usado para bloquear serviços por cota, falta de pagamento, nova assinatura, mudança de plano.

Você não está limitado apenas à containers como endpoints das rotas, qualquer IP ou URL pode ser um alvo da rota.

2.9 – Provedores de certificados

O “Cert Resolver”, normalmente usando protocolo ACME (Automated Certificate Management Environment), são sub-sistemas responsáveis por obter certificados assinados na cadeia de PKI global (reconhecidos até as Root CA dos navegadores).

Simplificando, quando você coloca o label “mq.dominio.com” em um container, o Traefik realiza os seguintes procedimentos:

  • Cria uma chave privada sua respectiva chave pública para esse FQDN;
  • Emite um certificado auto-assinado temporariamente;
    • Se você abrir o endereço no navegador nesse momento o HTTPs estará disponível mas exibirá alerta de não confiável;
  • Entra em contato com o provedor ACME, por padrão Let’s Encrypt (LE) solicitando o inicio do desafio para provar a propriedade do FQDN e do site:
    • O LE ordena o armazenamento de arquivos de desafio na porta HTTP 80 (entrypoint web);
    • O LE acessa o arquivo de desafio (exemplo: http://mq.dominio.com/…/arquivo);
    • Se o acesso funcionar, isso significa que o solicitente possui do DNS e o HTTP para esse site funcionando;
    • O LE fornecerá o certificado (chave pública assinada);
    • O Traefik alterna do certificado auto-assinado para o certificado válido;
    • O site abrirá normalmente sem alerta (obs: o cache do navegador pode não atualizar o certificado, reinicie-o para conferir).

2 – Usando Traefik no Docker Standalone

Vou apresentar nesse capitulo o uso do Traefik em um ambiente com Docker onde não há swarm instalado. Esse caso se aplica a ambientes simples com aplicações básicas do dia-a-dia e envolve usuários leigos que não precisam se embolar com muita tecnologia avançada sem necessidade (swarm, overlay vxlan, ingress e egress, etc).

2.1 – Rede Docker – Bridge

Vamos criar uma rede onde o traefik poderá enxergar os containers vizinhos pelo DNS interno do Docker.

Escolha a rede com ou sem IPv6, recomendo configurar IPv6 no HOST e usar o modelo com IPv6, caso você tenha apenas IPv4 use o primeiro exemplo.

Por padrão o MTU de 1500 é funcional para todos os casos, se você tem VPNs e túneis antes do servidor recomendo reduzir para o PTMU funcional, nunca inferior a 1330.

2.2 – Rede somente IPv4

Essa é a rede mais conservadora, para ambientes onde o usuário não domina IPv6 ou o IPv6 não foi disponibilizado para a VPS/VM ou provedor.

Bash
# Rede de containers
docker network create network_public \
    -d bridge \
    -o com.docker.network.bridge.name=br-net-public \
    -o com.docker.network.driver.mtu=1500 \
    -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
    --subnet 10.249.0.0/16 \
    --gateway 10.249.255.254;

2.3 – Rede dual-stack IPv4+IPv6 (recomendado)

Essa é a rede perfeita! IPv4 e IPv6 juntos:

Bash
docker network create \
    -d bridge \
    \
    -o "com.docker.network.bridge.name"="br-net-public" \
    -o "com.docker.network.bridge.enable_icc"="true" \
    -o "com.docker.network.driver.mtu"="65495" \
    \
    --subnet 10.249.0.0/16 --gateway 10.249.255.254 \
    --ipv6 \
    --subnet=2001:db8:10:249::/64 \
    --gateway=2001:db8:10:249::ffff \
    \
    network_public;

2.4 – Container Traefik básico

Antes de avançar para configurações completas, apresento abaixo a configuração mais simples e conservadora do Traefik, para ambientes apenas HTTP sem muitos mistérios e pronto para trabalho.

Criando container simples com nome traefik-app:

Bash
# Obter email do administrador
EMAIL=$(head -1 /etc/email);

# Nome oficial do servidor
SERVER=$(hostname -f);

# Pasta par reter dados no volume
mkdir -p /storage/traefik-app/letsencrypt; # certificados em acme.json
mkdir -p /storage/traefik-app/logs;        # logs de acessos e erros
mkdir -p /storage/traefik-app/config;      # configs manuais
mkdir -p /storage/traefik-app/tmp;         # core-dump debug

# Obter imagem atualizada
docker image pull traefik:latest;

# Renovar/criar/rodar
docker container rm -f traefik-app 2>/dev/null;
docker container run \
    --name                 traefik-app \
    --hostname             traefik-app.intranet.br \
    --restart              unless-stopped \
    \
    --detach \
    \
    --network              network_public \
    \
    --publish                80:80 \
    --publish              443:443 \
    \
    -v /var/run/docker.sock:/docker.sock:ro \
    \
    -v /storage/traefik-app/letsencrypt:/letsencrypt \
    -v /storage/traefik-app/config:/config \
    -v /storage/traefik-app/logs:/logs \
    -v /storage/traefik-app/tmp:/tmp \
    \
    \
    traefik:latest \
      \
      --global.checkNewVersion=false \
      --global.sendAnonymousUsage=false \
      \
      --api.insecure=true \
      \
      --log=true \
      --log.level=INFO \
      --log.filePath=/logs/error.log \
      --log.noColor=false \
      \
      --accessLog=true \
      --accessLog.filePath=/logs/access.log \
      \
      --entrypoints.web.address=:80 \
      --entrypoints.web.http.tls=false \
      --entrypoints.web.http.redirections.entryPoint.to=websecure \
      --entrypoints.web.http.redirections.entryPoint.scheme=https \
      --entrypoints.web.http.redirections.entryPoint.permanent=true \
      \
      --entrypoints.websecure.address=:443 \
      \
      --providers.docker=true \
      --providers.docker.watch=true \
      --providers.docker.endpoint=unix:///docker.sock \
      --providers.docker.exposedbydefault=false \
      --providers.docker.network=network_public \
      \
      --providers.file.directory=/config \
      --providers.file.watch=true \
      \
      --certificatesresolvers.letsencrypt.acme.email=$EMAIL \
      --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json \
      --certificatesresolvers.letsencrypt.acme.httpchallenge=true \
      --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web;

2.5 – Container Traefik completo

Agora é hora de fazer direito. Vou explicar após o script o que cada argumento está fazendo.

Bash
# Obter email do administrador
EMAIL=$(head -1 /etc/email);

# Nome oficial do servidor
SERVER=$(hostname -f);

# Pasta par reter dados no volume
mkdir -p /storage/traefik-app/letsencrypt; # certificados em acme.json
mkdir -p /storage/traefik-app/logs;        # logs de acessos e erros
mkdir -p /storage/traefik-app/config;      # configs manuais
mkdir -p /storage/traefik-app/tmp;         # core-dump debug

# Obter imagem atualizada
docker image pull traefik:latest;

# Renovar/criar/rodar
docker container rm -f traefik-app 2>/dev/null;
docker container run \
    --name                 traefik-app \
    --hostname             traefik-app.intranet.br \
    --restart              unless-stopped \
    \
    --detach \
    --read-only \
    \
    --cpus                 1 \
    --memory               512M \
    --memory-swap          512M \
    --memory-reservation   512M \
    --shm-size             512M \
    \
    --network              network_public \
    --ip                   10.249.255.253 \
    --ip6                  2001:db8:10:249::255:253 \
    --mac-address          00:25:31:25:52:53 \
    \
    --publish                80:80/tcp \
    --publish                80:80/udp \
    --publish              443:443/tcp \
    --publish              443:443/udp \
    --publish            8081:8081/tcp \
    \
    -v /var/run/docker.sock:/docker.sock:ro \
    \
    -v /storage/traefik-app/letsencrypt:/letsencrypt \
    -v /storage/traefik-app/config:/config:ro \
    -v /storage/traefik-app/logs:/logs \
    -v /storage/traefik-app/tmp:/tmp \
    \
    --health-cmd="traefik healthcheck --ping" \
    --health-start-period=6s \
    --health-start-interval=1s \
    --health-interval=10s \
    --health-timeout=3s \
    --health-retries=3 \
    \
    --label "traefik.enable=true" \
    --label "traefik.http.routers.dashboard.rule=Host(\`${SERVER}\`)" \
    --label "traefik.http.routers.dashboard.entrypoints=dashboard" \
    --label "traefik.http.routers.dashboard.service=api@internal" \
    --label "traefik.http.routers.dashboard.tls=true" \
    --label "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" \
    \
    \
    traefik:latest \
      \
      --global.checkNewVersion=false \
      --global.sendAnonymousUsage=false \
      \
      --api.insecure=true \
      --api.dashboard=true \
      \
      --ping=true \
      \
      --log=true \
      --log.level=INFO \
      --log.filePath=/logs/error.log \
      --log.format=json \
      --log.noColor=true \
      \
      --accessLog=true \
      --accessLog.filePath=/logs/access.log \
      --accessLog.format=json \
      --accessLog.fields.defaultMode=keep \
      --accessLog.fields.headers.defaultMode=drop \
      --accessLog.fields.headers.names.X-Forwarded-For=keep \
      --accessLog.fields.headers.names.X-Real-IP=keep \
      \
      --entrypoints.ping.address=127.0.0.1:8082 \
      \
      --entrypoints.dashboard.address=:8081 \
      \
      --entrypoints.web.address=:80 \
      --entrypoints.web.http.tls=false \
      --entrypoints.web.http.redirections.entryPoint.to=websecure \
      --entrypoints.web.http.redirections.entryPoint.scheme=https \
      --entrypoints.web.http.redirections.entryPoint.permanent=true \
      --entrypoints.web.http.redirections.entrypoint.priority=10000000 \
      \
      --entrypoints.websecure.address=:443 \
      --entryPoints.websecure.forwardedHeaders.insecure=true \
      --entrypoints.websecure.http3=true \
      \
      --providers.docker=true \
      --providers.docker.watch=true \
      --providers.docker.endpoint=unix:///docker.sock \
      --providers.docker.exposedbydefault=false \
      --providers.docker.network=network_public \
      --providers.docker.defaultRule="Host(\`{{ .Name }}.dominio.com\`)" \
      \
      --providers.file.directory=/config \
      --providers.file.watch=true \
      \
      --certificatesresolvers.letsencrypt.acme.email=$EMAIL \
      --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json \
      --certificatesresolvers.letsencrypt.acme.httpchallenge=true \
      --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web;

2.6 – Containers de teste HTTPs

O Traefik tem um container auxiliar na imagem “traefik/whoami” muito bom para testes de URLs.

Vou criar o FQDN no DNS chamado who01.dominio.com para esse teste.

Bash
# Nome de DNS do contaienr (deve apontar para o IP do servidor Traefik)
#FQDN="who01.$(hostname -f)";
FQDN="who01.dominio.com";

# Obter imagem atualizada
docker image pull traefik/whoami:latest;

# Renovar/criar/rodar
docker container rm -f whoami001 2>/dev/null;
docker container run \
    --name                 whoami001 \
    --hostname             whoami001.intranet.br \
    --restart              unless-stopped \
    --network              network_public \
    --detach  --read-only  \
    \
    --label "traefik.enable=true" \
    \
    --label "traefik.http.routers.whoami001.rule=Host(\`${FQDN}\`)" \
    --label "traefik.http.routers.whoami001.entrypoints=web,websecure" \
    --label "traefik.http.routers.whoami001.service=whoami001" \
    --label "traefik.http.routers.whoami001.tls=true" \
    --label "traefik.http.routers.whoami001.tls.certresolver=letsencrypt" \
    \
    --label "traefik.http.services.whoami001.loadbalancer.server.port=80" \
    \
    traefik/whoami;

Ao acessar o endereço https://who01.dominio.com você verá o seguinte texto:

https://who001.dominio.com
Hostname: whoami001.intranet.br
IP: 127.0.0.1
IP: ::1
IP: 10.249.0.1
IP: 2001:db8:10:249::1
IP: fe80::6489:cbff:fe9b:2d4f
RemoteAddr: 10.249.255.253:57466
GET / HTTP/1.1
Host: who01.cloud01.tmsoft.com.br
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: max-age=0
Priority: u=0, i
Sec-Ch-Ua: "Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 45.255.130.219
X-Forwarded-Host: who01.dominio.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik-app.intranet.br
X-Real-Ip: 45.255.130.219

2.7 – Containers de teste TLS-Offload

Neste exemplos vamos usar a técnica de proxy-TCP com TLS na entrada e TCP simples até o container. O Traefik não fará processamento do payload HTTP, somente repasse do payload TCP puro.

O que muda é a linha com label de Host para HostSNI.

Vou criar o FQDN no DNS chamado who02.dominio.com para esse teste.

Bash
# Nome de DNS do contaienr (deve apontar para o IP do servidor Traefik)
#FQDN="who02.$(hostname -f)";
FQDN="who02.dominio.com";

# Obter imagem atualizada
docker image pull traefik/whoami:latest;

# Renovar/criar/rodar
docker container rm -f whoami002 2>/dev/null;
docker container run \
    --name                 whoami002 \
    --hostname             whoami002.intranet.br \
    --restart              unless-stopped \
    --network              network_public \
    --detach  --read-only  \
    \
    --label "traefik.enable=true" \
    \
    --label "traefik.http.routers.whoami002.rule=Host(\`$FQDN\`)" \
    --label "traefik.http.routers.whoami002.entrypoints=websecure" \
    --label "traefik.http.routers.whoami002.service=whoami002" \
    --label "traefik.http.routers.whoami002.tls=true" \
    --label "traefik.http.routers.whoami002.tls.certresolver=letsencrypt" \
    \
    --label "traefik.http.services.whoami002.loadbalancer.server.port=80" \
    \
    traefik/whoami;

#    --label "traefik.tcp.routers.whoami002.tls.passthrough=true" \

Essa técnica é pouco explorada, a vantagem dessa técnica é que você pode abrir qualquer porta no entrypoints e entregar a um container. O Traefik obterá o certificado assinado usando Acme e te entregará uma porta TLS perfeita para seu aplicativo/protocolo.

Onde usar:

  • RabbitMQ;
  • Postgres;
  • Redis;
  • NATS;

x

x

3 – Usando Traefik no Docker Swarm

x

x

x

x

x

x

x

4 – Usando Traefik em Cluster

x

x

x

Bash
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS
# RASCUNHOS

- "traefik.http.middlewares.STSHeader.headers.stsSeconds=31536000"
- "traefik.http.middlewares.STSHeader.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.STSHeader.headers.stsPreload=true"
- "traefik.http.middlewares.STSHeader.headers.forceSTSHeader=true"

#---------------------------

  
  
      - "traefik.enable=true"
      - "traefik.http.middlewares.STSHeader.headers.stsSeconds=15552000"
      - "traefik.http.middlewares.STSHeader.headers.stsPreload=true"
      - "traefik.http.middlewares.STSHeader.headers.forceSTSHeader=true"
      - "traefik.http.routers.api.rule=Host(`traefik.${MY_DOMAIN}`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.middlewares=STSHeader"
      
      
      
      

      - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
      - "--certificatesresolvers.letsencrypt.acme.email=me@example.com"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.delaybeforecheck=60"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53"


      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.http.tls=true"
      - "--entrypoints.websecure.http.tls.certResolver=letsencrypt"
      - "--entrypoints.websecure.http.tls.domains[0].main=${MY_DOMAIN}"
      - "--entrypoints.websecure.http.tls.domains[0].sans=*.${MY_DOMAIN}"
      - "--entrypoints.websecure.http.tls.options=secureoptions@file"
      - "--entrypoints.websecure.http3"
      - "--entrypoints.websecure.http3.advertisedport=443"
      
      # used for other services, but should not conflict right?
      - "--entrypoints.tcp.address=:5201"
      - "--entrypoints.udp.address=:5201/udp"
    ports:
      - "80:80"
      - "443:443/tcp"
      - "443:443/udp"
      
      # used for other services, but should not conflict right?
      - "5201:5201"
      - "5201:5201/udp"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt:/letsencrypt"
      - "./dynamic-file-provider:/dynamic-file-provider"
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.STSHeader.headers.stsSeconds=15552000"
      - "traefik.http.middlewares.STSHeader.headers.stsPreload=true"
      - "traefik.http.middlewares.STSHeader.headers.forceSTSHeader=true"
      - "traefik.http.routers.api.rule=Host(`traefik.${MY_DOMAIN}`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.middlewares=STSHeader"

x

x

YAML
rascunhos:
rascunhos:
rascunhos:
rascunhos:
rascunhos:
rascunhos:
rascunhos:
rascunhos:

.

.

Terminamos por hoje!

Patrick Brandão, patrickbrandao@gmail.com

Quem estuda e não pratica o que aprendeu,
é como o homem que lavra e não semeia.
Provérbio Árabe