Resumen
Corrección de dos superficies de la UI local del Agent que quedaron inaccesibles tras activar el enforcement del Bearer local (v2.1.2):
- Página de diagnóstico local (
localhost:5050/metrics/ui,/licenses) — servidas sin sesión del SaaS, no pueden llevar token. Se eximen seis rutas (dos páginas + cuatro GET de estado). - Iframe del terminal — ahora recibe y envía el
Bearerrecibido porpostMessage, permitiendo que el fetch a/terminal/sessionsse autentique.
Resultado: Fase 3 (Raíz 3) CERRADA. La política fail-closed intacta: acciones y GETs sensibles siguen protegidas.
Problema y contexto
Con el enforcement del token local activado en Agent 2.1.2 (cierre de la Raíz 3 de la Auditoría Suprema), la mayoría de endpoints pasaron a exigir Authorization: Bearer <token>. Sin embargo, dos superficies que el propio Agent muestra en el ordenador del cliente (localhost:5050) quedaron devolviendo 401:
-
Página de diagnóstico del Agent (assets/debug.html, metrics.html):
- Se sirven sin sesión del SaaS — el cliente accede vía navegador local sin JWT.
- No tienen forma de presentar un token (no son javascript que intercepte SaaS auth).
- Consumían GET de solo lectura (
/sentinel/status,/traps/stats,/metrics/stats). - Aparecía error: “Sentinel status not available” aunque el servicio funcionaba.
-
Iframe del terminal (assets/terminal.html):
- Empotraba un fetch a
/terminal/sessionssin header de autenticación. - El SaaS sí le pasaba el token local vía
postMessage(SET_AUTH_TOKEN), pero el iframe no lo usaba.
- Empotraba un fetch a
Solución (Agent 2.1.3)
Exempt paths en local_token.py
Se añadieron 6 rutas a EXEMPT_PATHS:
EXEMPT_PATHS = frozenset({
# ... bootstrap existente ...
"/metrics/ui", # Página HTML de métricas locales
"/licenses", # Página de licencias
"/licenses.txt", # Endpoint de descarga de licencias
"/sentinel/status", # GET estado del Sentinel (lo-lectura)
"/traps/stats", # GET estadísticas de traps
"/traps/recent", # GET eventos recientes de traps
"/metrics/stats", # GET estadísticas de métricas
# ... resto de exentos ...
})
Política: todas son GET de solo lectura consumidas por la propia UI local. El estado del Sentinel ya se expone en /info, por lo que no se añade lógica nueva — solo se abre lo que ya existía.
Terminal iframe y Bearer en terminal.html
El fetch a /terminal/sessions ahora añade el header Authorization:
// Antes:
fetch(`/terminal/sessions`).then(r => r.json())...
// Después:
fetch(`/terminal/sessions`,
localAuthToken ? { headers: { Authorization: `Bearer ${localAuthToken}` } } : undefined
).then(r => r.json())...
El localAuthToken ya se recibía vía postMessage(SET_AUTH_TOKEN) — ahora se usa.
Política fail-closed intacta
Todas las acciones y GETs sensibles siguen detrás del token:
| Ruta | Protección |
|---|---|
/cluster/* | ✅ Bearer obligatorio (RCE) |
/network/* | ✅ Bearer obligatorio (SSRF/scan) |
/sftp/* | ✅ Bearer obligatorio (acceso de ficheros) |
/signage/* | ✅ Bearer obligatorio |
/admin/uninstall | ✅ Bearer obligatorio |
/sentinel/start|stop|reset | ✅ Bearer obligatorio |
/metrics/purge* | ✅ Bearer obligatorio |
/terminal/sessions | ✅ Bearer obligatorio (iframe la envía) |
/terminal/session/* | ✅ Bearer obligatorio |
Cambios detallados
1. terminal/agent/core/local_token.py
- EXEMPT_PATHS += 8 rutas: páginas UI local + GET de estado.
- Documentación reforzada: se especifica por qué se eximen (bootstrap, páginas local, GET de solo lectura).
- Raíz 3 en comentario:
POLÍTICA (fail-closed)enumera qué sí está protegido.
2. terminal/agent/assets/terminal.html
- Línea ~390:
fetch(/terminal/sessions)incluyelocalAuthTokenen headers si está disponible. - Formato:
.replace()(no.format()) — las llaves JS quedan escapadas.
3. terminal/agent/version.py
- Versión:
2.1.2→2.1.3. - Changelog detallado: qué se exime y por qué.
4. tests/agent/test_agent_local_auth.py
test_exempt_paths_cover_bootstrap(): nuevas rutas en assertion.- Confirmación:
/terminal/sessions,/sentinel/start,/metrics/purgeNO se eximen.
Validación
- ✅ 7 tests verdes.
- ✅
rufflimpio. - ✅ Testeado en Docker.
- ✅ RELEASE_NOTES actualizado con pasos de Edu (build_agent.bat → smoke → publish).
Implicaciones
- Usuarios: No ven cambios — la UI local vuelve a funcionar normalmente.
- Seguridad: Raíz 3 cierre sin regresiones — el iframe del terminal sigue protegido.
- Operaciones: Fase 3 (audit local) CERRADA. Próxima: Fase 4.
Véase también
- [[entity—terminal—model—agent-instance]]
- [[entity—terminal—service—local-token]]
- [[concept—security—bearer-token]]
- [[concept—terminal—local-api]]
- [[feature—terminal—enforcement-local-token]]