CreaRack-SL

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

  • ID CF Access: 4879f9f4-… (truncado por seguridad)
  • Policies dual-mode: añadidas a las 3 apps marcadas Critical inicialmente (precedence 2, coexisten con bypass everyone precedence 1)

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:

  • CF_ACCESS_CLIENT_ID
  • CF_ACCESS_CLIENT_SECRET

Para entornos locales/staging:

  • PowerShell (Edu): variables de entorno de usuario en Windows (User scope via [Environment]::GetEnvironmentVariable)
  • cron Linux: archivos $DIR/.cf-access-id y $DIR/.cf-access-secret (readable solo por el user del cron, leídos con tr -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.ps1 en repo workspace (PC Edu local).
  • Eliminar policies bypass everyone una 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-drift para 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]]