Ingress y TLS automático
Cómo exponer aplicaciones por el puerto 443 con certificados emitidos y renovados automáticamente, sin operar un router externo.
HeroCtl trae un router integrado embebido en el plano de control. No lo instalas, no lo configuras, no lo actualizas. Al levantar el cluster, el router ya está activo en los puertos 80 y 443 de cada nodo servidor.
Eso significa que exponer una aplicación en internet es una línea en el spec del job.
El camino más corto
job: minha-api
ingress:
host: api.exemplo.com
port: http
tls: true
Con eso el cluster hace tres cosas en secuencia:
- Resuelve el host
api.exemplo.coma cualquier alocación saludable del job. - Solicita un certificado válido para ese dominio en la primera request.
- Renueva ese certificado antes del vencimiento, sin intervención.
Solo necesitas apuntar el DNS de api.exemplo.com a las IPs de los nodos servidores. El resto corre solo.
Cómo se emite el certificado
HeroCtl usa Let's Encrypt como autoridad certificadora por defecto. Detrás de eso hay dos mecanismos de validación soportados.
HTTP-01 (default)
Funciona para cualquier dominio público que apunta al cluster. Cuando una autoridad pide prueba de posesión, el router integrado responde en la ruta /.well-known/acme-challenge/ automáticamente. Nada necesita ser configurado.
Atención: HTTP-01 exige que el puerto 80 esté accesible externamente. Si el firewall bloquea el 80, la emisión falla.
DNS-01 (para wildcards)
Si necesitas un certificado wildcard (*.exemplo.com), usa DNS-01. Configura el proveedor DNS en el spec del cluster:
acme:
challenge: dns-01
provider: cloudflare
credentials:
api_token: ${secret.cloudflare_token}
Proveedores soportados nativamente: Cloudflare, Route53, DigitalOcean, Hostinger, Hetzner. Otros van por plugin externo.
Con DNS-01 activo, basta declarar:
ingress:
host: "*.exemplo.com"
tls: true
Redirección HTTP → HTTPS
Cuando tls: true, todo tráfico que llega al puerto 80 es redirigido con 301 al 443. No necesitas duplicar configuración.
Para forzar HSTS:
ingress:
host: api.exemplo.com
tls: true
hsts:
enabled: true
max_age: 31536000
include_subdomains: true
preload: false
No habilites preload sin entender el compromiso. Es difícil revertir.
Múltiples dominios para la misma app
Patrón común: www.exemplo.com y exemplo.com apuntando a la misma aplicación, con uno siendo canónico.
ingress:
host: exemplo.com
redirect_from:
- www.exemplo.com
tls: true
Cada dominio en redirect_from recibe su propio certificado y responde 301 al host canónico.
Ruteo por path
Mismo host, aplicaciones diferentes en paths diferentes:
# job: site-marketing
ingress:
host: exemplo.com
path: /
tls: true
# job: api-publica
ingress:
host: exemplo.com
path: /api
tls: true
El router resuelve la precedencia por especificidad. /api/users cae en api-publica. /sobre cae en site-marketing.
Cabeceras customizadas
Para agregar o remover headers en la respuesta:
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
Defensa en primera línea contra abuso:
ingress:
host: api.exemplo.com
tls: true
rate_limit:
requests_per_second: 100
burst: 200
by: ip
Límites más elaborados (por token, por endpoint, con bypass para partners) quedan en la aplicación.
Troubleshooting
El certificado no fue emitido
Chequea tres cosas, en orden:
| Síntoma | Causa probable | Qué hacer |
|---|---|---|
dial tcp: timeout en el log del challenge | Puerto 80 cerrado | Liberar 80/tcp inbound en los nodos servidores |
unauthorized: Invalid response | DNS apunta a otro lugar | dig +short api.exemplo.com debe retornar IP de nodo servidor |
too many failed authorizations | Pegaste el rate limit de Let's Encrypt | Aguardar 1h y revisar la config antes de intentar de nuevo |
Inspecciona el estado de la emisión:
heroctl ingress cert-status api.exemplo.com
El certificado venció sin renovar
La renovación corre 30 días antes del vencimiento. Si pasó de eso, hay error acumulado:
heroctl ingress cert-renew api.exemplo.com --force
Verifica también si redirect_from aún apunta al cluster. Un dominio que fue movido a otro proveedor sin ser removido del spec rompe la renovación silenciosamente.
ACME challenge intermitente
Síntoma: a veces emite, a veces falla. Generalmente es DNS round-robin con una IP que no responde en 80, o un Cloudflare al frente en modo proxy. Para HTTP-01, el dominio debe resolver directo al cluster durante el desafío. Usa DNS-01 si mantienes Cloudflare proxy activado.