Seção 5: Implementação da Rede Segura (Traefik, Cloudflare Tunnel, Authelia)¶
Esta seção é fundamental para expor seus serviços auto-hospedados à internet de forma segura, controlada e gerenciada. Implementaremos uma stack de rede robusta na core-services-vm composta por:
- Traefik Proxy: Atuará como nosso proxy reverso, roteando o tráfego externo para os containers Docker corretos e gerenciando automaticamente os certificados SSL/TLS com Let's Encrypt.
- Cloudflare Tunnel (
cloudflared): Criará uma conexão de saída segura da nossa rede local para a rede global da Cloudflare. Isso elimina a necessidade de abrir portas no roteador doméstico e protege nosso endereço IP público. - Authelia: Servirá como um portal de autenticação e autorização centralizado, adicionando uma camada de segurança com Single Sign-On (SSO) e autenticação de dois fatores (2FA) para os serviços que escolhermos proteger.
Todos esses componentes rodarão como containers Docker na core-services-vm, gerenciados via Docker Compose e, posteriormente, Portainer.
5.1. Entendendo o Fluxo de Tráfego e o Conceito de Proxy Reverso¶
Antes de mergulharmos na configuração, é vital entender como o tráfego de um usuário externo alcançará seus serviços internos:
- Requisição do Usuário: O usuário digita
https://meuservico.{{ base_domain }}no navegador (e.g.,https://nextcloud.meuhomelab.com). - Resolução DNS via Cloudflare: Os nameservers do seu domínio estão configurados para a Cloudflare. A Cloudflare não resolverá o subdomínio para o seu IP doméstico, mas para um endpoint da rede Cloudflare associado ao seu Cloudflare Tunnel.
- Cloudflare Edge Network: A requisição do usuário chega à rede global da Cloudflare. Aqui, ela pode se beneficiar de proteções como mitigação de DDoS e Web Application Firewall (WAF) básico da Cloudflare. Seu IP doméstico real permanece oculto.
- Cloudflare Tunnel (
cloudflaredAgent): O containercloudflaredrodando na suacore-services-vmmantém uma conexão de saída persistente e criptografada com o data center da Cloudflare mais próximo. O tráfego destinado aos seus subdomínios configurados no túnel é "puxado" do Cloudflare Edge, através desta conexão segura, para o containercloudflaredna sua rede local. Nenhuma porta de entrada precisa ser aberta no seu roteador doméstico. cloudflaredpara Traefik: O containercloudflaredrecebe o tráfego do túnel e o encaminha para o container Traefik. No nossodocker-compose.yml, ocloudflaredé configurado para enviar o tráfego para o serviço Dockertraefikna porta80(dentro da rede Dockerproxycompartilhada).- Traefik (Proxy Reverso):
- Traefik escuta nas portas
80(HTTP) e443(HTTPS) nacore-services-vm(mapeadas do host da VM para o container Traefik). - Redirecionamento HTTP para HTTPS: Traefik está configurado para redirecionar automaticamente todo o tráfego chegando na porta
80para HTTPS na porta443. - Roteamento Baseado em Host: Traefik inspeciona o cabeçalho
Hostda requisição HTTP (e.g.,nextcloud.meuhomelab.com). - Middleware de Autenticação (Authelia): Se a rota do Traefik para o serviço estiver configurada com o middleware Authelia (e.g.,
authelia@docker), Traefik primeiro consulta o container Authelia para verificar a autenticação do usuário.- Se o usuário não estiver logado, Authelia instrui Traefik a redirecionar o usuário para o portal de login do Authelia (e.g.,
https://auth.meuhomelab.com). - Após o login bem-sucedido (incluindo 2FA), Authelia estabelece uma sessão (via cookie) e informa ao Traefik que o usuário está autenticado.
- Se o usuário não estiver logado, Authelia instrui Traefik a redirecionar o usuário para o portal de login do Authelia (e.g.,
- Gerenciamento de Certificados SSL/TLS (Let's Encrypt): Traefik automaticamente solicita, renova e gerencia certificados SSL/TLS da Let's Encrypt para todos os seus subdomínios expostos. Ele utiliza o desafio DNS-01 com a API da Cloudflare (usando o token API que configuraremos), o que é robusto e não requer que portas HTTP estejam abertas para o mundo. Os certificados são armazenados no arquivo
acme.json(em um volume NFS). - Roteamento para o Serviço Docker de Destino: Com base nas labels Docker definidas no
docker-compose.ymldo serviço de destino (e após autenticação bem-sucedida, se aplicável), Traefik encaminha a requisição para o container Docker correto e sua porta interna.
- Traefik escuta nas portas
- Serviço Docker: O container da aplicação (e.g., Nextcloud) processa a requisição e envia a resposta de volta pelo mesmo caminho.
Diagrama Visual do Fluxo
Para uma representação gráfica detalhada deste fluxo, consulte o diagrama na seção Fluxo de Rede e Acesso Externo da Visão Geral da Arquitetura.
5.2. Docker Compose para a Stack Core (docker/stacks/core/docker-compose.yml)¶
Esta stack fundamental inclui os containers Traefik e Cloudflared. Crie o arquivo home-server/docker/stacks/core/docker-compose.yml com o seguinte conteúdo:
# docker/stacks/core/docker-compose.yml
version: '3.9'
networks:
proxy: # Rede para Traefik e serviços expostos
name: proxy # Nome da rede que foi criada pelo Ansible na Seção 4.2
external: true # Indica que a rede 'proxy' já existe e será usada por esta stack
services:
traefik:
image: traefik:v2.11.2 # Use uma versão específica e estável. Verifique por atualizações.
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true # Boa prática de segurança, impede escalação de privilégios
networks:
- proxy # Conecta Traefik à rede proxy
ports:
# Mapeia as portas do Traefik no IP da core-services-vm.
# A variável ${CORE_SERVICES_VM_IP_VAR} virá do arquivo .env carregado pelo Portainer.
- "${CORE_SERVICES_VM_IP_VAR}:80:80" # HTTP (será redirecionado para HTTPS por Traefik)
- "${CORE_SERVICES_VM_IP_VAR}:443:443" # HTTPS (ponto de entrada principal para serviços)
# - "${CORE_SERVICES_VM_IP_VAR}:8080:8080" # Opcional: Se você quiser expor a API/Dashboard do Traefik
# diretamente nesta porta, mas é melhor expô-la via rota segura HTTPS.
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro" # Permite Traefik detectar containers Docker e suas labels
# Caminhos para configs e certs DENTRO DA VM (que são montagens NFS do host Proxmox).
# As variáveis (e.g., ${VM_NFS_MOUNT_BASE_PATH}) virão do .env.
- "${VM_NFS_MOUNT_BASE_PATH}/${ZFS_DOCKER_VOLUMES_DATASET_NAME}/traefik/data:/data" # Para acme.json (certificados SSL) e logs Traefik
- "${VM_NFS_MOUNT_BASE_PATH}/${ZFS_DOCKER_VOLUMES_DATASET_NAME}/traefik/config/traefik.yml:/traefik.yml:ro" # Configuração estática do Traefik (readonly)
- "${VM_NFS_MOUNT_BASE_PATH}/${ZFS_DOCKER_VOLUMES_DATASET_NAME}/traefik/config/dynamic:/etc/traefik/dynamic_configs:ro" # Configs dinâmicas (middlewares, etc., readonly)
- "/etc/localtime:/etc/localtime:ro" # Sincroniza o fuso horário do container com o da VM
environment:
# Token da API Cloudflare para o desafio DNS-01 (Let's Encrypt).
# Esta variável ${CLOUDFLARE_API_TOKEN_FROM_VAULT} será injetada pelo .env
# (que por sua vez é preenchido por Ansible com o valor do vault.yml).
- CF_DNS_API_TOKEN=${CLOUDFLARE_API_TOKEN_FROM_VAULT}
# Algumas configurações DNS podem exigir o email ou um token API específico da zona.
# Para DNS Challenge com API Token global, apenas CF_DNS_API_TOKEN é geralmente necessário.
# - CF_API_EMAIL=${LETSENCRYPT_EMAIL_FROM_VAULT}
# - CF_ZONE_API_TOKEN=${CLOUDFLARE_ZONE_API_TOKEN_FROM_VAULT} # Se usar token específico da zona
labels:
# --- Labels para Traefik gerenciar o próprio Traefik (Dashboard) ---
traefik.enable: "true" # Permite Traefik gerenciar este container (ele mesmo)
# Roteador para o Dashboard do Traefik
traefik.http.routers.traefik-dashboard.rule: "Host(`traefik.${BASE_DOMAIN_FROM_VAULT}`)" # ${BASE_DOMAIN_FROM_VAULT} do .env
traefik.http.routers.traefik-dashboard.entrypoints: "websecure" # Acessível apenas via HTTPS
traefik.http.routers.traefik-dashboard.service: "api@internal" # Serviço interno especial do Traefik para o dashboard/API
traefik.http.routers.traefik-dashboard.tls.certresolver: "letsencrypt" # Usar Let's Encrypt
traefik.http.routers.traefik-dashboard.middlewares: "authelia@docker" # Proteger o dashboard com Authelia
healthcheck: # Define um healthcheck para o container Traefik
test: ["CMD", "traefik", "healthcheck", "--ping"] # Comando para verificar a saúde
interval: 10s # Intervalo entre verificações
timeout: 2s # Timeout para a verificação
retries: 3 # Número de tentativas antes de marcar como "unhealthy"
start_period: 15s # Período inicial de tolerância antes de falhas no healthcheck contarem
cloudflared:
image: cloudflare/cloudflared:2024.5.0 # Use uma versão específica e estável. Verifique por atualizações.
container_name: cloudflared
restart: unless-stopped
networks:
- proxy # Precisa estar na mesma rede que Traefik para poder encaminhar tráfego para ele
# O comando para rodar o túnel.
# O token ${CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT} será injetado pelo .env.
# A flag `--url http://traefik:80` direciona todo o tráfego do túnel para o serviço Docker 'traefik' na porta 80.
# A flag `--hostname ${BASE_DOMAIN_FROM_VAULT}` tenta registrar este hostname (e um wildcard) para o túnel.
# Se você preferir gerenciar os Ingress Rules (Public Hostnames) exclusivamente no dashboard da Cloudflare,
# remova `--hostname` e `--url`, e use apenas 'run --token ${CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT}'.
command: tunnel --no-autoupdate --metrics 0.0.0.0:2000 --protocol http2 --hostname ${BASE_DOMAIN_FROM_VAULT} --url http://traefik:80 run --token ${CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT}
# Exemplo de comando para Ingress Rules gerenciadas no dashboard Cloudflare:
# command: tunnel --no-autoupdate --metrics 0.0.0.0:2000 run --token ${CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT}
# E então, no Cloudflare Zero Trust Dashboard > Access > Tunnels > Seu Túnel > Public Hostnames:
# - Hostname: app.seudominio.com, Service: http://traefik:80
# - Hostname: *.seudominio.com, Service: http://traefik:80 (para um catch-all)
depends_on: # Garante que Traefik esteja saudável antes de iniciar cloudflared
traefik:
condition: service_healthy # Espera o healthcheck do Traefik passar
Variáveis de Ambiente no Docker Compose
Variáveis como ${CORE_SERVICES_VM_IP_VAR}, ${VM_NFS_MOUNT_BASE_PATH}, ${ZFS_DOCKER_VOLUMES_DATASET_NAME}, ${CLOUDFLARE_API_TOKEN_FROM_VAULT}, ${BASE_DOMAIN_FROM_VAULT}, e ${CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT} são placeholders que serão substituídos:
*Pelo Ansible quando ele gerar um arquivo .env para esta stack na core-services-vm.
* Ou, se você não usar um .env gerado por Ansible para esta stack específica, você precisaria definir essas variáveis diretamente como "Environment variables" da stack no Portainer. O método recomendado é via arquivo .env preparado por Ansible para consistência.
5.3. Docker Compose para a Stack Authelia (docker/stacks/authelia/docker-compose.yml)¶
Authelia fornecerá o portal de autenticação e o serviço de 2FA. Crie o arquivo home-server/docker/stacks/authelia/docker-compose.yml:
# docker/stacks/authelia/docker-compose.yml
version: '3.9'
networks:
proxy: # Para Traefik alcançar o portal Authelia e o endpoint de verificação
name: proxy
external: true
internal_services: # Rede para o banco de dados PostgreSQL do Authelia
name: internal_services
external: true
services:
authelia:
image: authelia/authelia:4.38.0 # Use uma versão específica e estável. Verifique por atualizações.
container_name: authelia
restart: unless-stopped
volumes:
# Caminho NFS para as configurações do Authelia.
# As variáveis ${VM_NFS_MOUNT_BASE_PATH} e ${ZFS_DOCKER_VOLUMES_DATASET_NAME} virão do .env.
- "${VM_NFS_MOUNT_BASE_PATH}/${ZFS_DOCKER_VOLUMES_DATASET_NAME}/authelia/config:/config"
- "/etc/localtime:/etc/localtime:ro" # Sincroniza fuso horário
networks:
- proxy
- internal_services
expose:
# Expõe a porta interna do Authelia para outros containers na mesma rede Docker,
# mas não a mapeia para o host da VM. Traefik acessará esta porta.
- "9091"
environment:
- TZ=${SYSTEM_TIMEZONE_FROM_VAULT} # Injetado pelo .env
# Outras variáveis de ambiente podem ser definidas aqui se o configuration.yml do Authelia
# estiver configurado para lê-las (e.g., segredos, embora seja melhor no config.yml).
# AUTHELIA_JWT_SECRET: ${AUTHELIA_JWT_SECRET_FROM_VAULT} # Exemplo
# AUTHELIA_SESSION_SECRET: ${AUTHELIA_SESSION_SECRET_FROM_VAULT} # Exemplo
depends_on: # Garante que o banco de dados esteja saudável antes de iniciar Authelia
authelia_db:
condition: service_healthy
labels: # Labels Traefik para expor o portal Authelia e definir o middleware de autenticação
traefik.enable: "true"
# --- Roteador para o portal Authelia (e.g., auth.meuhomelab.com) ---
traefik.http.routers.authelia-portal.rule: "Host(`auth.${BASE_DOMAIN_FROM_VAULT}`)"
traefik.http.routers.authelia-portal.entrypoints: "websecure"
traefik.http.routers.authelia-portal.tls.certresolver: "letsencrypt"
traefik.http.routers.authelia-portal.service: "authelia-svc"
# --- Serviço Traefik para o portal Authelia ---
traefik.http.services.authelia-svc.loadbalancer.server.port: "9091" # Porta interna do Authelia
# --- Middleware de Forward Authentication Authelia ---
# Este middleware será referenciado por outros serviços que precisam ser protegidos.
# O nome 'authelia@docker' é como outros containers (via suas labels) referenciarão este middleware.
traefik.http.middlewares.authelia.forwardauth.address: "http://authelia:9091/api/verify?rd=https://auth.${BASE_DOMAIN_FROM_VAULT}/"
traefik.http.middlewares.authelia.forwardauth.trustForwardHeader: "true" # Confia nos headers X-Forwarded-* do Traefik
traefik.http.middlewares.authelia.forwardauth.authResponseHeaders: "Remote-User,Remote-Groups,Remote-Name,Remote-Email" # Headers a serem passados para o serviço backend após auth
authelia_db: # Banco de dados PostgreSQL para Authelia
image: postgres:15.6-alpine # Use uma versão específica e estável.
container_name: authelia_db
restart: unless-stopped
volumes:
# Caminho NFS para os dados do banco de dados do Authelia. As variáveis virão do .env.
- "${VM_NFS_MOUNT_BASE_PATH}/${ZFS_DOCKER_VOLUMES_DATASET_NAME}/authelia/db_pg:/var/lib/postgresql/data"
networks:
- internal_services # Apenas na rede interna, não acessível diretamente pelo Traefik ou externamente
environment:
- POSTGRES_DB=authelia
- POSTGRES_USER=authelia
- POSTGRES_PASSWORD=${AUTHELIA_STORAGE_POSTGRES_PASSWORD_FROM_VAULT} # Injetado pelo .env
- TZ=${SYSTEM_TIMEZONE_FROM_VAULT} # Injetado pelo .env
healthcheck: # Verifica se o banco de dados PostgreSQL está pronto para aceitar conexões
test: ["CMD-SHELL", "pg_isready -U authelia -d authelia -h localhost"] # -h localhost pois roda dentro do container
interval: 10s
timeout: 5s
retries: 5
5.4. Playbook ansible/playbooks/setup-core-networking.yml¶
Este playbook Ansible orquestra a preparação dos arquivos de configuração e o deploy das stacks core e authelia.
Principais Ações do Playbook:
- Cria Redes Docker: Garante que as redes
proxyeinternal_servicesexistam nacore-services-vm. - Executa Role
infra/traefik-config:- Cria os diretórios de configuração e dados para Traefik no volume NFS.
- Copia o
traefik.yml(config estática) templateado. - Copia os arquivos de configuração dinâmica (middlewares como
security_headers.yml,nextcloud_middleware.yml, e o roteamento para RAG servicesrag_services.yml) para o diretório apropriado. - Garante que o arquivo
acme.json(para certificados SSL) exista com as permissões corretas.
- Deploy da Stack
core: Usa o módulocommunity.docker.docker_compose_v2para fazer deploy da stackcore(Traefik, Cloudflared), injetando as variáveis de ambiente necessárias do Ansible Vault/main.yml. - Aguarda Traefik: Espera o healthcheck do Traefik passar antes de prosseguir.
- Executa Role
infra/authelia-config:- Cria o diretório de configuração para Authelia no volume NFS.
- Copia o
configuration.ymleusers_database.yml(exemplo) templateados.
- Deploy da Stack
authelia: Usacommunity.docker.docker_compose_v2para fazer deploy da stackauthelia(Authelia app, Authelia DB), injetando as variáveis de ambiente.
Ações Preparatórias: Roles Ansible para Configuração¶
Antes de criar o playbook, precisamos dos roles que preparam os arquivos de configuração.
Role: ansible/roles/infra/traefik-config/¶
Este role prepara os arquivos de configuração do Traefik.
tasks/main.yml¶
# ansible/roles/infra/traefik-config/tasks/main.yml
- name: "Garantir que diretórios de configuração do Traefik existam na VM (via NFS)"
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ docker_puid }}" # PUID/PGID para que o container Traefik possa ler/escrever (e.g., acme.json)
group: "{{ docker_pgid }}"
mode: '0750' # Mais restritivo para diretórios de config
loop:
# Caminhos DENTRO da VM, que são montagens NFS.
# Correspondem aos volumes no docker-compose do Traefik.
- "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/config" # Para traefik.yml
- "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/config/dynamic" # Para configs dinâmicas (middlewares)
- "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/data" # Para acme.json e logs
- name: "Copiar configuração estática do Traefik (traefik.yml)"
ansible.builtin.template:
src: traefik.yml.j2
dest: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/config/traefik.yml"
owner: "{{ docker_puid }}"
group: "{{ docker_pgid }}"
mode: '0640' # Traefik só precisa ler este
- name: "Copiar configuração de headers de segurança para Traefik (dinâmica)"
ansible.builtin.template:
src: security_headers.yml.j2
dest: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/config/dynamic/security_headers.yml"
owner: "{{ docker_puid }}"
group: "{{ docker_pgid }}"
mode: '0640'
- name: "Copiar configuração dinâmica para serviços RAG (se ai-desktop-vm existir)"
ansible.builtin.template:
src: dynamic_rag_services.yml.j2 # Para rotear para OpenWebUI na ai-desktop-vm
dest: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/config/dynamic/rag_services.yml"
owner: "{{ docker_puid }}"
group: "{{ docker_pgid }}"
mode: '0640'
when: "'ai-desktop-vm' in groups['virtual_machines']" # Só cria se a VM AI estiver no inventário
- name: "Copiar middleware para Nextcloud (dinâmico)"
ansible.builtin.template:
src: nextcloud_middleware.yml.j2 # Headers recomendados para Nextcloud
dest: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/config/dynamic/nextcloud_middleware.yml"
owner: "{{ docker_puid }}"
group: "{{ docker_pgid }}"
mode: '0640'
- name: "Garantir que acme.json existe e tem permissões corretas para Traefik"
ansible.builtin.file:
path: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/traefik/data/acme.json"
state: touch # Cria se não existir, não modifica se existir (Traefik gerencia o conteúdo)
owner: "{{ docker_puid }}" # Usuário que o container Traefik usa (ou root se PUID/PGID não for setado no container)
group: "{{ docker_pgid }}" # Certifique-se que as permissões do NFS squash para este PUID/PGID
mode: '0600' # Permissões restritas para o arquivo de certificados SSL
templates/traefik.yml.j2 (Configuração Estática)¶
# ansible/roles/infra/traefik-config/templates/traefik.yml.j2
# Configuração Estática do Traefik (gerada por Ansible)
global:
checkNewVersion: true
sendAnonymousUsage: false # Desabilitar telemetria para privacidade
# API e Dashboard do Traefik (será acessível via rota segura definida nas labels do container Traefik)
api:
dashboard: true
# insecure: true # APENAS para debug local em ambiente isolado. NUNCA em produção. Removido.
# EntryPoints (Pontos de Entrada HTTP e HTTPS)
entryPoints:
web: # Entrypoint para HTTP na porta 80
address: ":80"
http:
redirections: # Redirecionar todo tráfego HTTP para HTTPS
entryPoint:
to: "websecure" # Nome do entrypoint HTTPS
scheme: "https"
permanent: true # Usa redirecionamento 301 (permanente)
websecure: # Entrypoint principal para HTTPS na porta 443
address: ":443"
http:
tls:
certResolver: "letsencrypt" # Resolvedor padrão para certificados TLS
domains: # Opcional: Define domínios para os quais gerar certificados "on-demand"
- main: "{{ base_domain }}" # Seu domínio principal
sans: # Subdomínios alternativos (SANs)
- "*.{{ base_domain }}" # Wildcard para todos os subdomínios
middlewares: # Middlewares globais aplicados a TODAS as rotas no entrypoint websecure
- "securityHeaders@file" # Referencia o middleware de headers de segurança (definido em dynamic/security_headers.yml)
# Provedores de Configuração (Docker e Arquivos para configs dinâmicas)
providers:
docker:
endpoint: "unix:///var/run/docker.sock" # Socket Docker para detectar containers
exposedByDefault: false # APENAS containers com label 'traefik.enable=true' são expostos
network: "proxy" # Rede Docker padrão onde Traefik procura por containers a serem expostos
# watch: true # Padrão, Traefik monitora eventos Docker
file:
# Diretório para arquivos de configuração dinâmica (e.g., middlewares, serviços manuais)
# O caminho é DENTRO do container Traefik, mapeado do volume NFS.
directory: "/etc/traefik/dynamic_configs" # Corresponde ao volume montado no docker-compose do Traefik
watch: true # Traefik monitora este diretório por mudanças e recarrega dinamicamente
# Resolvedores de Certificados (Let's Encrypt)
certificatesResolvers:
letsencrypt: # Nome do resolvedor (usado em entrypoints e routers)
acme:
email: "{{ letsencrypt_email }}" # Email para notificações da Let's Encrypt (do vault.yml)
storage: "/data/acme.json" # Onde os certificados são armazenados (DENTRO do container Traefik)
# Desafio DNS-01 usando Cloudflare API Token
# O token é passado como variável de ambiente para o container Traefik.
dnsChallenge:
provider: "cloudflare"
# delayBeforeCheck: 0 # Padrão geralmente funciona bem
# resolvers: # Opcional: especificar servidores DNS para verificação do desafio
# - "1.1.1.1:53"
# - "8.8.8.8:53"
# Configurações de Log (opcional, mas útil para debug)
log:
level: INFO # Níveis: DEBUG, INFO, WARNING, ERROR, FATAL, PANIC
filePath: "/data/traefik.log" # Caminho DENTRO do container Traefik (mapeado para volume NFS)
format: json # Ou common
accessLog:
filePath: "/data/access.log" # Caminho DENTRO do container Traefik
format: json # Ou common
bufferingSize: 100 # Número de linhas de log para bufferizar antes de escrever
# Healthcheck para o container Traefik (usado por depends_on: condition: service_healthy no docker-compose)
ping: {} # Habilita o entrypoint /ping para healthchecks do Traefik
templates/security_headers.yml.j2 (Middleware de Segurança)¶
# ansible/roles/infra/traefik-config/templates/security_headers.yml.j2
# (Copiado para /etc/traefik/dynamic_configs/security_headers.yml no container Traefik)
# Define um middleware para adicionar headers de segurança HTTP.
http:
middlewares:
securityHeaders: # Nome do middleware (referenciado em entrypoints ou routers)
headers:
# Habilita o filtro XSS do navegador (obsoleto em navegadores modernos, mas não prejudicial)
browserXssFilter: true
# Impede que o navegador tente adivinhar o tipo de conteúdo (MIME-sniffing)
contentTypeNosniff: true
# Força o uso de HSTS (HTTP Strict Transport Security)
forceSTSHeader: true
stsIncludeSubdomains: true # Aplica HSTS a todos os subdomínios
stsPreload: true # Permite que seu domínio seja incluído na lista de preload HSTS
stsSeconds: 31536000 # Duração do HSTS em segundos (1 ano). CUIDADO: habilite apenas quando tudo estiver funcionando 100% em HTTPS.
# Previne clickjacking, permitindo iframes apenas do mesmo domínio.
customFrameOptionsValue: "SAMEORIGIN"
# Content-Security-Policy (CSP) - ALTAMENTE RECOMENDADO, mas requer ajuste fino por aplicação.
# Este é um exemplo básico. Você precisará ajustá-lo para seus serviços específicos.
# contentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'self' *.{{ base_domain }};"
# Controla quais informações de referer são enviadas.
referrerPolicy: "strict-origin-when-cross-origin"
# Controla o acesso a APIs do navegador.
permissionsPolicy: "camera=(), microphone=(), geolocation=(), payment=(), usb=(), vr=()"
# Adiciona outros headers de segurança conforme necessário
# X-Content-Type-Options: "nosniff" (já coberto por contentTypeNosniff)
# Strict-Transport-Security: (já coberto por stsSeconds, etc.)
Content-Security-Policy (CSP)
O contentSecurityPolicy é um header muito poderoso para mitigar XSS e outros ataques de injeção. No entanto, configurá-lo incorretamente pode quebrar a funcionalidade das suas aplicações. O exemplo acima é muito básico. Você precisará pesquisar e testar as diretivas CSP apropriadas para cada um dos seus serviços (Nextcloud, Grafana, etc.) ou começar com uma política menos restritiva e aumentá-la gradualmente.
templates/dynamic_rag_services.yml.j2 (Roteamento para VM de IA)¶
# ansible/roles/infra/traefik-config/templates/dynamic_rag_services.yml.j2
# (Copiado para /etc/traefik/dynamic_configs/rag_services.yml no container Traefik)
# Define roteamento para serviços rodando na ai-desktop-vm (e.g., OpenWebUI).
# Usa o File Provider do Traefik para definir serviços que não são descobertos via Docker (porque estão em outra VM).
http:
routers:
openwebui-dynamic: # Roteador para OpenWebUI
entryPoints: ["websecure"] # Usa o entrypoint HTTPS
rule: "Host(`openwebui.{{ base_domain }}`)" # Subdomínio para OpenWebUI
service: "openwebui-svc-dynamic" # Nome do serviço Traefik definido abaixo
tls:
certResolver: "letsencrypt" # Usa Let's Encrypt
middlewares:
- "authelia@docker" # Protegido por Authelia (middleware definido no compose do Authelia)
# Adicione aqui roteadores para outros serviços RAG/AI na ai-desktop-vm se necessário
# Exemplo para um hipotético OpenNotebookLM
# opennotebooklm-dynamic:
# entryPoints: ["websecure"]
# rule: "Host(`notebooklm.{{ base_domain }}`)"
# service: "opennotebooklm-svc-dynamic"
# tls: { certResolver: "letsencrypt" }
# middlewares: ["authelia@docker"]
services:
openwebui-svc-dynamic: # Serviço Traefik para OpenWebUI
loadBalancer:
servers:
# Aponta para o IP da ai-desktop-vm e a porta interna do container OpenWebUI.
# A variável ai_desktop_vm_ip_var vem do inventário Ansible (hosts.ini).
- url: "http://{{ ai_desktop_vm_ip_var }}:8080" # Porta que OpenWebUI expõe na ai-desktop-vm
# opennotebooklm-svc-dynamic:
# loadBalancer:
# servers:
# - url: "http://{{ ai_desktop_vm_ip_var }}:7860" # Porta interna do Open-NotebookLM
templates/nextcloud_middleware.yml.j2 (Middleware para Nextcloud)¶
# ansible/roles/infra/traefik-config/templates/nextcloud_middleware.yml.j2
# (Copiado para /etc/traefik/dynamic_configs/nextcloud_middleware.yml no container Traefik)
# Define um middleware com headers recomendados para Nextcloud.
http:
middlewares:
nextcloud-headers: # Nome do middleware
headers:
# Headers de segurança recomendados pelo Nextcloud para .htaccess, adaptados para Traefik
# https://docs.nextcloud.com/server/latest/admin_manual/installation/harden_server.html#security-headers
# Strict-Transport-Security já é tratado globalmente, mas pode ser reforçado aqui se necessário.
# Referrer-Policy já é tratado globalmente.
# X-Content-Type-Options: nosniff (já tratado globalmente)
# X-Frame-Options: SAMEORIGIN (já tratado globalmente)
# X-Permitted-Cross-Domain-Policies: none
# X-Robots-Tag: none
# X-XSS-Protection: 1; mode=block (já tratado globalmente)
# Adicione outros específicos se Nextcloud evoluir suas recomendações.
# Este middleware pode ser combinado com 'authelia@docker' e 'securityHeaders@file'
# usando um chain middleware se necessário, ou aplicado diretamente.
# Exemplo:
# traefik.http.routers.nextcloud.middlewares: "authelia@docker,nextcloud-headers@file"
# Se os headers globais já cobrem, este middleware pode não ser estritamente necessário,
# mas pode ser usado para ajustes finos específicos para Nextcloud.
# Por enquanto, pode ser deixado vazio ou com headers muito específicos que não são globais.
# Exemplo de header que poderia ser específico:
# customRequestHeaders:
# X-My-Nextcloud-Header: "true" # Exemplo, não é um header real do Nextcloud
# Ou, para redirecionamentos .well-known para CalDAV/CardDAV se não tratados de outra forma:
# (Mas Traefik faria isso com routers, não headers)
pass: true # Middleware vazio por enquanto, para estrutura. Adicione headers específicos se necessário.
Nextcloud Headers
Nextcloud tem recomendações específicas de headers de segurança, muitas das quais podem já estar cobertas pelos securityHeaders@file globais. Este middleware nextcloud-headers@file pode ser usado para adicionar ou sobrescrever headers especificamente para o Nextcloud se necessário. Consulte a documentação do Nextcloud para os headers mais recentes.
Role: ansible/roles/infra/authelia-config/¶
Este role prepara os arquivos de configuração do Authelia.
tasks/main.yml¶
# ansible/roles/infra/authelia-config/tasks/main.yml
- name: "Garantir que diretórios de configuração do Authelia existam na VM (via NFS)"
ansible.builtin.file:
path: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/authelia/config"
state: directory
owner: "{{ docker_puid }}" # PUID/PGID para que o container Authelia possa ler
group: "{{ docker_pgid }}"
mode: '0750' # Authelia só precisa ler (e talvez escrever logs se configurado para arquivos)
- name: "Copiar configuração principal do Authelia (configuration.yml)"
ansible.builtin.template:
src: configuration.yml.j2
dest: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/authelia/config/configuration.yml"
owner: "{{ docker_puid }}"
group: "{{ docker_pgid }}"
mode: '0640' # Authelia só precisa ler este
- name: "Copiar banco de dados de usuários inicial do Authelia (users_database.yml)"
ansible.builtin.template:
src: users_database.yml.j2
dest: "{{ vm_nfs_mount_base_path }}/{{ zfs_docker_volumes_dataset_name }}/authelia/config/users_database.yml"
owner: "{{ docker_puid }}"
group: "{{ docker_pgid }}"
mode: '0640'
# Este arquivo só é usado se o backend de autenticação do Authelia for 'file'.
# No nosso caso, usaremos PostgreSQL, então este arquivo serve mais como um exemplo
# ou um backup se mudarmos para o backend 'file'.
templates/configuration.yml.j2 (Configuração Principal Authelia)¶
# ansible/roles/infra/authelia-config/templates/configuration.yml.j2
# Configuração Principal do Authelia (gerada por Ansible)
# Nome da instância exibido em alguns locais (e.g., título da página)
instance_name: "Meu Servidor Doméstico"
# URL de redirecionamento padrão se Authelia não souber para onde enviar o usuário
# Geralmente o próprio portal Authelia.
default_redirection_url: "https://auth.{{ base_domain }}/"
# Tema da interface Authelia (opções: light, dark, grey)
theme: dark
# Segredos JWT e de Sessão (do vault.yml)
jwt_secret: "{{ authelia_jwt_secret }}"
# Método 2FA padrão para novos usuários (pode ser sobrescrito por usuário)
# Opções: "totp" (Time-based One-Time Password), "webauthn" (FIDO2/Passkeys)
default_2fa_method: "totp"
# Configuração da Sessão
session:
name: "homelab_authelia_session" # Nome do cookie de sessão
secret: "{{ authelia_session_secret }}" # Secret para criptografar o cookie (do vault.yml)
expiration: 1h # Duração da sessão (e.g., 1h, 8h, 30d). Aumente se desejar sessões mais longas.
inactivity: 10m # Duração da inatividade antes de exigir re-autenticação (login novamente).
# Domínio do cookie. Importante para SSO entre subdomínios.
# DEVE ser o domínio pai (e.g., seudominio.com, NÃO auth.seudominio.com).
domain: "{{ base_domain }}"
# Se usar Redis para sessões distribuídas (não coberto neste guia simples):
# redis:
# host: authelia_redis # Nome do serviço Docker do Redis
# port: 6379
# password: "SENHA_DO_REDIS_SE_HOUVER"
# database_index: 1 # Índice do banco de dados Redis
# Armazenamento de Dados do Usuário (PostgreSQL)
# Authelia usará este banco de dados para armazenar informações de usuários, dispositivos 2FA, etc.
storage:
postgres:
address: "tcp://authelia_db:5432" # Nome do serviço Docker do banco de dados Authelia e porta
database: authelia # Nome do banco de dados
username: authelia # Usuário do banco de dados
password: "{{ authelia_storage_postgres_password }}" # Senha do usuário do DB (do vault.yml)
timeout: 5s # Timeout para conexão com o DB
# Para SSL com o DB (se configurado no PostgreSQL e você tiver os certificados):
# schema: public # Padrão
# ssl:
# mode: require # ou verify-ca, verify-full
# root_certificate: /config/certs/db_ca.pem
# client_certificate: /config/certs/db_client.crt
# client_key: /config/certs/db_client.key
# Backend de Autenticação
# Define como Authelia verifica as credenciais do usuário.
# Com 'storage.postgres' configurado, Authelia usará o mesmo DB para autenticação.
# Se você quisesse usar um arquivo YAML para usuários (users_database.yml):
# authentication_backend:
# file:
# path: /config/users_database.yml # Caminho DENTRO do container Authelia
# # Configurações de hashing de senha (argon2id é o padrão e recomendado)
# password:
# algorithm: argon2id
# iterations: 3
# memory: 65536 # KiB (64MB)
# parallelism: 4 # Número de threads
# salt_length: 16
# key_length: 32
# Regras de Controle de Acesso
# Define quais domínios/subdomínios são protegidos e qual política aplicar.
access_control:
default_policy: deny # Política padrão se nenhuma regra corresponder (deny, one_factor, two_factor, bypass)
rules:
# Regra para o próprio portal Authelia (auth.seudominio.com)
# Usuários precisam acessá-lo para logar, então não deve ser protegido pelo próprio Authelia.
- domain: "auth.{{ base_domain }}"
policy: bypass # Não requer autenticação para acessar o portal de login
# Regra geral para todos os outros subdomínios do seu domínio base: requer dois fatores.
- domain: "*.{{ base_domain }}"
policy: two_factor # Requer login bem-sucedido com primeiro e segundo fator
# Exemplo: Permitir acesso da rede local sem Authelia (bypass) para certos serviços.
# Cuidado ao usar bypass, pois anula a proteção do Authelia para essas condições.
# - domain: "servico_local.{{ base_domain }}"
# networks: # IPs ou CIDRs da sua rede local
# - "192.168.1.0/24"
# policy: bypass
# Notificador (para reset de senha, alertas de segurança, etc.) - ALTAMENTE RECOMENDADO
# Configure para receber notificações por email.
# notifier:
# smtp:
# address: "smtp.seuprovedor.com:587" # Servidor SMTP e porta (587 para STARTTLS, 465 para SSL/TLS direto)
# username: "[email protected]"
# password: "SENHA_DO_SEU_EMAIL_DE_ENVIO_APP_PASSWORD" # Use senha de aplicativo se o provedor exigir
# sender: "Authelia <authelia@{{ base_domain }}>" # Remetente exibido nos emails
# subject: "[Authelia - {{ base_domain }}] {title}" # Assunto do email (pode usar placeholders)
# # Para TLS (STARTTLS é geralmente usado na porta 587):
# # tls:
# # server_name: "smtp.seuprovedor.com" # Se diferente do address
# # skip_verify: false # Mantenha false para segurança, a menos que seja um servidor interno com cert autoassinado
# # minimum_version: "TLS1.2"
# # Para verificar a configuração do notificador ao iniciar Authelia:
# # startup_check_address: "[email protected]"
# Configuração TOTP (Time-based One-Time Password)
totp:
issuer: "Homelab {{ base_domain }}" # Nome exibido no app autenticador (e.g., Google Authenticator, Authy)
algorithm: SHA1 # Padrão (SHA256, SHA512 também são opções)
digits: 6 # Padrão (número de dígitos no código TOTP)
period: 30 # Padrão (período de validade do código em segundos)
skew: 1 # Permite uma pequena dessincronização de relógio (1 período para frente ou para trás)
# Regulação (Proteção contra Brute-Force nas tentativas de login)
regulation:
max_retries: 3 # Número de tentativas de login falhas antes do banimento temporário.
find_time: 2m # Janela de tempo para contar as tentativas falhas (e.g., 3 falhas em 2 minutos).
ban_time: 5m # Duração do banimento temporário.
templates/users_database.yml.j2 (Exemplo de Usuários - se backend file)¶
# ansible/roles/infra/authelia-config/templates/users_database.yml.j2
# Arquivo de Banco de Dados de Usuários Locais do Authelia (gerado por Ansible)
# Hashes de senha DEVEM ser gerados com 'authelia crypto hash generate argon2'
# !!! NOTA IMPORTANTE !!!
# Este arquivo SÓ É USADO se 'authentication_backend.file' estiver configurado em configuration.yml.
# Com a configuração atual usando PostgreSQL para 'storage', Authelia gerenciará os usuários
# diretamente no banco de dados. O primeiro usuário (admin) será criado com base nas
# variáveis de ambiente passadas para o container Authelia na primeira execução, ou
# você precisará registrar o admin via UI/API do Authelia se essa funcionalidade existir.
# Este arquivo é mantido aqui como um exemplo ou fallback.
users:
# Usuário administrador inicial (do vault.yml)
"{{ authelia_admin_user_initial }}": # Nome do usuário
displayname: "Administrador"
# O hash da senha já foi gerado e está na variável do vault.yml
password: "{{ authelia_admin_password_hash_initial }}"
email: "{{ letsencrypt_email }}" # Email para notificações, reset de senha
groups: # Grupos para controle de acesso (podem ser usados nas regras de access_control)
- admins
- users
# Adicione outros usuários aqui conforme necessário, se usar o backend 'file':
# "outro_usuario":
# displayname: "Outro Usuário"
# password: "$argon2id$v=19$m=..." # Hash gerado para a senha deste usuário
# email: "[email protected]"
# groups:
# - users
# - media_users # Exemplo de grupo customizado
Playbook ansible/playbooks/setup-core-networking.yml¶
Este playbook orquestra o deploy da stack de rede.
# ansible/playbooks/setup-core-networking.yml
- hosts: core-services-vm # Todas estas stacks rodam na core-services-vm
become: yes # Para gerenciar Docker e arquivos de configuração no sistema de arquivos
vars_files:
- ../inventories/home/group_vars/all/vault.yml
# Variáveis de group_vars/all/main.yml são carregadas automaticamente
tasks:
- name: "Garantir que redes Docker globais (proxy, internal_services) existam"
community.docker.docker_network:
name: "{{ item }}"
state: present # Cria se não existir, não faz nada se já existir
loop:
- proxy
- internal_services
- name: "Incluir role para criar diretórios e copiar configs para Traefik"
ansible.builtin.include_role:
name: infra/traefik-config
- name: "Deploy Core Stack (Traefik, Cloudflared)"
community.docker.docker_compose_v2: # Módulo Ansible para Docker Compose v2
project_src: "{{ playbook_dir }}/../../docker/stacks/core/" # Caminho para o diretório da stack
files:
- docker-compose.yml # Arquivo compose a ser usado
state: present # Garante que a stack está rodando conforme definido
remove_orphans: true # Remove containers órfãos se o compose mudar
pull: always # Sempre tenta puxar a imagem mais recente (respeitando a tag especificada)
environment: # Injeta variáveis Ansible/Vault no ambiente do docker-compose
# Estas variáveis são usadas dentro do docker-compose.yml da stack 'core'
# Para Traefik:
CLOUDFLARE_API_TOKEN_FROM_VAULT: "{{ cloudflare_api_token }}"
BASE_DOMAIN_FROM_VAULT: "{{ base_domain }}"
LETSENCRYPT_EMAIL_FROM_VAULT: "{{ letsencrypt_email }}" # Usado por Traefik
# Para Cloudflared:
CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT: "{{ cloudflare_tunnel_token }}"
# Para caminhos de volume NFS (usados por Traefik):
VM_NFS_MOUNT_BASE_PATH: "{{ vm_nfs_mount_base_path }}"
ZFS_DOCKER_VOLUMES_DATASET_NAME: "{{ zfs_docker_volumes_dataset_name }}"
# IP da VM para mapeamento de portas no Traefik (se referenciado no compose):
CORE_SERVICES_VM_IP_VAR: "{{ core_services_vm_ip_var }}"
register: core_stack_deploy_result
- name: "Aguardar Traefik estar saudável após deploy/atualização"
community.docker.docker_container_info:
name: traefik # Nome do container Traefik
register: traefik_container_status
until: traefik_container_status.container.State.Health.Status == "healthy" # Espera pelo healthcheck do Traefik
retries: 15 # Número de tentativas
delay: 10 # Segundos de espera entre tentativas
when: core_stack_deploy_result.changed # Só espera se a stack Traefik foi realmente alterada/deployada
- name: "Incluir role para criar diretórios e copiar configs para Authelia"
ansible.builtin.include_role:
name: infra/authelia-config
- name: "Deploy Authelia Stack"
community.docker.docker_compose_v2:
project_src: "{{ playbook_dir }}/../../docker/stacks/authelia/"
files:
- docker-compose.yml
state: present
remove_orphans: true
pull: always
environment: # Variáveis para a stack Authelia
SYSTEM_TIMEZONE_FROM_VAULT: "{{ system_timezone }}"
BASE_DOMAIN_FROM_VAULT: "{{ base_domain }}"
AUTHELIA_STORAGE_POSTGRES_PASSWORD_FROM_VAULT: "{{ authelia_storage_postgres_password }}"
AUTHELIA_JWT_SECRET_FROM_VAULT: "{{ authelia_jwt_secret }}" # Necessário se Authelia lê do env
AUTHELIA_SESSION_SECRET_FROM_VAULT: "{{ authelia_session_secret }}" # Necessário se Authelia lê do env
# Se for criar o admin inicial via ENV (algumas versões do Authelia suportam isso)
# AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS_0_ID: admin # Exemplo, verifique a doc do Authelia
# AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS_0_DESCRIPTION: Admin User
# AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS_0_SECRET: "{{ authelia_admin_password_hash_initial }}" # Não é o ideal
# AUTHELIA_USERS_ADMIN_PASSWORD: "{{ authelia_admin_password_hash_initial }}" # Não é o ideal
# É melhor que o configuration.yml (template) já tenha os segredos e hashes.
# As variáveis PUID/PGID e de caminho NFS também devem ser passadas se referenciadas no compose Authelia
VM_NFS_MOUNT_BASE_PATH: "{{ vm_nfs_mount_base_path }}"
ZFS_DOCKER_VOLUMES_DATASET_NAME: "{{ zfs_docker_volumes_dataset_name }}"
Executando o Playbook de Configuração da Rede Core¶
- Verifique os Roles e Templates: Certifique-se de que todos os arquivos de role (
infra/traefik-config,infra/authelia-config) e seus respectivos templates (.j2arquivos) estão criados e preenchidos com o código correto no seu projeto Ansible. - Execute a Partir da Máquina de Controle: Na sua máquina de controle Ansible, navegue até a raiz do seu projeto
home-server/. -
Execute o Playbook:
Você será solicitado a fornecer a senha do Ansible Vault.
5.5. Configuração de DNS na Cloudflare¶
Após o deploy bem-sucedido da stack de rede, especialmente do container cloudflared (Cloudflare Tunnel), você precisa configurar seus registros DNS na Cloudflare para que seus subdomínios (e.g., portainer.meuhomelab.com, traefik.meuhomelab.com, auth.meuhomelab.com) apontem corretamente para o seu túnel.
Abordagem Recomendada: Usar Ingress Rules no Cloudflare Zero Trust Dashboard
Esta abordagem oferece a maior flexibilidade e é gerenciada centralmente no painel da Cloudflare. Assumindo que seu comando cloudflared no docker-compose.yml é:
command: tunnel --no-autoupdate --metrics 0.0.0.0:2000 run --token ${CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT}
(ou seja, sem as flags --hostname e --url que tentam gerenciar DNS automaticamente).
- Acesse o Cloudflare Zero Trust Dashboard: Vá para one.dash.cloudflare.com.
- No menu lateral, navegue para Access -> Tunnels.
- Localize e selecione o túnel que foi criado (você deve ter criado um ao obter o
CLOUDFLARE_TUNNEL_TOKEN_FROM_VAULT). - Clique na aba "Public Hostnames".
- Clique em "+ Add a public hostname" para cada serviço que você deseja expor.
- Subdomain: O prefixo do seu serviço (e.g.,
portainer,traefik,auth,nextcloud,hass,grafana,openwebui, etc.). - Domain: Selecione seu domínio base (
{{ base_domain }}e.g.,meuhomelab.com). - Path: Deixe em branco (a menos que você queira rotear caminhos específicos para serviços diferentes, o que é mais avançado e geralmente tratado pelo Traefik).
- Service Type:
HTTP. - Service URL:
traefik:80- Isso instrui o Cloudflare Tunnel a encaminhar o tráfego para o serviço Docker chamado
traefik(o container Traefik) na porta80(dentro da rede Dockerproxyonde ambos,cloudflaredetraefik, estão conectados).
- Isso instrui o Cloudflare Tunnel a encaminhar o tráfego para o serviço Docker chamado
- Clique em "Save hostname".
- Subdomain: O prefixo do seu serviço (e.g.,
Exemplos de Public Hostnames a Adicionar Inicialmente:
portainer.{{ base_domain }}->http://traefik:80traefik.{{ base_domain }}->http://traefik:80(para o dashboard do Traefik)auth.{{ base_domain }}->http://traefik:80(para o portal do Authelia)- (Opcional, mas recomendado) Catch-all com Wildcard:
- Subdomain:
* - Domain:
{{ base_domain }} - Service:
http://traefik:80
Vantagem do Catch-All (
*)Configurar um hostname público com o subdomínio
*(wildcard) significa que qualquer subdomínio de{{ base_domain }}que não tenha uma regra de Public Hostname mais específica na Cloudflare será automaticamente encaminhado para o Traefik. O Traefik então usará suas próprias regras de roteamento (definidas por labels Docker ou File Provider) para direcionar para o container correto. Isso simplifica muito a adição de novos serviços no futuro: você só precisa configurar as labels no Traefik, e não precisará adicionar um novo Public Hostname na Cloudflare toda vez (a menos que queira configurações de túnel muito específicas por subdomínio, como políticas de acesso Cloudflare). - Subdomain:
Opção Alternativa: Usando a flag --hostname no cloudflared
Se o seu comando cloudflared no docker-compose.yml inclui:
--hostname {{ base_domain }} --url http://traefik:80
O cloudflared tentará criar e gerenciar automaticamente um registro CNAME para {{ base_domain }} e um CNAME wildcard *.{{ base_domain }} apontando para o seu túnel.
- Ação: Verifique no painel DNS da Cloudflare (não no Zero Trust, mas na seção DNS do seu domínio) se esses CNAMEs foram criados (e.g.,
seudominio.com CNAME <TUNNEL_ID>.cfargotunnel.come* CNAME <TUNNEL_ID>.cfargotunnel.com). O<TUNNEL_ID>é o UUID do seu túnel. - Menos Flexível: Esta abordagem é mais simples, mas oferece menos controle granular do que as Ingress Rules/Public Hostnames no dashboard Zero Trust.
Verificação Importante (em ambos os casos):
- Status do Proxy Cloudflare (Nuvem Laranja): Para todos os registros DNS (CNAMEs) relacionados aos seus serviços expostos, certifique-se de que o "Proxy status" na Cloudflare está habilitado (nuvem laranja). Isso garante que o tráfego passe pela rede da Cloudflare, beneficiando-se de suas proteções e permitindo que o túnel funcione.
Pode levar alguns minutos para que quaisquer novas configurações de DNS ou Public Hostnames se propaguem globalmente.
Com a stack de rede segura implementada e o DNS configurado, você está pronto para começar a implantar suas aplicações de forma organizada e segura usando o Portainer.