Skip to content

ADR-0020: Wildcard TLS DNS-01 para *.d.wetopi.com en el edge

Date: 2026-09-10 · Status: Accepted (2026-09-10 — GO del operador + ejecución completa verificada: staging gate, prod Ready, tofu DNS, Gateway apply; ver SVC-DEV-WILDCARD) Related: ADR-0006 (edge v1), ADR-0019 (D-tls offloading), OBJ-EDGE-DWETOPI-001

Context

El cluster d (RKE2 wetopi3) será el sandbox donde se despliegan wepods de desarrollo. El operador requiere exponerlos en https://<wepod>.d.wetopi.com (muchos subdominios, los de desarrollo de wetopi) con optimización del tiempo de despliegue.

Estado live verificado 2026-09-10:

  • Edge v1 (ADR-0006, accepted): Traefik hostNetwork en rke-wan01/02, Gateway kube-system/wetopi-edge (Accepted+Programmed), listeners web:8000/https:8443, nftables REDIRECT 80/443→8000/8443, 11 certRefs LE PROD en producción (argo/docs/vault/infer/memory/grafana/prometheus/ alertmanager/dashboards/otlp). https://docs.wetopi.net sirve LE prod verificado desde el harness.
  • cert-manager v1.21.1 + ClusterIssuers letsencrypt-staging/prod con solver DNS-01 Cloudflare: patrón probado por 6+ certificados.
  • DNS: wetopi.com ya vive en Cloudflare (NS propio). d.wetopi.com hoy NXDOMAIN (sin zona/registro). El token Cloudflare existente (cloudflare-dns-api-token) está scoping a la zona wetopi.net (comentario del ClusterIssuer: "Zone-DNS-Edit AND Zone-Zone-Read on the wetopi.net zone") — NO cubre wetopi.com.
  • ADR-0019 §D-tls (DECIDIDO): TLS offloading en Traefik, HTTP interno hasta el wepod; re-encrypt por-pod rechazado.

El problema del cert por servicio: cada wepod nuevo requeriría (1) certificado LE por nombre (~1-2 min + riesgo de rate limit), (2) certRef en el Gateway con apply manual (el Secret ausente tumba el listener completo — fail-safe order), (3) DNS A record por subdominio en tofu. Con "muchos subdominios de desarrollo" ese coste se multiplica y el despliegue deja de ser ágil: choca con el requisito de velocidad.

Decision

  1. Un certificado wildcard LE PROD dev-wildcard-tls (kube-system): dnsNames: ["*.d.wetopi.com"], issuer letsencrypt-prod, vía DNS-01. El wildcard cubre demo-joan.d.wetopi.com y cualquier subdominio futuro de d.wetopi.com SIN nueva emisión.
  2. Emisión staging-first: el wildcard se emite primero con letsencrypt-staging (valida token nuevo + solver en la zona wetopi.com), gate explícito, y solo entonces se corta a prod. (Protege el rate budget: LE prod permite 50 certs/domain/week y 5 duplicados/7d — un wildcard mal formado quema intentos.)
  3. DNS wildcard en la zona wetopi.com (variante por defecto, D3bis variante A): registros *.d A 79.137.114.193/.194 + d A .193/.194 (4 records, grey-cloud, TTL 300, tofu-owned en cloudflare_dns_records). Cualquier wepod.d.wetopi.com resuelve desde el minuto uno; no se vuelve a tocar DNS para wepods. Variante B (a decisión del operador): subdominios explícitos por wepod en tfvars — más conservador, cada wepod nuevo toca tofu.
  4. Token Cloudflare: RESUELTO por el operador 2026-09-10 — el token existente (cloudflare-dns-api-token, zona wetopi.net) fue AMPLIADO para cubrir también la zona wetopi.com. Consecuencias: el MISMO Secret k8s (cloudflare-dns-api-token key api-token, ns cert-manager) sirve a los solvers DNS-01 de ambas zonas; NO se crean Secrets nuevos ni issuers clonados; los ClusterIssuers letsencrypt-staging/prod existentes sirven el wildcard sin cambios. Verificación del permiso ampliado: la emisión STAGING del wildcard es el test (un token sin DNS-Edit en wetopi.com hace fallar el solver con zone-discovery — sin coste de rate-limit prod). Si el valor del token hubiese ROTADO (token nuevo en vez de ampliación), rotar el Secret cloudflare-dns-api-token desde el env 0600 de ops01 antes de emitir.
  5. El certRef dev-wildcard-tls entra al Gateway con el fail-safe order obligatorio (Certificate Ready → apply Gateway; un Secret ausente tumba el listener completo con los 10 co-tenants).
  6. Routing: namespace wepods-d + HTTPRoute dev-catchall con hostnames: ["*.d.wetopi.com"] → backend default 404 + Services por wepod añadidos por PR (ver OBJ D4). Añadir un wepod = 1 PR al repo del servicio, cero toques en kube-system/edge/cert-manager. MECANISMO MÍNIMO (fall-back probado): el mecanismo de onboarding primario NO depende del catch-all — cada wepod declara su HTTPRoute con hostname EXACTO (wepod.d.wetopi.com) en su propio namespace (patrón docs-site/argo ya live: parentRef sectionName https, allowedRoutes from:All). Esa variante es exact-hostname matching, el camino estándar que ya sirve 13 rutas en producción. El catch-all wildcard-hostname es una mejora opcional cuyo soporte (Traefik findMatchingHostname sobre route hostnames) se valida en build; si Traefik no lo soporta, se despliega la HTTPRoute default-404 con matches path catch-all SIN hostname propio — que captura solo lo que ninguna route exacta matchee no es posible en Gateway API (route-less hosts caen a 404 del router, comportamiento por defecto del provider), así que el 404 default se implementa con la route catch-all O se acepta el comportamiento por defecto del proxy (404/503 nativo). Decisión de build, no bloquea el onboarding.

