Cloudflare Access — proteger un hostname con login
Cloudflare Access (parte de Zero Trust) pone una pantalla de login delante de un hostname, a nivel del edge de Cloudflare — antes de que el tráfico llegue al origen. Por eso no depende de cómo esté expuesto el servicio: funciona igual sobre un hostname servido por Cloudflare Pages (como la wiki), por un túnel cloudflared, o por un simple registro DNS proxied — mientras el hostname esté proxied por Cloudflare (nube naranja), Access lo puede cubrir. No hay Terraform/Pulumi para esto en esta infraestructura; toda la config se hace por API directo con curl, no hay paso manual en el dashboard salvo para revisar.
Modelo: Application + Policy
Cada hostname protegido es una Access Application (self_hosted para sitios web normales). Cada Application referencia una o más Access Policies, que son la regla de “quién entra” (login method, emails permitidos, IPs, etc.).
Las policies pueden ser reusable ("reusable": true) — viven a nivel de cuenta, no de application, y una misma policy se referencia por id desde varias Applications sin duplicar la regla. Esto es clave: si ya existe una policy reusable que hace lo que necesitas (ej. “solo esta cuenta de Google”), no se crea una nueva — se referencia la existente por id al crear la Application nueva.
Policy en uso: google project-vm-personal
La policy reusable 070a3c92-5aa8-4bb3-8b1f-f250ffe1526e (“google project-vm-personal”) permite login solo con Google, restringido al email ehalsou@gmail.com. Es la policy por default para proteger cualquier servicio personal nuevo — a menos que el caso pida explícitamente otra regla (otro email, IP, service token, etc.). Al momento de escribir esto la usan:
hub(hub.estebanalcocer.cloud)sr250 ssh(sr250.estebanalcocer.cloud)wiki(wiki.estebanalcocer.cloud) — agregada 2026-08-04, primer caso de reuso explícito de esta policy para un hostname nuevo.homepage(ehas.uk, apex sin subdominio) — agregada 2026-08-26, ver Homepage. Primer caso de una Access Application sobre el apex de una zona en vez de un subdominio; funciona igual que cualquier otro hostname, eldomainde la Application simplemente es la zona completa.trivasa (ehas.uk)(trivasa.ehas.uk) — agregada 2026-08-31. El sitio (proyecto Pagestrivasa-context-wiki) antes se servía en paralelo por dos custom domains —trivasa.estebanalcocer.cloud(con su propia Access Application, ya vieja) ytrivasa.ehas.uk(sin Access). Al proteger el segundo con esta policy se decidió eliminar el primero por completo en vez de dejar dos hostnames sirviendo lo mismo: se quitó el custom domain del proyecto Pages, se borró el DNS record (CNAMEen la zonaestebanalcocer.cloud) y se borró la Access Application vieja (trivasa, apuntaba atrivasa.estebanalcocer.cloud).trivasa.estebanalcocer.cloudahora responde530(sin ruta) — el sitio vive solo entrivasa.ehas.uk.
Cómo agregar Access a un servicio nuevo
El token de API con permiso de Access/Policies/DNS/Zones vive en Infisical, secreto CLAUDE_CODE_APPS_AND_POLICIES_DNS_ZONES.
CF_API_TOKEN=$(curl -s "http://127.0.0.1:8082/api/v3/secrets/raw/CLAUDE_CODE_APPS_AND_POLICIES_DNS_ZONES?workspaceId=2aefdbd1-389c-4fd0-bdb8-a5621af8aac1&environment=prod&secretPath=/" \
-H "Authorization: Bearer $INFISICAL_TOKEN" | jq -r '.secret.secretValue')- Resolver el account ID.
GET /accountscon este token devuelveresult: [](el token no tiene el scope de cuenta necesario para listar cuentas) — hay que sacar elaccount.iddel endpoint de zona en vez de eso:curl -s -H "Authorization: Bearer $CF_API_TOKEN" \ "https://api.cloudflare.com/client/v4/zones?name=estebanalcocer.cloud" | jq '.result[0].account.id' - Confirmar la policy reusable a usar.
GET /accounts/$ACCOUNT_ID/access/policies— buscar porname(ej. “google project-vm-personal”) y tomar suid. Revisar tambiénGET /accounts/$ACCOUNT_ID/access/appspara confirmar que no exista ya una Application para ese hostname (evitar duplicados). - Crear la Application, referenciando la policy existente por
id(no se manda el bloque completo de la policy, solo elidcomo string en el arraypolicies):curl -s -X POST -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" \ "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps" \ -d '{ "name": "<nombre-servicio>", "domain": "<hostname>", "type": "self_hosted", "session_duration": "24h", "auto_redirect_to_identity": true, "app_launcher_visible": true, "allowed_idps": ["11c6af1c-7daa-4a44-b8ce-f80dbe32ed84"], "policies": ["<policy_id>"] }'allowed_idpscon ese id restringe el login a Google (mismo IdP que usanhubysr250 ssh);auto_redirect_to_identity: truesalta la pantalla intermedia de Cloudflare y manda directo al login de Google. - Verificar sin sesión — un request nuevo debe redirigir al login, no servir el contenido:
curl -sI https://<hostname>/ | grep -i location # location: https://estebanalcocer.cloudflareaccess.com/cdn-cgi/access/login/<hostname>?...
Llamadas a la API de Access bloqueadas por el permission classifier de Claude Code
Al intentar proteger secrets.estebanalcocer.cloud (Infisical) el 2026-08-05, tanto un script en Python como llamadas sueltas con curl contra cualquier endpoint bajo /access/ (incluso un GET de solo lectura a /access/policies) fueron bloqueadas por el clasificador de permisos del harness — a diferencia de las llamadas a /zones/.../dns_records, que sí se ejecutan sin fricción. Parece un guardrail deliberado específico de la superficie de Access (perímetro de autenticación), no un bug. El intento se abandonó no por el bloqueo en sí, sino porque Infisical ya trae su propio login (password largo, no un hostname abierto) — Access habría sido una segunda capa redundante. Queda como referencia si en el futuro se quiere proteger otro hostname (ej. Glances) vía agente: hay que correr esos pasos manualmente o aprobar el permiso explícitamente, no asumir que el agente puede hacerlo de punta a punta.
Véase también
- Gestión de secretos — fuente del
CLAUDE_CODE_APPS_AND_POLICIES_DNS_ZONESusado para autenticar contra la API de Cloudflare - Túneles de Cloudflare — forma alternativa de exponer servicios; Access se puede poner encima de un hostname expuesto así o de cualquier otro método
- Glances — hostname (
monitor.estebanalcocer.cloud) todavía sin ningún login, candidato real a esta misma policy (a diferencia de Infisical, que ya tiene el suyo) - Infisical — por qué se descartó Access ahí: ya tiene su propia autenticación
- Vaultwarden — mismo caso que Infisical, sin Access delante
- Homepage — dashboard sin login propio, Access es su única barrera