Seguridad por escenario de despliegue en Mentat
Consideraciones de seguridad a tener en cuenta al desplegar Mentat en cada escenario —no una auditoría de vulnerabilidades, sino qué mirar y qué decidir en cada caso.
TL;DR
- Mentat se despliega en cuatro escenarios (GPU compartida, cloud autogestionado, cliente que solo consume agentes, y on-premise), y cada uno tiene un perfil de riesgo distinto.
- El más delicado es el Escenario 3 (cliente solo consume agentes): conviven dos aplicaciones sobre la misma infraestructura y hay que resolver sesión y autorización con cuidado.
- Las preocupaciones recurrentes son la exposición de puertos, el aislamiento de trazas en Langfuse y el control de acceso al gateway LiteLLM.
- Al final hay una tabla-resumen de riesgos y una lista de decisiones de seguridad pendientes.
Contexto de arquitectura
Este artículo asume la arquitectura descrita en Arquitectura de Mentat — el framework RAG. En resumen: Mentat es la plataforma base (ingesta + RAG) sobre la que cada cliente monta sus agentes; todas las llamadas a los modelos pasan por un gateway LiteLLM con trazas en Langfuse, los modelos corren en local con Ollama, y los datos viven en Qdrant (vectores) y PostgreSQL (estructurado).
Dimensiones de seguridad analizadas
- Autenticación — ¿quién puede entrar y cómo se verifica?
- Autorización — ¿qué puede ver y hacer cada rol?
- Aislamiento de datos — ¿puede un usuario ver datos de otro cliente?
- Trazas y observabilidad — ¿quién ve las trazas de Langfuse?
- Sesión compartida — ¿la UI de Mentat y la app del cliente comparten sesión?
Escenario 1 — Múltiples clientes, GPU compartida
| Dimensión | Situación | Riesgo | Recomendación |
|---|---|---|---|
| Autenticación | Cada instancia de Mentat tiene su propio sistema de auth (sesiones Redis) | Bajo — instancias aisladas | Mantener auth por instancia |
| Autorización | Roles admin / user_avc por instancia |
Bajo | Añadir rol consumer para usuarios finales |
| Aislamiento de datos | Cada cliente tiene su propio Qdrant + PostgreSQL | Bajo | Verificar que el DOCS_COLLECTION no se comparte |
| Trazas Langfuse | Si Langfuse es compartido, el equipo ve las trazas de todos los clientes | Medio | Separar proyectos en Langfuse por cliente, o un Langfuse por instancia |
| Sesión compartida | La UI de Mentat y app cliente en el mismo dominio/puerto → misma cookie | Bajo | Sin problema si van en la misma instancia |
| LiteLLM compartido | Todas las llamadas LLM pasan por el mismo proxy con una LITELLM_MASTER_KEY compartida (gasto agregado, sin distinguir servicios) |
Medio | Separar en virtual keys por cliente/servicio (fase Build) para atribución de gasto y límites de uso; la Admin UI requiere una BD litellm propia en PostgreSQL |
Escenario 2 — Cliente autogestionado en cloud
| Dimensión | Situación | Riesgo | Recomendación |
|---|---|---|---|
| Autenticación | Instancia propia → auth propia | Bajo | El cliente gestiona sus propios usuarios |
| Autorización | El cliente es admin de su instancia |
Medio | Definir qué puede tocar el cliente admin y qué no (ej. no puede modificar prompts base de Mentat) |
| Aislamiento de datos | Stack completo aislado | Bajo | Sin riesgo de fuga entre clientes |
| Trazas Langfuse | Langfuse propio del cliente | Bajo | El cliente ve solo sus trazas |
| Sesión compartida | App cliente en la misma instancia | Bajo | Sin problema |
| Exposición de puertos | El cliente gestiona su infraestructura cloud | Alto | Documentar qué puertos deben estar cerrados al exterior (Qdrant, Redis, PostgreSQL, Adminer solo en localhost) |
Escenario 3 — Cliente solo consume agentes
El escenario más complejo
Es el más delicado desde el punto de vista de seguridad porque hay dos aplicaciones (la UI de Mentat interna + la app de cliente externa) sobre la misma infraestructura.
| Dimensión | Situación | Riesgo | Recomendación |
|---|---|---|---|
| Autenticación | Dos apps, ¿una sesión o dos? | Alto | Definir estrategia (ver opciones abajo) |
| Autorización | El cliente no debe ver ingesta, prompts internos ni jobs | Alto | Rol consumer: solo accede a /agentes, sin acceso al resto de Mentat |
| Aislamiento de datos | El cliente solo consulta su colección Qdrant | Medio | agents-api debe validar que el collection pertenece al cliente autenticado |
| Trazas Langfuse | El cliente no debería ver las trazas | Medio | Langfuse es solo para el equipo interno, no expuesto al cliente |
| Sesión compartida | Apps en dominios distintos → cookies no se comparten | Alto | Ver opciones de sesión compartida abajo |
| API agents-api expuesta | El cliente llama directamente a agents-api |
Medio | agents-api debe requerir autenticación, no ser un endpoint público |
Opciones para gestión de sesión
Opción A — Mismo dominio, rutas separadas (recomendada para empezar)
- Admin (Mentat) en
https://app.empresa.com - App cliente en
https://app.empresa.com/cliente - Misma cookie de sesión, el rol
consumerlimita el acceso - Ventaja: sin complejidad de auth adicional
- Desventaja: el cliente accede al mismo dominio que el equipo interno
Opción B — Subdominios con sesión compartida
- Admin (Mentat) en
https://admin.empresa.com - App cliente en
https://app.empresa.com - Sesión compartida via cookie en dominio padre (
.empresa.com) - Ventaja: separación visual clara
- Desventaja: requiere HTTPS y configuración de cookies de dominio
Opción C — Auth independiente con JWT
- Cada app tiene su propio sistema de auth
agents-apivalida JWT en lugar de cookie de sesión- Ventaja: máxima independencia
- Desventaja: duplicar la gestión de usuarios, más complejidad
Escenario 4 — On-premise en el cliente
| Dimensión | Situación | Riesgo | Recomendación |
|---|---|---|---|
| Autenticación | El cliente gestiona todo | Bajo | Documentar cómo crear el primer admin |
| Autorización | El cliente es dueño de la instancia | Bajo | Sin restricciones adicionales |
| Aislamiento de datos | Todo en la red del cliente | Bajo | El mayor riesgo es la seguridad de la red interna del cliente, no nuestra responsabilidad |
| Trazas Langfuse | 100% en local del cliente | Bajo | Soberanía total de datos |
| Exposición de puertos | Crítico — el docker-compose actual expone algunos puertos sin restricción | Alto | Ver checklist abajo |
| Ollama expuesto | Ollama por defecto escucha en 0.0.0.0 |
Medio | Configurar Ollama para escuchar solo en localhost o red interna |
Resumen de riesgos por escenario
| Escenario | Riesgo global | Principal preocupación |
|---|---|---|
| 1 — GPU compartida | Medio | Langfuse compartido puede exponer trazas entre clientes |
| 2 — Cloud autogestionado | Medio | Puertos expuestos en cloud si el cliente no configura el firewall |
| 3 — Solo consume agentes | Alto | Gestión de sesión entre dos apps y autorización granular |
| 4 — On-premise | Bajo-Medio | Puertos expuestos en red interna del cliente |
Decisiones de seguridad pendientes
- Rol
consumer— definir exactamente qué rutas puede ver y cuáles están bloqueadas - Estrategia de sesión Escenario 3 — elegir entre Opción A, B o C antes de implementar la UI del cliente
- Autenticación en agents-api — actualmente no requiere auth propia (confía en que el backend NestJS actúa de proxy). Si se expone directamente al cliente, necesita validación de sesión propia
- Langfuse por cliente vs compartido — decidir en Escenario 1 si Langfuse es compartido o por instancia
- Reverse proxy — para producción, todos los servicios deberían estar detrás de nginx/Caddy con HTTPS
Vuelta a la arquitectura completa: Arquitectura de Mentat — el framework RAG.