CreaRack-SL

ADR — Patrón CF Access Service Token en callers Django runtime (Fix d20 / s58)

ADR — Patrón CF Access Service Token en callers Django runtime

Contexto

Tras la sesión 57 (11-12 mayo 2026), se endureció CF Access en las apps “Workspace MCP API” y “API Biblioteca” eliminando la policy bypass-everyone. Se distribuyó el Service Token a todos los callers conocidos (batch: GitHub Actions, scripts CLI, crons). Sin embargo, los callers Django runtime de PROD no estaban catalogados como callers y quedaron sin los headers necesarios, causando el incidente del Help Widget (sesión 58).

Esta decisión documenta el patrón técnico adoptado y el protocolo de audit para futuros endurecimientos de CF Access.

Decisión

Patrón de inyección de headers CF Access

Cualquier caller Django que llame a un endpoint protegido por CF Access debe usar un helper centralizado que inyecte los headers condicionalmente:

# Patrón estándar — inyección condicional (no rompe dev local)
cf_id = getattr(settings, "CF_ACCESS_CLIENT_ID", "")
cf_secret = getattr(settings, "CF_ACCESS_CLIENT_SECRET", "")
if cf_id and cf_secret:
    headers["CF-Access-Client-Id"] = cf_id
    headers["CF-Access-Client-Secret"] = cf_secret

Principios clave:

  • Condicional: si las vars están vacías (dev local), no se envían → no rompe entornos sin CF Access.
  • Centralizado: un solo helper por archivo (_workspace_headers() en api_help.py, _cf_access_headers() en scripts harness) cubre todos los callers del módulo.
  • Settings explícitos: las vars se declaran en config/settings/base.py con default vacío, documentando que existen.
  • Compose propagado: compose.yml y compose.prod.yml incluyen las vars para que Dokploy las pueda inyectar en PROD.

Variables de entorno

VariableDescripciónDónde configurar
CF_ACCESS_CLIENT_IDService Token ID de CF AccessDokploy panel (PROD) + PowerShell local (dev)
CF_ACCESS_CLIENT_SECRETService Token Secret de CF AccessDokploy panel (PROD) + PowerShell local (dev)

Siguen el mismo patrón que WORKSPACE_MCP_TOKEN (precedente establecido antes de s57).

Protocolo de audit para futuros endurecimientos CF Access

Cuando se elimine bypass-everyone (o equivalente) de cualquier app CF Access, el audit de callers debe cubrir todas las capas:

CapaDónde buscar
GitHub Actions workflows.github/workflows/*.yml
Scripts CLI / cronsscripts/**/*.py, scripts/**/*.sh
Callers Django runtime ⚠️*/api_*.py, */views*.py, cualquier archivo que use requests, urllib, o httpx para llamar a workspace.crearack.com
Frontend (si aplica)Llamadas directas a workspace desde el browser (generalmente no aplica por CORS)

⚠️ La capa más fácil de olvidar es Django runtime — no aparece en los workflows de GitHub y sólo se descubre revisando activamente los archivos de la app Django.

Alternativas consideradas

AlternativaRazón de descarte
Mantener bypass-everyone en PRODReduce la postura de seguridad; CF Access pierde su valor como capa de autenticación M2M
Middleware Django centralizado para inyectar headersSobreingeniería para el número actual de callers; el patrón helper es suficiente y más explícito
Proxy interno (evitar CF Access desde Django)Añade complejidad de infraestructura; el patrón de Service Token es el estándar CF para M2M

Consecuencias

  • Positivas: Todos los callers Django runtime ahora se autentican correctamente contra CF Access. El patrón es reutilizable para futuros módulos.
  • Negativas / deuda: El audit manual de callers sigue siendo necesario — no existe automatización que verifique que todos los callers tienen los headers.
  • Deuda técnica pendiente: Considerar un test de integración o linter que detecte llamadas a workspace.crearack.com sin CF-Access-* headers en el futuro.

Estado

Implementado en PR #26 (v1.0.73 hotfix, 2026-05-12).

Véase también

  • [[incident—20260512—help-widget-cf-access-s58]]