ADR: CF Access Service Tokens como segunda capa de autenticación edge
Estado
Aprobado — implementado en PR #25 (2026-05-12). Migración en curso (1/2).
Contexto
El informe CF Security Insights 2026-05-11 identificó 5 hallazgos Critical en las aplicaciones de Cloudflare Access del workspace workspace.crearack.com. El problema: las 5 apps de CF Access tenían configurada la policy bypass everyone, lo que significa que cualquier request al edge pasaba sin validación Access. La autenticación real residía únicamente en la capa de aplicación (middleware Pages Functions validando Bearer Token).
Apps afectadas con bypass everyone
| App CF Access | Ruta |
|---|---|
.well-known | /.well-known/… |
biblioteca | /api/biblioteca/ |
maintenance | /api/maintenance/* |
mcp | /api/mcp |
oauth | /oauth |
CF Access marcó este patrón como Critical: la protección edge estaba desactivada, exponiendo los endpoints al edge de Cloudflare sin ninguna verificación de identidad a ese nivel.
Decisión
Añadir Cloudflare Access Service Token como segunda capa de autenticación (edge), manteniendo el Bearer Token de aplicación existente intacto.
Estrategia dual-mode (opcional / segura)
Los headers de CF Access (CF-Access-Client-Id / CF-Access-Client-Secret) se inyectan condicionalmente: solo si las variables de entorno CF_ACCESS_CLIENT_ID y CF_ACCESS_CLIENT_SECRET están definidas. Si no lo están, las requests siguen funcionando porque CF Access aún mantiene activa la policy bypass everyone (precedence 1).
Esto permite una migración gradual y sin downtime:
Fase actual (dual-mode):
CF Access: policy "bypass everyone" (precedencia 1) + policy Service Token (precedencia 2)
Callers: envían CF-Access headers cuando secrets están configurados
Fase final (tras PR 2/2 — workspace):
CF Access: solo policy Service Token
Callers: siempre envían CF-Access headers
Service Token creado
- ID CF Access:
4879f9f4-…(truncado por seguridad) - Policies dual-mode: añadidas a las 3 apps marcadas Critical inicialmente (precedence 2, coexisten con
bypass everyoneprecedence 1)
Callers modificados (PR #25)
Todos los scripts/workflows que realizan llamadas al MCP endpoint han sido actualizados para inyectar los headers condicionalmente:
| Archivo | Tipo | Mecanismo |
|---|---|---|
.github/scripts/bib_ingest.py | Script Python CI | os.environ.get("CF_ACCESS_CLIENT_ID") + dict headers |
.github/workflows/post-merge-ingest.yml | GitHub Actions | secrets → env del step Python |
.github/workflows/push-drift.yml | GitHub Actions | secrets → env + array CF_HEADERS bash |
scripts/bib_ast.py | Script Python local | os.environ.get("CF_ACCESS_CLIENT_ID") |
scripts/bib_docs.py | Script Python local | os.environ.get("CF_ACCESS_CLIENT_ID") |
scripts/bib_openapi.py | Script Python local | os.environ.get("CF_ACCESS_CLIENT_ID") |
scripts/bib-reindex.ps1 | PowerShell (Windows/Edu) | [Environment]::GetEnvironmentVariable → $env:CF_ACCESS_* |
scripts/cron-bib-reindex.sh | Bash cron (Linux staging) | Archivos .cf-access-id / .cf-access-secret → export |
Patrón Python (uniforme en todos los scripts)
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
"User-Agent": "…",
}
cf_id = os.environ.get("CF_ACCESS_CLIENT_ID", "").strip()
cf_secret = os.environ.get("CF_ACCESS_CLIENT_SECRET", "").strip()
if cf_id and cf_secret:
headers["CF-Access-Client-Id"] = cf_id
headers["CF-Access-Client-Secret"] = cf_secret
Patrón bash (workflows y cron)
CF_HEADERS=()
if [ -n "${CF_ACCESS_CLIENT_ID:-}" ] && [ -n "${CF_ACCESS_CLIENT_SECRET:-}" ]; then
CF_HEADERS+=(-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID")
CF_HEADERS+=(-H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET")
fi
curl ... "${CF_HEADERS[@]}" ...
Secrets configurados
Añadidos manualmente al repo CreaRack-Pro via gh secret set:
CF_ACCESS_CLIENT_IDCF_ACCESS_CLIENT_SECRET
Para entornos locales/staging:
- PowerShell (Edu): variables de entorno de usuario en Windows (
Userscope via[Environment]::GetEnvironmentVariable) - cron Linux: archivos
$DIR/.cf-access-idy$DIR/.cf-access-secret(readable solo por el user del cron, leídos contr -d '\r\n')
Consecuencias
Positivas
- Elimina 5 hallazgos Critical del informe CF Security Insights 2026-05-11.
- Añade defensa en profundidad: edge (CF Access) + aplicación (Bearer Token).
- Migración sin downtime gracias al dual-mode.
- Patrón uniforme y consistente en todos los callers (Python, bash, PowerShell).
Pendientes (PR 2/2 — repo CreaRackSL-workspace)
- Modificar
bib-reindex.ps1en repo workspace (PC Edu local). - Eliminar policies
bypass everyoneuna vez confirmado que todos los callers envían Service Token correctamente. - Repo pareja: CreaRackSL/CreaRackSL-workspace#TBD.
Test plan (estado al merge de PR #25)
- ✅ Workflows YAML válidos (parser GH Actions check)
- ✅ Service Token creado en CF Access (
4879f9f4-…) - ✅ Policies dual-mode añadidas a las 3 apps Critical (precedence 2)
- ⏳ CI verde — pendiente verificación post-merge
- ⏳ Trigger manual
push-driftpara verificar funcionamiento con/sin secrets activos
Referencias
- PR #25:
feat(security/d19): CF Access Service Token headers en workflows MCP - Informe origen: CF Security Insights 2026-05-11 (interno)
- Repo pareja: CreaRackSL/CreaRackSL-workspace (PR TBD)
Véase también
- [[feature—ci—cf-access-headers-workflow-scripts]]
- [[runbook—infra—rotate-mcp-token]]