Ingress e TLS automático
Como expor aplicações pela porta 443 com certificados emitidos e renovados automaticamente, sem operar um roteador externo.
O HeroCtl traz um roteador integrado embutido no plano de controle. Você não instala, não configura, não atualiza. Ao subir o cluster, o roteador já está ativo nas portas 80 e 443 de cada nó servidor.
Isso significa que expor uma aplicação na internet é uma linha no spec do job.
O caminho mais curto
job: minha-api
ingress:
host: api.exemplo.com
port: http
tls: true
Com isso o cluster faz três coisas em sequência:
- Resolve o host
api.exemplo.compara qualquer alocação saudável do job. - Solicita um certificado válido para esse domínio na primeira requisição.
- Renova esse certificado antes do vencimento, sem intervenção.
Você só precisa apontar o DNS de api.exemplo.com para os IPs dos nós servidores. O resto roda sozinho.
Como o certificado é emitido
O HeroCtl usa Let's Encrypt como autoridade certificadora padrão. Por trás disso há dois mecanismos de validação suportados.
HTTP-01 (default)
Funciona para qualquer domínio público que aponta para o cluster. Quando uma autoridade pede prova de posse, o roteador integrado responde no caminho /.well-known/acme-challenge/ automaticamente. Nada precisa ser configurado.
Atenção: HTTP-01 exige que a porta 80 esteja acessível externamente. Se o firewall bloqueia 80, a emissão falha.
DNS-01 (para wildcards)
Se você precisa de um certificado wildcard (*.exemplo.com), use DNS-01. Configure o provedor DNS no spec do cluster:
acme:
challenge: dns-01
provider: cloudflare
credentials:
api_token: ${secret.cloudflare_token}
Provedores suportados nativamente: Cloudflare, Route53, DigitalOcean, Hostinger, Hetzner. Outros vão por plugin externo.
Com DNS-01 ativo, basta declarar:
ingress:
host: "*.exemplo.com"
tls: true
Redirecionamento HTTP → HTTPS
Quando tls: true, todo tráfego que chega na porta 80 é redirecionado com 301 para 443. Você não precisa duplicar configuração.
Para forçar HSTS:
ingress:
host: api.exemplo.com
tls: true
hsts:
enabled: true
max_age: 31536000
include_subdomains: true
preload: false
Não habilite preload sem entender o compromisso. É difícil reverter.
Múltiplos domínios para o mesmo app
Padrão comum: www.exemplo.com e exemplo.com apontando para a mesma aplicação, com um sendo canônico.
ingress:
host: exemplo.com
redirect_from:
- www.exemplo.com
tls: true
Cada domínio em redirect_from recebe seu próprio certificado e responde 301 para o host canônico.
Roteamento por path
Mesmo host, aplicações diferentes em paths diferentes:
# job: site-marketing
ingress:
host: exemplo.com
path: /
tls: true
# job: api-publica
ingress:
host: exemplo.com
path: /api
tls: true
O roteador resolve a precedência por especificidade. /api/users cai em api-publica. /sobre cai em site-marketing.
Cabeçalhos customizados
Para adicionar ou remover headers na resposta:
ingress:
host: api.exemplo.com
tls: true
headers:
add:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin
remove:
- Server
- X-Powered-By
Rate limiting básico
Defesa em primeira linha contra abuso:
ingress:
host: api.exemplo.com
tls: true
rate_limit:
requests_per_second: 100
burst: 200
by: ip
Limites mais elaborados (por token, por endpoint, com bypass para parceiros) ficam na aplicação.
Troubleshooting
Certificado não foi emitido
Cheque três coisas, em ordem:
| Sintoma | Causa provável | O que fazer |
|---|---|---|
dial tcp: timeout no log do challenge | Porta 80 fechada | Liberar 80/tcp inbound nos nós servidores |
unauthorized: Invalid response | DNS aponta para outro lugar | dig +short api.exemplo.com deve retornar IP de nó servidor |
too many failed authorizations | Você bateu o rate limit do Let's Encrypt | Aguardar 1h e revisar a config antes de tentar de novo |
Inspecione o estado da emissão:
heroctl ingress cert-status api.exemplo.com
O certificado venceu sem renovar
Renovação roda 30 dias antes do vencimento. Se passou disso, há erro acumulado:
heroctl ingress cert-renew api.exemplo.com --force
Verifique também se redirect_from ainda aponta para o cluster. Domínio que foi movido para outro provedor sem ser removido do spec quebra a renovação silenciosamente.
ACME challenge intermitente
Sintoma: às vezes emite, às vezes falha. Geralmente é DNS round-robin com um IP que não responde na 80, ou um Cloudflare na frente em modo proxy. Para HTTP-01, o domínio precisa resolver direto para o cluster durante o desafio. Use DNS-01 se mantiver Cloudflare proxy ligado.