CreaRack-SL

D19: CF Access Service Token en workflows y scripts de ingest de Biblioteca

D19: CF Access Service Token en workflows y scripts de ingest de Biblioteca

Contexto

La infraestructura de Biblioteca expone endpoints MCP en workspace.crearack.com protegidos por Cloudflare Access. Hasta la sprint s57 (2026-05-12), las políticas de CF Access incluían reglas de tipo bypass-everyone que permitían que los scripts de ingest y los workflows de GitHub Actions alcanzaran los endpoints usando únicamente el BIB_MCP_TOKEN (Bearer token JWT).

Un análisis de CF Security Insights marcó estas reglas bypass como hallazgos Critical, exigiendo su eliminación. Sin ellas, cualquier request que no presente un Service Token válido de CF Access es bloqueada en el edge antes de llegar a los Workers.

Decisión

Añadir soporte condicional para los headers CF-Access-Client-Id y CF-Access-Client-Secret en todos los puntos de llamada HTTP hacia la API MCP de Biblioteca. El soporte es opt-in vía variables de entorno: si CF_ACCESS_CLIENT_ID y CF_ACCESS_CLIENT_SECRET están vacías, el comportamiento es idéntico al anterior (retrocompatible).

Archivos afectados

ArchivoMecanismo
.github/scripts/bib_ingest.pyos.environ.get("CF_ACCESS_CLIENT_ID") → headers dict
.github/workflows/post-merge-ingest.ymlSecrets CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET → env
.github/workflows/push-drift.ymlSecrets → CF_HEADERS array en curl
scripts/bib_ast.pyos.environ.get("CF_ACCESS_CLIENT_ID") → headers dict
scripts/bib_docs.pyos.environ.get("CF_ACCESS_CLIENT_ID") → headers dict
scripts/bib_openapi.pyos.environ.get("CF_ACCESS_CLIENT_ID") → headers dict
scripts/bib-reindex.ps1[Environment]::GetEnvironmentVariable → $headers dict
scripts/cron-bib-reindex.shArchivos .cf-access-id / .cf-access-secret → CF_HEADERS array

Patrón de implementación (Python)

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {token}",
    "User-Agent": "CreaRack-Bib.../1.0",
}
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 de implementación (Bash/curl)

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[@]}" "$MCP_URL"

Origen de credenciales por entorno

EntornoFuente de las credenciales
GitHub Actions (post-merge-ingest)Secrets CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET
GitHub Actions (push-drift)Mismos secrets vía env:
Linux cron (cron-bib-reindex.sh)Archivos $DIR/.cf-access-id y $DIR/.cf-access-secret
Windows dev (bib-reindex.ps1)Variables de entorno de usuario (User scope)
Ejecución local PythonVariables de entorno del proceso

Consecuencias

Positivas:

  • Elimina los hallazgos Critical de CF Security Insights (sprint s57).
  • Permite retirar las políticas bypass-everyone de CF Access sin romper los workflows automatizados.
  • Retrocompatible: entornos sin las variables de entorno configuradas siguen funcionando exactamente igual.
  • Patrón uniforme en todos los lenguajes (Python, Bash, PowerShell).

Negativas / riesgos:

  • Los Service Tokens de CF Access tienen expiración. Si rotan sin actualizar los secrets de GitHub Actions / cron, los workflows fallan con 403 en el edge antes de llegar al Worker (el error puede confundirse con un fallo del MCP).
  • El script cron-bib-reindex.sh lee las credenciales de ficheros en disco ($DIR/.cf-access-id). Esos ficheros deben tener permisos 600 y estar fuera del árbol del repo.

Alternativas descartadas

  • No hacer nada: inaceptable, los hallazgos Critical de CF Security Insights bloquearían la certificación de seguridad.
  • Añadir IP allowlist en CF Access: no viable en GitHub Actions (IPs dinámicas de los runners).
  • Tunnel dedicado para CI: sobreingeniería para el volumen actual de llamadas.

Estado

Implementado en PR#25, mergeado a main el 2026-05-12. Las políticas bypass-everyone deben eliminarse de CF Access dashboard en sprint s57 tras confirmar que los secrets están configurados en todos los entornos.

Véase también

  • [[runbook—infra—rotate-mcp-token]]
  • [[feature—biblioteca—bib-ingest-pipeline]]
  • [[concept—infra—cloudflare-access]]