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
| Archivo | Mecanismo |
|---|---|
.github/scripts/bib_ingest.py | os.environ.get("CF_ACCESS_CLIENT_ID") → headers dict |
.github/workflows/post-merge-ingest.yml | Secrets CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET → env |
.github/workflows/push-drift.yml | Secrets → CF_HEADERS array en curl |
scripts/bib_ast.py | os.environ.get("CF_ACCESS_CLIENT_ID") → headers dict |
scripts/bib_docs.py | os.environ.get("CF_ACCESS_CLIENT_ID") → headers dict |
scripts/bib_openapi.py | os.environ.get("CF_ACCESS_CLIENT_ID") → headers dict |
scripts/bib-reindex.ps1 | [Environment]::GetEnvironmentVariable → $headers dict |
scripts/cron-bib-reindex.sh | Archivos .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
| Entorno | Fuente 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 Python | Variables 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.shlee las credenciales de ficheros en disco ($DIR/.cf-access-id). Esos ficheros deben tener permisos600y 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]]