Volver a la wiki

ADR: Migración a CF Access Service Tokens para endpoints internos del Workspace

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 AccessRuta
.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

Callers modificados (PR #25)

Todos los scripts/workflows que realizan llamadas al MCP endpoint han sido actualizados para inyectar los headers condicionalmente:

ArchivoTipoMecanismo
.github/scripts/bib_ingest.pyScript Python CIos.environ.get("CF_ACCESS_CLIENT_ID") + dict headers
.github/workflows/post-merge-ingest.ymlGitHub Actionssecrets → env del step Python
.github/workflows/push-drift.ymlGitHub Actionssecrets → env + array CF_HEADERS bash
scripts/bib_ast.pyScript Python localos.environ.get("CF_ACCESS_CLIENT_ID")
scripts/bib_docs.pyScript Python localos.environ.get("CF_ACCESS_CLIENT_ID")
scripts/bib_openapi.pyScript Python localos.environ.get("CF_ACCESS_CLIENT_ID")
scripts/bib-reindex.ps1PowerShell (Windows/Edu)[Environment]::GetEnvironmentVariable → $env:CF_ACCESS_*
scripts/cron-bib-reindex.shBash 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:

Para entornos locales/staging:

Consecuencias

Positivas

Pendientes (PR 2/2 — repo CreaRackSL-workspace)

Test plan (estado al merge de PR #25)

Referencias

Véase también

Subir