Cloudflare Security — Configuración de los 3 zones
Referencia técnica de la postura de seguridad en Cloudflare aplicada a los 3 dominios de Edu tras la sesión 57 (12-05-2026). Incluye inventario de zones, settings concretos aplicados, decisiones con su razonamiento, comandos
curlde verificación, y mini-runbook para replicar a un dominio nuevo.Versión coloquial para el equipo: [[workspace—guias—dominios-y-seguridad-cloudflare]]
Última actualización: 24-07-2026 (banner de deltas post-s57). Base: 12-05-2026 (sesión 57 cierre completo).
🔄 Actualización 24-07-2026 — qué ha cambiado desde s57 (el cuerpo de abajo documenta el estado de mayo; estos deltas MANDAN):
- Apex
crearack.comPROXIFICADO tras CF (s204, 07-07) — Transform Rule + Traefik trustedIPs; wiki [[concept—infra—cloudflare-proxy-apex]]. SSH a PROD ya NO va por el dominio: solo NetBird.- HSTS de crearack.com subido a 1 año + preload (s224, 15-07) —
max-age=31536000; includeSubDomains; preload(el envío a hstspreload.org sigue siendo decisión manual de Edu).always_use_httpsON.- DMARC en
p=rejecten crearack.com y channelassistance.com (21-07, s230) —ruaalogcrearack@esfericlabs.com+ TXT de autorización externa en esfericlabs.com.- Service Token CF Access rotado (s222-s223) — el secret vigente y sus 6 copias: playbook [[crearack-tech—guides—secret-rotation-playbook]] §9. Policy de
/api/mcp= Service Auth (un token en policy Bypass NO autentica — footgun s223). La mención a “1Password compartido” de abajo es de mayo; la decisión de gestor de credenciales sigue abierta (task #33).- App
DR Backup ServiceELIMINADA (s78) — su path quedó bajo la app principal.- channelassistance.com ya no está en PythonAnywhere — servidor propio Hetzner CX23
DCA(~06/2026).- esfericlabs.com ya no está parked — landing CreaRack EN VIVO desde el 10-07-2026 (el caveat 4.5 del cert de origin quedó resuelto).
- Firewalls Hetzner solo-NetBird (s222) — wiki [[concept—infra—hetzner-firewalls]] (sustituye a la vieja capa “tailnet”: Tailscale se retiró en s85 → NetBird).
- Workers Paid 5 $/mes en la cuenta desde 07-07 (workspace MCP/functions; el resto de zonas sigue Free).
1. Inventario de zones (actualizado 24-07-2026)
| Zone | Zone ID | Account ID | Tipo de origen | Plan |
|---|---|---|---|---|
crearack.com | b61ecf3f6372c9b928bfdfc39b8debac | ca1cf7d247ed0293b623b75e6d462bd9 | Hetzner CCX23 (Dokploy) — crearack-pro-zcmvsl-* · apex proxificado tras CF (s204) | Free |
channelassistance.com | ccc6ad4c9f5d579cbf5eda33820a588c | (mismo) | Servidor propio Hetzner CX23 DCA (antes PythonAnywhere) | Free |
esfericlabs.com | 55bb0ec67de292352518249c9b78d102 | (mismo) | Landing CreaRack EN VIVO (home v2 desde 10-07-2026) | Free |
Registrador: los 3 en Cloudflare Registrar → DNSSEC sincronizado al instante sin riesgo de desync de 48h.
2. Postura de seguridad — matriz consolidada
Estado base s57 con los deltas de julio anotados. ✅ = aplicado. ⚠ = aplicado con caveat documentado. — = no aplica.
| Setting | crearack.com | channelassistance.com | esfericlabs.com |
|---|---|---|---|
| SSL mode | strict | strict | strict (s57 PM via API) |
| TLS min version | 1.2 (s57 AM) | 1.2 (s57 AM) | 1.2 (s57 PM via API, antes 1.0) |
| TLS 1.3 | on (sin 0-RTT) | on (s57 PM via API, antes zrt) | on (sin 0-RTT) |
| Always Use HTTPS | on (reactivado s224 tras el paréntesis ACME del flip a proxy) | on (s57 AM) | on (s57 PM via API, antes off) |
| HSTS | 1 año + includeSubDomains + preload (s224; antes 6mo) | enabled, max_age=6mo, include_subdomains, nosniff (s57 AM) | enabled, max_age=1año, include_subdomains, nosniff (s57 PM via API, antes disabled) |
| Security Level | medium | medium (s57 PM via API, antes essentially_off) | medium |
| Bot Fight Mode | ⚠ off (deuda asumida — rompería cron Bibliotecario y Agent/MCP; reconfirmado s204: NO activar) | ⚠ off (mismo motivo) | on (s57 PM toggle manual Edu — revisar formularios de la landing, ver 4.2) |
| Block AI bots | on (s57 AM) | on (s57 AM) | on (s57 PM toggle manual Edu) |
| DNSSEC | on | on | on |
| DMARC TXT | p=reject (21-07-2026 · rua → logcrearack@) | p=reject (21-07-2026) | TXT de autorización externa <dominio>._report._dmarc (21-07) |
3. CF Access apps Critical (post-s57 · ver banner para rotaciones posteriores)
Account ID: ca1cf7d247ed0293b623b75e6d462bd9
| App | App ID | Domain | Policies |
|---|---|---|---|
| API Biblioteca | 841d6589-afd8-4418-9b83-a0ac78ae5172 | workspace.crearack.com/api/biblioteca/ | Service Token (Service Auth) + Staff Allow |
| Workspace MCP API | fc31f796-8505-4d82-a334-e42afe171d7b | workspace.crearack.com/api/mcp | Service Auth (desde s223 — antes Bypass, que NO autentica service tokens) + Staff Allow |
| — | workspace.crearack.com/api/maintenance/* | ELIMINADA en s78 (tumbaba el SSO de la app principal por especificidad de path; su path quedó bajo CreaRackSL Workspace) |
Service Token compartido (todo el equipo):
- Client ID:
c0cfca1ac691093a2c48945017d27ba4.access(el/rotatede s222 mantiene el client-id) - Client Secret: rotado s222-s223 — inventario de las 6 copias en [[crearack-tech—guides—secret-rotation-playbook]] §9
- Duration:
forever - Vive en CF Zero Trust → Access → Service Tokens → “Workspace API - Bibliotecario crons + MCP”
⚠ CF Access reusable policies NO se pueden eliminar via
DELETE /apps/{id}/policies/{id}(devuelvecan not delete reusable policies through this endpoint). Detach viaPUT /apps/{id}con el arraypoliciesSIN la policy. Footgun s57.
4. Decisions log
4.1 Bot Fight Mode OFF en crearack.com y channelassistance.com (deuda asumida)
Por qué: Bot Fight Mode básico bloquea User-Agents que parecen automatizados (curl/X.Y, python-requests/X.Y). El cron del Bibliotecario lanza queries con UA crearack-staging-bib-communities/1.0 y otros tipos, y CF respondía con challenge HTML (no JSON 403, sino una página HTML interstitial) → el cron interpretaba el body como respuesta válida pero el JSON parsing fallaba.
Decisión (s57 AM, reconfirmada s204 con el apex ya proxificado): NO activar Bot Fight Mode en las zonas activas — además del cron, rompería el Local Agent y el MCP. Bloqueo del cron/Agent > beneficio anti-bot básico. Block AI bots (también gratis) cubre el caso de scraping IA, que era la preocupación principal.
Alternativa futura: upgrade a Cloudflare Pro y usar Super Bot Fight Mode con WAF rules custom que whiteliste los UAs internos.
4.2 Bot Fight Mode ON en esfericlabs.com
Misma feature, decisión opuesta: cuando era parked no había nada que romper. La landing está EN VIVO desde el 10-07-2026 → si los formularios lead capture dan problemas, revisar aquí primero (desactivar o subir a Pro+Super).
4.3 TLS 1.3 sin 0-RTT en channelassistance.com (cambio del valor por defecto)
Estado por defecto en CF: zrt (TLS 1.3 con 0-RTT habilitado).
Cambio s57 PM: on (TLS 1.3 sin 0-RTT).
Por qué: 0-RTT (Zero Round Trip Time resumption) acelera reconexiones HTTPS pero abre ventana de replay attack en endpoints idempotentes. channelassistance es una app con login y formularios POST. La pérdida de latencia al desactivar 0-RTT es marginal (~50-100ms en clientes recurrentes) — el cambio es conservador correcto.
4.4 Security Level “Medium” en channelassistance.com (subido desde “Essentially Off”)
Estado pre-s57: essentially_off. Detectado por auditoría CSV interna. Sin razón aparente que lo justificara (no había crones de fuera contra esa zona). Subido a medium — el default recomendado por CF para apps web. Sin fricción a usuarios normales; solo challenges a IPs de baja reputación.
4.5 esfericlabs.com endurecido antes de tener tráfico
Estado pre-s57: SSL full (no strict), TLS min 1.0, Always HTTPS off, HSTS disabled.
Estado post-s57: todo blindado al máximo nivel.
Por qué endurecer un dominio parked: para que cuando se despliegue la landing comercial, no haya que recordar configurar cada item bajo presión de fecha de lanzamiento. Funcionó: la landing salió el 10-07-2026 heredando el hardening, sin errores 526 (sirve vía CF Pages con HTTPS válido).
5. Comandos de verificación
5.1 Estado actual de una zone — todos los settings críticos
$h = @{ Authorization = "Bearer $env:CF_API_TOKEN"; "Content-Type" = "application/json" }
$zone = "<zone-id>"
foreach ($s in @("ssl","min_tls_version","always_use_https","security_header","security_level","tls_1_3")) {
$r = Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/$s" -Headers $h
"$s = $($r.result.value | ConvertTo-Json -Compress -Depth 3)"
}
5.2 Verificar HSTS desde fuera (sin token)
curl.exe -I "https://crearack.com" | Select-String -Pattern "strict-transport-security"
# Esperado (desde s224): strict-transport-security: max-age=31536000; includeSubDomains; preload
5.3 Verificar TLS mínimo
# Probar TLS 1.0 (debe fallar)
curl.exe --tls-max 1.0 -v "https://crearack.com" 2>&1 | Select-String -Pattern "tls_protocol_version|handshake"
# Esperado: alguna referencia a tls_protocol_version not_supported / handshake failure
# Probar TLS 1.2 (debe funcionar)
curl.exe --tls-max 1.2 -v "https://crearack.com" 2>&1 | Select-String -Pattern "SSL connection|HTTP/"
# Esperado: SSL connection using TLSv1.2 + HTTP/2 200
5.4 Verificar DNSSEC propagación
nslookup -q=DNSKEY crearack.com 1.1.1.1
# Debe devolver al menos una DNSKEY con flags 256/257
# Alternativa con dig si está instalado:
dig +short DNSKEY crearack.com
5.5 Probar acceso a app CF Access (smoke test Service Token)
# Sin headers — debe dar 403 (tras eliminar bypass-everyone en s57)
curl.exe -s -o nul -w "%{http_code}" "https://workspace.crearack.com/api/mcp"
# Con Service Token — debe dar 200 (o método no permitido si es POST-only, pero NUNCA 403)
curl.exe -s -o nul -w "%{http_code}" `
-H "CF-Access-Client-Id: $env:CF_ACCESS_CLIENT_ID" `
-H "CF-Access-Client-Secret: $env:CF_ACCESS_CLIENT_SECRET" `
"https://workspace.crearack.com/api/mcp"
6. Endpoints API Cloudflare relevantes
| Acción | Endpoint | Método | Token scope |
|---|---|---|---|
| Listar zones de la cuenta | /accounts/{account_id}/... o /zones?per_page=50 | GET | Zone:Zone:Read |
| Leer setting de zone | /zones/{zone_id}/settings/{setting} | GET | Zone:Zone Settings:Read |
| Modificar setting de zone | /zones/{zone_id}/settings/{setting} | PATCH | Zone:Zone Settings:Edit |
| Estado DNSSEC | /zones/{zone_id}/dnssec | GET / PATCH | Zone:DNSSEC:Edit (scope dedicado, NO incluido en Zone Settings) |
| Bot Management | /zones/{zone_id}/bot_management | GET / PUT | Zone:Bot Management:Edit (scope dedicado) |
| Listar apps CF Access | /accounts/{account_id}/access/apps | GET | Access:Apps and Policies:Read |
| Modificar app CF Access | /accounts/{account_id}/access/apps/{app_id} | PUT | Access:Apps and Policies:Edit |
| WAF Managed Rules (Block AI bots) | /zones/{zone_id}/rulesets/... | GET / PATCH | Zone:Firewall Services:Edit |
⚠ Footgun s57: token con scope
Zone Settings:EditNO cubre Bot Management ni DNSSEC — scopes dedicados separados. Para hardening completo via API, crear token con los 3-4 scopes que apliquen. Desde julio existe el token permanenteCF_CLAUDE_TOKENpara los huecos del MCP CF (memoriareference_cf_claude_token_gaps— OJO: no cubre Access). Alternativa pragmática: panel manual (1 click por toggle).
7. Footguns aprendidos s57
7.1 Bot Fight Mode rompe crons con UA genérico
Bot Fight Mode básico activado en una zona donde corre un cron que llama via curl/python-requests/etc con UA por defecto → CF devuelve challenge HTML (no 403 JSON), el cron interpreta como respuesta válida pero el parsing JSON falla con Unexpected token <. Mitigación: NO activar en zonas con crons IPs no allowlistadas, o usar Super Bot Fight Mode (Pro+) con reglas custom whitelist.
7.2 claude.ai/connectors no permite headers HTTP custom
El sistema de connectors de Claude.ai web (OAuth) no soporta envío de headers HTTP custom al servidor MCP. Esto significa que un MCP detrás de CF Access NO funciona vía connector de Claude.ai web — solo vía .mcp.json local de Claude Code CLI. Limitación arquitectónica, no parametrizable.
7.3 claude.ai MCP connector toma precedencia sobre .mcp.json local
Si un MCP con el mismo slug está registrado AMBOS como connector en claude.ai web Y en .mcp.json local del repo, el de claude.ai gana. Para forzar el uso del local, eliminar el connector en claude.ai/settings/connectors y reiniciar Claude Code.
7.4 CF API token con resource filter vacío → 403 críptico
Al crear un token en CF, cada permission line tiene su propio “Apply to” (resource filter). Si una line se deja vacía, ese permiso no aplica a nada y los endpoints relacionados devuelven 10000 Authentication error. Rellenar Resources de CADA permission line.
7.5 CF Access reusable policies NO se pueden eliminar via DELETE
DELETE /accounts/{id}/access/apps/{app_id}/policies/{policy_id} devuelve can not delete reusable policies through this endpoint. Solución: detach via PUT /accounts/{id}/access/apps/{app_id} con el array policies SIN la policy a eliminar (también requiere pasar name, domain, type, self_hosted_domains, session_duration).
7.6 wiki_create_page y wiki_update_page schema laxo y silencioso (s57)
Síntoma: pasar parámetros a nivel root del tool call en lugar de anidados en front_matter produce páginas con front-matter mínimo, body vacío, y meta_updates: [] en la respuesta sin avisar del problema.
Schema correcto (functions/api/mcp/tools.ts):
wiki_create_page:{type, slug, title, front_matter: {status, owner, tags, sources, related, supersedes}, content: string, actor}wiki_update_page:{slug, patch: string | object, reason, actor}.patchstring reemplaza el body entero (incluido el front-matter — peligroso si se pasa un JSON serializado, lo escribe RAW).patchobject ={title, status, owner, tags, sources, related, last_verified, last_updated_by, supersedes, content}.
Footgun extra: pasar patch como JSON string crudo a wiki_update_page lo escribe literalmente al .md sin parsing, corrompiendo la página. Detectado al crear estas mismas páginas — la primera versión quedó en JSON crudo de una línea. Fix moderno: usar wiki_update_content (API s91 con body string explícito, la única vía que alinea D1+repo).
Además, sources debe ser {type, ref, last_seen}[], NO string[]. Si se pasan strings sueltos, el commit a GitHub OK pero CF Pages Deploy falla con InvalidContentEntryDataError y bloquea TODOS los deploys posteriores. Detectado en s56. Para arrays vacíos [] está OK.
8. Mini-runbook · replicar hardening a un dominio nuevo
Cuando se añada un cuarto dominio a la cuenta CF (ejemplo: una sub-brand nueva):
8.1 Pre-requisito · CF API token
Crear en https://dash.cloudflare.com/profile/api-tokens con scopes:
Zone:Zone Settings:Edit(para SSL/TLS/HSTS/Always HTTPS/Security Level/TLS 1.3)Zone:DNSSEC:Edit(para DNSSEC)Zone:Bot Management:Edit(para Bot Fight Mode)Zone:Firewall Services:Edit(para Block AI bots vía rulesets)
Resources: Include All zones from <cuenta>. TTL: 1h (auto-revoca). (O usar el permanente CF_CLAUDE_TOKEN si sus scopes cubren.)
8.2 Settings vía API
$h = @{ Authorization = "Bearer $env:CF_API_TOKEN"; "Content-Type" = "application/json" }
$zone = "<nuevo-zone-id>" # obtener con GET /zones
# SSL Full Strict
Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/ssl" -Method PATCH -Headers $h -Body '{"value":"strict"}'
# TLS min 1.2
Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/min_tls_version" -Method PATCH -Headers $h -Body '{"value":"1.2"}'
# Always HTTPS on
Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/always_use_https" -Method PATCH -Headers $h -Body '{"value":"on"}'
# TLS 1.3 sin 0-RTT
Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/tls_1_3" -Method PATCH -Headers $h -Body '{"value":"on"}'
# Security Level medium
Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/security_level" -Method PATCH -Headers $h -Body '{"value":"medium"}'
# HSTS (max_age 1 año, include_subdomains, nosniff)
$hsts = @{
value = @{
strict_transport_security = @{
enabled = $true
max_age = 31536000
include_subdomains = $true
preload = $false
nosniff = $true
}
}
} | ConvertTo-Json -Depth 5 -Compress
Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$zone/settings/security_header" -Method PATCH -Headers $h -Body $hsts
8.3 Settings manuales (panel CF)
- Block AI bots: panel CF → dominio → Security → Bots → “Block AI Scrapers and Crawlers” → Block
- Bot Fight Mode: panel CF → Security → Bots → “Bot Fight Mode” → On (¡cuidado si hay crones/Agent/MCP llamando a la zone! — en crearack.com está OFF a propósito)
- DNSSEC: panel CF → DNS → Settings → DNSSEC → Enable. Si el registrador es CF Registrar, sincronización automática inmediata.
- DMARC (si el dominio envía email): añadir TXT record en root con
v=DMARC1; p=none; rua=mailto:logcrearack@esfericlabs.comy subir ap=rejecttras 1-2 meses de telemetría limpia (así se hizo con crearack/channelassistance el 21-07-2026).
8.4 Verificación post-hardening
Ejecutar sección 5 de esta página contra el nuevo dominio. Esperar 24h para confirmar DNSSEC propagado.
8.5 Documentación
- Añadir el dominio a esta página (sección 1 — Inventario de zones + sección 2 — Matriz).
- Actualizar memoria
reference_cf_zones_edu.mdcon el nuevo dominio. - Si el dominio expone APIs internas, crear CF Access app con policies Service Auth (nunca Bypass para service tokens — footgun s223) + Staff Allow.
9. Pendientes / verificaciones diferidas
- ✅
DNSSEC propagación— activo en los 3 dominios. - Bot Fight Mode esfericlabs con la landing YA en vivo (10-07): si los formularios lead capture fallan, revisar aquí primero.
- ✅
DMARC enforcement—p=rejecten ambos dominios desde el 21-07-2026 (s230, tras telemetría limpia de julio). - ✅
API Token CF temporal— caducado por TTL (19-05-2026); sustituido por el permanentecfut_OPIEiN...CF_CLAUDE_TOKEN. - ✅
Documentar DR Backup Service app id— la app se eliminó en s78 (ver sección 3). - CSP de DCA (channelassistance): pendiente — requiere inventariar los recursos de la app. Cabeceras base ya puestas.
Véase también
- [[workspace—guias—dominios-y-seguridad-cloudflare]] — versión coloquial para el equipo
- [[concept—infra—hetzner-firewalls]] — firewalls solo-NetBird (s222; sustituye a la capa “tailnet” retirada)
- [[concept—infra—cloudflare-proxy-apex]] — apex proxificado (s204)
- [[crearack-tech—admin—dokploy-internals]] — internals Dokploy en PROD
- [[crearack-tech—guides—secret-rotation-playbook]] — rotación de secrets (§9 = Service Token CF Access)
- [[crearack-tech—guides—disaster-recovery]] — DR del workspace