Alternatives considered

  • Cert por subdominio (patrón infer-tls por wepod): rechazado como mecanismo primario — el tiempo de despliegue por wepod (emisión + certRef
  • apply del Gateway) es exactamente lo que el operador pidió evitar; con muchos subdominios quema rate limits. Queda como fallback si el wildcard se bloquea.
  • Ampliar el token wetopi.net a wetopi.com: viable y más simple; el operador decide. ~~Por defecto se propone token dedicado (separación dev/ plataforma).~~ DECIDIDO por el operador 2026-09-10: token ampliado a ambas zonas (misma referencia Secret por nombre; sin piezas nuevas).
  • cert-manager con issuer propio por wepod (namespace-scoped Issuer): rechazado — multiplica cuentas/emisiones y no mejora el tiempo.
  • Traefik default cert wildcard para todo el edge: rechazado — el wildcard dev no debe cubrir *.wetopi.net (blast radius).
  • Cloudflare proxied (orange-cloud) para ocultar los edges: rechazado — rompería el patrón grey-cloud probado (TTL 300, round-robin sin proxy) y añade una capa (WAF CF) fuera del scope; los A records grey-cloud son el patrón de todos los servicios v1.

Consequences

  • Positivo: despliegue de wepod = crear Service + entry HTTPRoute (o nueva route en el ns del wepod) — minutos, sin cert-manager ni edge applies; un solo cert que renovar; DNS configurado una vez para siempre; coherente con ADR-0019 (offloading, HTTP interno al wepod).
  • Negativo: (a) el wildcard da validez TLS a cualquier subdominio presente en DNS — mitigado por el catch-all 404 default + el carácter dev del dominio; (b) requiere un token Cloudflare adicional (una pieza operator + vault); (c) wildcard en LE prod exige gate staging-first (una emisión staging antes de prod); (d) el hostname wildcard en HTTPRoute requiere Gateway API ≥1.0 — el cluster corre Traefik 3.7.8 con provider Gateway y CRDs v1 (verificado en gateway-wetopi-edge.yaml), compatible.
  • Neutral: la disponibilidad hereda el modelo ADR-0006 (round-robin 2 A records, sin L4 health-check, remediation operador). Los wepods dev NO son clientes externos: la condición dura del ADR-0006 (v2 antes de hosting externo) NO cambia.

Verification / implementation notes

  • Orden fail-safe (obligatorio, verificado en 10 certRefs previos):
  • Token Cloudflare wetopi.com en vault + Secret en ns cert-manager.
  • Certificate dev-wildcard-tls-staging (letsencrypt-staging) Ready.
  • Gate operador: revisión del chain staging (fake LE).
  • Certificate prod (letsencrypt-prod) Ready → Secret dev-wildcard-tls.
  • Gateway certRef (platform-edge PR) + apply desde ops01.
  • DNS records ×4 (tofu tfvars + apply operador, grey-cloud TTL 300).
  • HTTPRoute catch-all + Services por wepod (GitOps).
  • Probes públicos (SAN wildcard, 404 default, co-tenant regression).
  • Rollback: revert del certRef → listener cae al resto de certs; Secret queda (cert-manager sigue renovando). DNS wildcard: tofu revert.