implant traefik-edge
Guarda de borda para o Traefik (v2.29.0+): arquivos sensíveis (.git,
.env, chaves, certificados, compose…) recebem 403 no próprio Traefik,
antes de a requisição chegar a qualquer serviço. É uma segunda linha de
defesa para o caso de uma aplicação servir, por engano, um arquivo que não
devia.
A guarda é o arquivo ccs-edge-sensitive.yml no diretório do
file-provider do Traefik. Com watch: true, o Traefik recarrega sozinho:
não há restart.
Uso
implant traefik-edge [OPTIONS]Sem flag, o comando audita (somente leitura).
Modos
`--audit` (padrão)
Acha o container do Traefik em execução, descobre de onde ele lê a config estática (arquivo, flags ou variáveis de ambiente), localiza o diretório do file-provider no host (mapeando o bind mount) e responde:
- se a guarda de paths sensíveis existe;
- se o catch-all de hosts sem router (
--with-404) existe; - o resultado de um probe: o implant pede
/.git/HEADao Traefik local (Host fictício, certificado ignorado).403prova que a guarda vale; a simples presença do arquivo não prova nada.
O campo risk resume o veredito:
risk |
Significado |
|---|---|
ok |
Guarda presente e efetiva |
sensitive_paths_unguarded |
Há file-provider, mas a guarda não existe — rode --configure |
guard_not_effective |
O arquivo existe, mas o probe não deu 403 (veja recent_errors) |
no_file_provider |
O Traefik não tem file-provider — ver --enable-file-provider |
file_provider_single_file |
O file-provider usa filename: (arquivo único): o Traefik ignora arquivos vizinhos |
`--configure`
Escreve a guarda no diretório dinâmico do Traefik (hot-reload, sem restart) e
prova o efeito com o probe. Idempotente: se o arquivo já está idêntico, nada
muda. Se existia uma versão diferente, um backup é gravado ao lado (caminho no
campo backup da saída).
Se o probe não der 403 e o log do Traefik mostrar que o file-provider não
subiu (YAML inválido anterior), o --configure reinicia o container do
Traefik uma vez e prova de novo (restarted: true).
Com file-provider de arquivo único (filename:), o --configure recusa e
indica as duas saídas: trocar por directory: ou colar o bloco do --print
dentro desse arquivo (mesclando sob http:).
`--print`
Só imprime o YAML que seria escrito, sem tocar em nada. Combine com
--major e --with-404 para gerar o bloco de uma versão específica.
`--enable-file-provider` (v2.35.0+)
Para Traefik que nasceu só com o provider do Docker: sem file-provider, a guarda não tem onde entrar. Funciona em duas etapas, com o dry-run como pré-condição verificada pelo próprio binário:
- Sem
--apply(dry-run): acha odocker-compose.ymle o serviço pelas labels do container, gera o patch e mostra o diff. Se existetraefik.ymlno host, o file-provider entra nele (providers.file.directory+watch: true) e o compose só ganha o bind do diretório dinâmico; sem arquivo estático, entram duas flags nocommand:do compose. O plano é gravado com o hash dos arquivos. --apply: só roda com um plano de até 1 hora para o mesmo compose (ninguém editou no meio). Faz backup, escreve, recria o serviço (docker compose up -d --force-recreate), confere que o Traefik subiu com o file-provider — senão restaura o backup e recria de novo (rollback) — e então escreve a guarda e prova com o probe.
O patch é textual (preserva comentários e formatação) e validado por parse
YAML. command: em forma de string/inline, TOML ou config dentro do container
sem mount são recusados com instrução manual.
Flags
| Flag | Descrição |
|---|---|
--audit |
Só auditar (padrão) |
--configure |
Escrever a guarda no diretório dinâmico do Traefik |
--with-404 |
Também instalar o catch-all para hosts sem router. Host com serviço continua respondendo o 404 do próprio serviço; o catch-all só pega host que não tem router (e, portanto, nenhum 404 próprio) e responde 403 com o mesmo bloqueio da guarda |
--bloquear <ENTRADA> |
Acrescenta uma entrada à lista de bloqueio própria (repetível; exige --configure) |
--desbloquear <ENTRADA> |
Remove uma entrada da lista de bloqueio própria (repetível; exige --configure) |
--dry-run |
Mostra o que escreveria, sem escrever |
--print |
Só imprime o YAML (não toca em nada) |
--major <2|3> |
Força a versão MAJOR do Traefik em vez de detectar |
--enable-file-provider |
Liga o file-provider editando o compose/traefik.yml. Sem --apply é dry-run |
--apply |
Aplica o plano do dry-run (exige dry-run recente com o compose inalterado) |
O que a guarda bloqueia
Além desta lista embutida, entram as entradas da lista de bloqueio própria.
Na raiz e em qualquer subdiretório:
.git,.env*,.secrets*,.deploy.yml,.gitops.yml,CLAUDE*.mddocker-compose*.yml,Dockerfile*,.npmrc,.yarnrc*.pem,*.key,*.crt,*.cer,*.p12,*.pfx,*.jks,*.keystore,*.enc,*.log- arquivos de credencial:
.test_credentials,.credentials(arquivo ou diretório),.htpasswd,.pgpass,.netrc, chaves SSH privadas sem extensão (id_rsa,id_dsa,id_ecdsa,id_ed25519— o.pubpassa) - iscas comuns de scanner (sem diferenciar maiúsculas):
phpinfo.php,info.php,wp-config.php,wp-login.php,xmlrpc.php,aws-config.js/.json,credentials.json,web.config,.htaccess,.aws/…
Detalhes técnicos da guarda:
- Router de prioridade
10000, em HTTP e em TLS, sem fixarentryPoints(vale em todos), com o serviçonoop@internal. - O 403 vem de um middleware de allowlist com origem impossível. No Traefik
v3 o middleware é
ipAllowList; no v2 éipWhiteList(anterior à 2.11 só conhece esse nome, e um campo desconhecido derruba o file-provider inteiro). - A regra usa
PathRegexpno v3 ePath(/{p:…})no v2. - O arquivo é gerenciado: editar à mão não adianta, o implant reescreve quando o conteúdo diverge.
Lista de bloqueio própria (v2.56.0+)
Além da lista embutida, o operador pode bloquear caminhos próprios. As
entradas ficam em [traefik_edge] do config.toml e entram na mesma guarda
(mesmo 403), no Traefik 2 e no 3:
[traefik_edge]
bloquear = ["/backup.sql", "/config/prod.ini", "/privado/", "dump.sql", "exports/"]Cada entrada é literal (sem curingas nem regex) e diferencia maiúsculas de minúsculas. Barra inicial: a partir da raiz. Sem barra inicial: a partir de qualquer pasta. Barra final: a pasta e tudo dentro dela.
| Entrada | Bloqueia | Não bloqueia |
|---|---|---|
/backup.sql |
/backup.sql |
/x/backup.sql, /backup.sql.gz |
/config/prod.ini |
/config/prod.ini |
/a/config/prod.ini |
/privado/ |
/privado, /privado/, /privado/qualquer/coisa |
/privados, /a/privado/x |
dump.sql |
/dump.sql, /a/b/dump.sql |
/meudump.sql |
exports/ |
/exports, /exports/x, /a/exports/x |
/myexports/x |
a/b.txt |
/a/b.txt, /x/a/b.txt |
/xa/b.txt |
Pelas flags (repetíveis, exigem --configure), que gravam no config.toml
(preservando comentários e as outras seções; cria o arquivo, 0600, se não
existir) e regravam a guarda:
sudo implant traefik-edge --configure --bloquear /backup.sql --bloquear exports/
sudo implant traefik-edge --configure --desbloquear exports/
sudo implant traefik-edge --configure --bloquear dump.sql --dry-run # mostra o plano, não grava nada- Caracteres aceitos: letras e dígitos ASCII e
. _ ~ - @ + /. Recusados: entrada vazia,/sozinho (bloquearia o site inteiro),//, segmentos.e.., mais de 200 caracteres.%não é aceito: o Traefik compara o caminho já decodificado, então%20nunca casaria. - Entrada inválida dá erro e nada é gravado. Duplicatas são ignoradas.
--desbloquearcompara o texto exato e não valida, então também remove uma entrada inválida escrita à mão no arquivo.- Uma entrada acrescentada direto no
config.tomlvale no próximo--configure. - O audit lista as entradas próprias e, com a guarda escrita, testa uma delas
(tem que dar 403). No json:
bloquear_extra,bloquear_erroeprobe_extra. Esse probe não entra norisk.
Editar a lista (--bloquear/--desbloquear) não mexe no resto da guarda: o catch-all de hosts sem router que já estiver instalado continua. Só o --configure puro segue o --with-404.
Exemplos
# Auditar (padrão)
sudo implant traefik-edge
sudo implant traefik-edge -o json
# Ver o que seria escrito e aplicar
sudo implant traefik-edge --configure --dry-run
sudo implant traefik-edge --configure
# Com catch-all para hosts sem router (responde 403)
sudo implant traefik-edge --configure --with-404
# Só gerar o YAML para Traefik v2
implant traefik-edge --print --major 2
# Traefik sem file-provider: revisar o diff e aplicar
sudo implant traefik-edge --enable-file-provider
sudo implant traefik-edge --enable-file-provider --apply
# Conferir de fora
curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/.env # 403Saída JSON
Envelopes traefik.edge.audit, traefik.edge.configure e
traefik.edge.enable_provider.
- audit:
traefik_found,container,traefik_major,static_config,config_source(file|flags|env),provider_kind(directory|file),dynamic_dir/dynamic_file,file_provider_watch,sensitive_guard,catch_all_404,probe_status,recent_errors,risk. - configure:
path,changed,dry_run,with_404,traefik_major,backup,requires_restart,probe_status,restarted.
Como reverter
- Guarda: apague o
ccs-edge-sensitive.ymldo diretório do file-provider (o caminho exato sai no campopathdo--configure). O Traefik recarrega sozinho. --enable-file-provider: o apply guarda backup do compose (e dotraefik.yml, quando editado) antes de escrever; restaure-os e recrie o serviço.
Notas
- O probe precisa alcançar a porta 443 local; sem isso,
probe_status: nulle o veredito fica por arquivo, sem prova. - Para o auto-ban pontuar scanners que recebem esse 403, o access log do Traefik precisa registrar a resposta — ver traefik-accesslog.