CreaRack-SL

Cloudflare delante del apex crearack.com — topología, IP real y TLS

Conceptoactivecreado Tue Jul 07#infra#cloudflare#seguridad#tls#prod

Cloudflare delante del apex crearack.com — topología, IP real y TLS

Ejecutado y verificado el 07-07-2026 (sesión Security Center). Origen: insights a_record_not_proxied + a_record_dangling (el apex entraba directo al Hetzner sin WAF/anti-DDoS y con la IP de origen expuesta). Código asociado: PR #286 (v1.45.13).

Topología (después del cambio)

Visitante → Cloudflare edge (proxy naranja, Full strict, WAF/DDoS)
          → Traefik (Dokploy, Hetzner PROD 116.203.31.166)
          → Daphne --proxy-headers → Django
  • Registro A del apex crearack.com → proxied (naranja) desde 07-07-2026. www ya lo estaba (Worker www-redirect-crearack responde 301 en el edge).
  • La zone estaba ya en SSL Full (strict); el origen sirve un cert Let’s Encrypt para crearack.com renovado por Traefik (desde s222 por dnsChallenge, ver abajo).

Las 3 piezas que sostienen la IP real (NO tocar sin leer esto)

Daphne corre con --proxy-headers y resuelve la IP del cliente desde el PRIMER valor de X-Forwarded-For (verificado en el source de daphne 4.2.1). Con un proxy más en la cadena, ese primer valor sería falseable por el cliente — reabriría el bypass de /admin cerrado en s104, una capa por debajo del middleware. Lo impiden:

  1. Transform Rule en la zone crearack.com (ruleset 552d96c5d98d4fcaa58dcd699cbff3ac, phase http_request_late_transform, regla f44fbd6d...): elimina X-Forwarded-For en el edge. CF prohíbe sobrescribir ese header, pero al eliminarlo su proxy lo re-añade con solo la IP real del visitante (documentado por CF). Resultado: el XFF que llega al origen es "visitante" — sin basura del cliente.
  2. Traefik (/etc/dokploy/traefik/traefik.yml, PROD): entryPoints.web/websecure.forwardedHeaders.trustedIPs = los 22 rangos publicados de CF (www.cloudflare.com/ips). Sin esto Traefik descartaría el XFF veraz de CF. Traefik añade el peer al final → Django ve "visitante, cf-edge" y Daphne coge el primero ✓. Backup previo: traefik.yml.bak-20260707. Conexiones directas (no-CF) siguen como antes: XFF del cliente descartado.
  3. Código (v1.45.13, PR #286): core/auth_api.py::_get_client_ip coge XFF[0] (cogía el último → habría logueado la IP del edge). admin_paths/rate_limit/signals/portal ya eran correctos.

Always Use HTTPS = ON (desde 15-07-2026 · s224)

  • Estado actual: always_use_https ON en la zone → 301 http→https en todo el edge, sin excepciones. La Redirect Rule custom que antes hacía ese trabajo (ruleset 3f9451bc, phase http_request_dynamic_redirect, regla 7366cc7e) se borró por redundante (0 reglas en el ruleset).
  • Por qué ahora se puede: hasta s222 estaba OFF a propósito, porque encendido rompía el reto HTTP-01 de Let’s Encrypt por el :80 (se redirigía a HTTPS antes de llegar al responder ACME de Traefik). En s222 PROD migró a renovación DNS-01 (Traefik dnsChallenge, provider cloudflare) y se cerró el :80 — HTTP-01 y su excepción /.well-known/ dejaron de usarse, así que la excepción ya no protege nada.
  • Verificado E2E (15-07): http://crearack.com/ → 301 → https ✓ · http://crearack.com/.well-known/test → 301 → https ✓ (la excepción ya no aplica, correcto) · http://www.crearack.com/ → 301 https ✓ (el Worker www-redirect sigue OK) · https://crearack.com/ → 302 /login (web viva) ✓.
  • El apex además redirige en el origen (308 de Traefik) — doble red.

Verificación E2E del 07-07 (todo en verde)

login 200 vía edge MAD · /admin 302 desde IP del equipo (la guardia de IPs funciona a través de la cadena) · IP real del visitante en logs de Daphne (93.176.x, no la del edge) · WSCONNECT del Agente local por websocket · security.txt del apex 200.

Barrido de superficie externa (12-07-2026 · s219) — F2 confirmado y :443 cerrado

Barrido pasivo con nuestras herramientas (curl/dig/TcpClient/SSH), no con herramienta ofensiva. Veredicto: superficie muy sana. OK: CSP estricta con nonces, X-Frame DENY, nosniff, Referrer/Permissions-Policy, COOP, TRACE→405, puerto 80→301, excepción ACME viva, security.txt en los 3 dominios, cert de origen con SAN solo crearack.com (no filtra internos), DNS sin subdominios candidatos (api/app/stage/dev/vpn/mail → NXDOMAIN), IP de origen ausente del DNS, workspace+esfericlabs tras CF Access.

  • Scan TCP de 116.203.31.166: OPEN solo 22/80/443/3000; los 27 restantes (Postgres 5432, Valkey 6379, Docker API 2375/6, RDP 3389, mirror 8090, agent 5050…) FILTERED. El firewall base de Hetzner es sólido.
  • F2 CONFIRMADO explotable → MITIGADO (Fase 2a): el origen respondía el mismo 302 → /login de la app en directo saltándose CF. Cerrado el :443 directo a solo-CF el 12-07 (ver sección F2). Se mantiene: rate-limit (300) de la app y cabeceras de Django.
  • ✅ HSTS subido a 1 año + preload (15-07 · s224): el origen (Django) ya emitía max-age=31536000; includeSubDomains; preload, pero CF lo recortaba a 15768000 (6 meses) sin preload. Igualado en la zona (security_header) → vía CF ahora llega max-age=31536000; includeSubDomains; preload (verificado). (El envío a hstspreload.org = decisión manual de Edu, no hecho.)
  • Higiene menor: api.insecure: true en Traefik (dashboard sin auth) — no expuesto (puerto filtrado), default de Dokploy. En acme.json entrada residual Email: test@localhost.com. Cosméticos.

F2 · cerrar el origen a solo-Cloudflare

✅ Fase 2a EJECUTADA (12-07-2026 · s219) — :443 cerrado

El firewall-CreaRack (Hetzner Cloud, ID 10679994) es COMPARTIDO por 4 servers (prod/staging/DCA/ops) y tenía 80/443 en 0.0.0.0/0. staging y DCA sirven :443 directo (no están tras CF), así que editar la regla compartida los habría roto. Solución: se creó firewall-crearack-prod (ID 11295929) solo para crearack-prod (server 121377067), replicando exactas las reglas (SSH 4 IPs, :80 abierto, ICMP, Dokploy :3000) salvo :443 restringido a los 22 rangos CF; se aplicó a prod y se quitó prod del firewall compartido (los otros 3 siguen igual). Verificado: :443 directo al origen = timeout · SSH ok · :80 directo = 301 · sitio vía CF = 302 vivo. Rollback: re-aplicar firewall-CreaRack a crearack-prod.

✅ Fase 1+2b HECHA (14-07-2026 · s222) — :80 cerrado con DNS-01

Traefik migrado de httpChallenge a dnsChallenge provider cloudflare (emisión DNS-01 real probada) → :80 cerrado en firewall-crearack-prod (SSL mode CF = strict). Con HTTP-01 fuera de juego, la excepción ACME /.well-known/ dejó de hacer falta, lo que habilitó poner always_use_https ON el 15-07 (sección arriba). Detalle: [[project_external_surface_audit_f2]] + concept--infra--hetzner-firewalls.

Otros pendientes de la sesión Security Center

  • Vigilar la 1ª renovación LE tras el proxy (~16-08-2026) — task #185.
  • ✅ HSTS: subido a 1 año + preload en CF el 15-07 (s224, ver arriba).
  • DMARC p=none de channelassistance.com (Low, tema aparte).

Rollback (segundos)

  1. Registro A crearack.com → gris (proxied: false).
  2. TLS/HTTPS: hoy always_use_https = on (sin Redirect Rule). Para volver al esquema viejo (OFF + Redirect Rule con excepción /.well-known/) solo haría falta si se revirtiera DNS-01 → httpChallenge; no es el caso.
  3. (Solo si molestan) Transform Rule y trustedIPs pueden quedarse: son inertes sin tráfico CF→origen.
  4. Firewall F2: re-aplicar firewall-CreaRack a crearack-prod reabre :443 directo.

Límites conocidos (zone en plan Free)

  • Subidas máx 100 MB/request vía apex (no tenemos uploads mayores).
  • Requests >100 s cortadas por el edge (el SSE del Help emite continuo, no le afecta).
  • La cuenta tiene Workers Paid (5 $/mes, para la CPU del Worker del workspace) — eso NO cambia los límites del proxy de la zone.

Token de gestión

Las piezas CF de esta página se gestionan con el token claude-cf-gaps (env var CF_CLAUDE_TOKEN en el perfil de Edu): Security Center Insights, Rulesets/Transform, Zone Settings, Page Rules y Redirección única (Single Redirects, añadido 07-07). El MCP de Cloudflare NO llega a esos scopes (lista OAuth fija). El firewall Hetzner se gestiona con un token HCLOUD_TOKEN de la Hetzner Cloud Console (usar y revocar).

Véase también

  • [[concept—infra—ops-server]]