CF Access Service Token headers en workflows CI y scripts del Workspace
Resumen
Implementación del patrón de autenticación CF Access Service Token en los 8 workflows y 3 scripts del repo CreaRackSL-workspace que llaman a endpoints internos protegidos por Cloudflare Access. Introducido en PR#29 (2026-05-12) como Parte 2/2 de la migración a CF Access.
Motivación
Los endpoints workspace.crearack.com/api/biblioteca/, /api/maintenance/* y /api/mcp están protegidos por Cloudflare Access. Sin credenciales de CF Access, los workflows dependían de la policy bypass everyone — un hallazgo Critical en el informe CF Security Insights 2026-05-11.
Patrón de implementación
La estrategia es headers opcionales: si los secrets CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET no están definidos en el entorno, las requests funcionan igual que antes (sin los headers). Esto garantiza retrocompatibilidad y rollout seguro.
Bash (workflows con curl directo)
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 -sS -X POST \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Content-Type: application/json" \
"${CF_HEADERS[@]}" \
"https://workspace.crearack.com/api/mcp"
Los secrets se exponen al step via env: del workflow:
env:
MCP_TOKEN: ${{ secrets.MCP_TOKEN }}
CF_ACCESS_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }}
CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }}
Python (urllib)
import os, urllib.request
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"User-Agent": "Bibliotecario-Ingest/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
req = urllib.request.Request(url, data=payload, headers=headers, method="POST")
JavaScript / Node.js (fetch)
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
'User-Agent': 'bib-ast-ts/1.0',
};
const cfId = (process.env.CF_ACCESS_CLIENT_ID || '').trim();
const cfSecret = (process.env.CF_ACCESS_CLIENT_SECRET || '').trim();
if (cfId && cfSecret) {
headers['CF-Access-Client-Id'] = cfId;
headers['CF-Access-Client-Secret'] = cfSecret;
}
const res = await fetch(mcpUrl, { method: 'POST', headers, body });
Inventario de archivos modificados
Workflows con curl directo
| Archivo | Step(s) afectado(s) | Endpoint destino |
|---|---|---|
.github/workflows/drift-cron.yml | Invoke /api/biblioteca/drift-run | /api/biblioteca/drift-run |
.github/workflows/wiki-curator.yml | Invoke wiki_curator_review via MCP | /api/mcp |
.github/workflows/wiki-lint.yml | wiki_lint_bulk (step 1) + wiki_lint_contradictions (step 2) | /api/mcp |
.github/workflows/wiki-lint-consolidation.yml | Invoke wiki_lint_contradictions mode=full | /api/mcp |
.github/workflows/wiki-utility.yml | Invoke wiki_utility_recompute via MCP | /api/mcp |
Scripts externos con sus workflows
| Script | Workflow que lo invoca | Lenguaje | User-Agent |
|---|---|---|---|
scripts/bib_ast_ts.mjs | bib-reindex-ts.yml | Node.js | bib-ast-ts/1.0 |
.github/scripts/bib_ingest.py | post-merge-ingest.yml | Python | Bibliotecario-Ingest/1.0 |
scripts/generate-weekly-report.py | wiki-weekly-report.yml | Python | Bibliotecario-Weekly-Report/1.0 |
Secrets de repositorio
| Secret | Uso |
|---|---|
CF_ACCESS_CLIENT_ID | Header CF-Access-Client-Id |
CF_ACCESS_CLIENT_SECRET | Header CF-Access-Client-Secret |
Añadidos manualmente via gh secret set. Corresponden al service token CF Access con ID 4879f9f4-... (duración forever).
Endpoints protegidos cubiertos
| Endpoint | App CF Access |
|---|---|
https://workspace.crearack.com/api/biblioteca/ | biblioteca |
https://workspace.crearack.com/api/biblioteca/drift-run | biblioteca |
https://workspace.crearack.com/api/mcp | mcp |
Estado de despliegue
- Secrets añadidos al repo.
- Código propagado a
main(PR#29). - Service Token CF Access activo.
- Policies
Service Token Bypassañadidas a las 3 apps CF Access (precedence 2). - PENDIENTE: Eliminar policy
bypass everyonede las 3 apps CF Access para cerrar los 5 Critical del informe.
Véase también
- [[decision—20260512—cf-access-service-tokens]]
- [[runbook—infra—rotate-mcp-token]]
Referenciado desde
- ¿Cómo se autentica el endpoint MCP del workspace (/api/mcp)? ¿Dónde se valida el MCP_TOKEN / Bearer y cómo se rota? ¿Qué relación tiene con CF Access?
- ADR: Migración a CF Access Service Tokens para endpoints internos del Workspace
- Procedimiento para rotar el CF Access service token del workspace (P1b de la auditoría s219): qué pasos incluye (/rotate mantiene client-id, GH secrets, OPS bib-reindex .cf-access-secret, env vars Use