01Infraestructura
Mekovault corre en AWS Lightsail, región us-east-1, sobre cuatro máquinas conectadas por red privada. Solo una de ellas recibe tráfico de Internet.
| Host | Rol | Servicios | Exposición |
|---|---|---|---|
mekovault-app | Aplicación | nginx, Next.js, servicios FastAPI, addons | Única expuesta: puertos 80/443 detrás de Cloudflare |
mekovault-data | Datos | PostgreSQL 16, Redis (2 instancias), RabbitMQ | Solo red privada |
mekovault-vault | Secretos | Infisical self-hosted, un project por tenant | Solo red privada |
mekovault-obs | Observabilidad | Grafana, Loki, Prometheus, Tempo, OpenTelemetry Collector | Solo red privada |
- Administración exclusivamente por Tailscale (WireGuard). No hay SSH abierto a Internet en ningún host.
- Firewall
ufwactivo en cada máquina con política de denegar por defecto. - Los servicios internos (PostgreSQL, Redis, RabbitMQ, Infisical, Grafana y los procesos FastAPI) escuchan en loopback o en la interfaz privada. nginx es el único proceso que atiende tráfico público.
- Cloudflare termina TLS y filtra tráfico antes de que llegue a nginx.
02Arquitectura de servicios
La interfaz web es una aplicación Next.js 16. El backend está compuesto por servicios FastAPI sobre Python 3.12 con SQLAlchemy asíncrono, cada uno con una responsabilidad acotada.
| Servicio | Responsabilidad |
|---|---|
svc-auth | Autenticación, sesiones, MFA y reset de contraseña |
svc-tenancy | Empresas, membresías, roles, resellers y eliminación de tenants |
svc-requests | Solicitudes de alta, baja y cambio; flujo de aprobaciones |
svc-tickets | Tickets de soporte y su seguimiento |
svc-billing | Planes, suscripciones y facturación |
svc-notifications | Correo y notificaciones a usuarios |
| addons | Módulos conectores. Ejemplo: super-workspace, que opera sobre Google Workspace |
Eventos y trabajos en segundo plano
- Los servicios se comunican por eventos a través de RabbitMQ, en el exchange de tipo topic
mekovault.events. - Los trabajos pesados (provisioning, eliminaciones, exportaciones) se registran en un libro mayor de jobs en PostgreSQL y los ejecuta un worker único.
- Cada job reintenta con backoff exponencial. Sus pasos son idempotentes: volver a ejecutar un paso ya completado no produce efectos duplicados sobre el directorio.
- Cuando un job agota sus reintentos, se genera una alerta y el job queda visible con su último error para intervención manual.
03Identidad y sesión
Los usuarios de Mekovault (administradores de TI, aprobadores, resellers) se autentican con OAuth de Google o con email y contraseña. La sesión está diseñada para que un token robado sirva poco tiempo y sea revocable.
| Mecanismo | Detalle |
|---|---|
| Métodos de acceso | OAuth con Google, o email y contraseña |
| Token de acceso | JWT firmado con RS256, vigencia de 15 minutos, con claims de rol y de estado MFA |
| Refresh token | Vigencia de 30 días. Vive solo en una cookie HttpOnly (inaccesible desde JavaScript). Se rota en cada uso y es revocable individualmente por jti en Redis. Hereda el estado MFA de la sesión y revalida la membresía del usuario en la empresa en cada renovación. |
| MFA | TOTP (apps de autenticación estándar). Límite de 5 intentos por minuto; bloqueo de 15 minutos tras 10 fallos consecutivos. |
| Operaciones sensibles | Exigen que la sesión tenga MFA verificado, aunque el usuario ya esté autenticado. |
| Reset de contraseña | Token de un solo uso con vigencia de 30 minutos. |
Consecuencia práctica: un access token filtrado caduca en 15 minutos como máximo; el refresh no es exportable desde el navegador; y revocar el jti en Redis corta la sesión de inmediato, sin esperar a que expire nada.
04Aislamiento multi-tenant
Mekovault es multi-tenant: varios clientes comparten la misma infraestructura. El aislamiento entre ellos se impone en capas independientes, de modo que un error en una no exponga datos de otro cliente.
| Capa | Control |
|---|---|
| Modelo de datos | Toda fila con datos de cliente lleva company_id. |
| Aplicación | Toda consulta filtra por la empresa que viene en el token de sesión. Nunca por un parámetro enviado por el cliente. |
| Base de datos | Row-Level Security de PostgreSQL como barrera adicional: aunque una consulta olvidara el filtro, la base de datos no devuelve filas de otra empresa. |
| Superadmin | Rol de plataforma separado de los roles de cliente. Sus acciones requieren confirmación por código y MFA verificado. |
| Resellers | Un reseller ve y opera solo sobre los tenants que administra. |
05Credenciales de proveedores y auditoría de uso
Para operar sobre su directorio, Mekovault recibe credenciales con permisos de administración: el JSON de una Service Account de Google con delegación a nivel de dominio. Son el activo más sensible que manejamos.
- Se almacenan cifradas en Infisical (self-hosted, en
mekovault-vault), en un project dedicado por tenant. - Nunca se escriben en la base de datos, en logs ni en respuestas de API. La base de datos guarda solo una referencia al secreto.
- Se cargan en memoria en el momento de llamar al proveedor y se descartan al terminar la operación.
Registro inmutable de cada uso
Cada llamada a Google con las credenciales del cliente deja una fila en la tabla credential_usage_log.
| Campo | Contenido |
|---|---|
| Tenant | company_id del cliente dueño de la credencial |
| Proveedor | Google Workspace |
| Operación | Acción ejecutada, por ejemplo create_user, suspend_user, add_group_member |
| Scopes | Scopes o permisos consumidos por esa operación |
| Quién | Usuario que originó la acción, o el proceso automático (worker, timer) que la ejecutó |
| Motivo | Referencia a la solicitud, ticket o tarea que justificó la llamada |
| Resultado | Éxito o fallo, con código de error si aplica |
| Latencia | Duración de la llamada al proveedor en milisegundos |
- La tabla tiene triggers de PostgreSQL que rechazan cualquier
UPDATEoDELETE. Ni el equipo de Mekovault puede editar o borrar una fila individual. - Cada fila tiene un id determinista derivado de la operación. Un reintento del mismo trabajo no genera una segunda fila, así el registro no se infla ni se contradice.
- Una ráfaga de fallos sobre las credenciales de un tenant dispara una alerta al equipo de operación.
Retención
| Parámetro | Valor |
|---|---|
| Mínimo | 24 meses |
| Máximo | 84 meses |
| Configuración | Por tenant, dentro de ese rango |
| Eliminación del tenant | Se borra junto con el tenant, o a los 24 meses, lo que ocurra primero |
| Purga | Semanal, solo de filas fuera del período configurado. Cada batch purgado deja constancia en la auditoría. |
Exportación para auditores
El registro se exporta en CSV o JSON. Cada archivo va firmado con HMAC-SHA256 e incluye el identificador de la clave usada, de modo que un auditor puede verificar que el archivo no fue alterado después de generarse.
06Permisos que Mekovault solicita
Mekovault pide únicamente los permisos que necesita para gestionar usuarios, grupos y unidades organizativas. No solicita acceso a correo, archivos, calendario ni contenido de ningún usuario.
| Scope | Para qué se usa |
|---|---|
admin.directory.user | Crear, suspender, reactivar y modificar cuentas de usuario; asignar contraseña inicial y forzar su cambio. |
admin.directory.group | Crear grupos y listas, y agregar o quitar miembros durante altas, cambios y bajas. |
admin.directory.orgunit | Leer y mover usuarios entre unidades organizativas según el flujo de onboarding u offboarding. |
Las llamadas se hacen con la Service Account impersonando al administrador delegado que el cliente designa al conectar el dominio. Ese administrador queda registrado en cada fila de credential_usage_log.
07Contraseñas iniciales y reset
- Cuando Mekovault crea una cuenta o resetea una contraseña, la contraseña temporal queda visible para el administrador que ejecutó la acción durante 15 minutos.
- Pasado ese plazo se purga del sistema. No se puede recuperar: si se perdió, se genera una nueva.
- Las contraseñas nunca se escriben en logs, eventos ni en el registro de uso de credenciales.
08Eliminación de un tenant
Un cliente puede pedir la eliminación completa de su tenant (derecho de supresión, RGPD art. 17 y Ley 21.719 de Chile). El proceso está diseñado para que las credenciales desaparezcan antes que cualquier otro dato.
- Un administrador del tenant solicita la eliminación. La solicitud se encola como job.
- El tenant queda inaccesible de inmediato: se rechazan sus sesiones y nuevos inicios de sesión.
- El job revoca los secretos del tenant y elimina su project en Infisical. Desde este punto Mekovault ya no puede llamar al directorio del cliente.
- Solo después se borran en cascada los datos del tenant en PostgreSQL: usuarios, solicitudes, tickets, configuración.
- La eliminación queda registrada en la auditoría de plataforma.
- Los administradores reciben un correo de confirmación con el detalle de lo eliminado.
09Addons: módulos con límites
Los conectores a proveedores y otras extensiones se empaquetan como addons. Un addon con un defecto no debe poder degradar la plataforma completa, y el registro de addons impone esa regla.
| Etapa | Control |
|---|---|
| Declaración | Cada módulo se describe en un manifiesto con sus puertos, rutas, tablas, migraciones y chequeo de salud. |
| Validación | Automática antes de activarlo: esquema del manifiesto, colisiones de puertos, rutas y tablas con otros módulos, migraciones aplicables y respuesta del chequeo de salud. |
| Ejecución | Corre con límites de memoria y CPU. Un módulo que se desborda se reinicia sin afectar a los demás. |
| Supervisión | Chequeo de salud cada 60 segundos, con resultado visible en el panel de operación. |
| Kill switch | Cualquier módulo puede desactivarse sin despliegue: en 15 segundos o menos sus rutas responden 503 y el resto de la plataforma sigue operando. |
10Perímetro y navegador
| Control | Efecto |
|---|---|
| HSTS | El navegador solo se conecta por HTTPS al dominio, incluso si el usuario escribe http. |
| Content-Security-Policy | Restringe los orígenes desde los que el portal puede cargar scripts, estilos y conexiones. |
| X-Content-Type-Options: nosniff | Impide que el navegador reinterprete el tipo de un archivo. |
| frame-ancestors 'none' | El portal no puede embeberse en un iframe de otro sitio (clickjacking). |
| CORS restringido | Solo los orígenes de Mekovault pueden llamar a la API desde un navegador. |
| Sin source maps en producción | No se publica el código fuente del frontend. |
| Sin documentación pública de API | Los esquemas OpenAPI de los servicios no están expuestos en producción. |
| Errores sin trazas | Las respuestas de error no incluyen stack traces ni detalles internos. |
11Operación y observabilidad
| Señal | Destino |
|---|---|
| Logs estructurados (JSON) | Loki, consultados desde Grafana |
| Métricas | Prometheus |
| Trazas distribuidas | OpenTelemetry hacia Tempo |
- Antes de cada despliegue se toma un backup de la base de datos.
- Los servicios se despliegan de forma controlada; un despliegue fallido se revierte al backup previo.
Timers de higiene
| Tarea | Qué hace |
|---|---|
| Purga de PII | Elimina datos personales que ya cumplieron su período de retención. |
| Cierre de tickets | Cierra tickets resueltos sin actividad después del plazo configurado. |
| Expiración de secretos | Avisa a los administradores 30, 7 y 0 días antes de que caduque una credencial de proveedor. |
| Purga del registro de uso | Semanal; elimina solo filas fuera de la retención configurada y deja constancia del batch. |
12Resumen de capas de seguridad
| Capa | Controles |
|---|---|
| Perímetro | Cloudflare, nginx como único proceso público, ufw |
| Red | Red privada de Lightsail, Tailscale para administración, servicios en loopback |
| Identidad | JWT RS256 de 15 min, refresh HttpOnly rotado y revocable, MFA TOTP con rate limit |
| Aplicación | Filtro por company_id del token en toda consulta |
| Datos | Row-Level Security en PostgreSQL 16 |
| Secretos | Infisical self-hosted, un project por tenant, nunca en DB ni logs |
| Auditoría | credential_usage_log inmutable, exportación firmada con HMAC-SHA256 |
| Módulos | Manifiesto validado, límites de recursos, supervisión cada 60 s, kill switch |
| Operación | Backups previos al despliegue, timers de higiene, logs, métricas y trazas |
13Lo que está en curso
Preferimos decir qué falta antes de que lo pregunte un cuestionario. Estos puntos están en trabajo y no deben asumirse como vigentes hasta que esta página los mueva a la sección correspondiente.
| Ítem | Estado |
|---|---|
ENVIRONMENT=production en todos los servicios (hoy algunos corren con la configuración de desarrollo endurecida) | En curso |
| Alertas de operación hacia Slack | En curso |
| Rol de PostgreSQL dedicado por addon, para que cada módulo acceda solo a sus tablas | En curso |
| SOC 2 | No planificado por ahora; el marco se usa como guía de controles |
FAQPreguntas de auditoría frecuentes
Respuestas directas a lo que suelen preguntar los equipos de seguridad y los auditores.
- ¿Dónde viven mis credenciales?
- En Infisical, una instancia self-hosted en la máquina
mekovault-vault, dentro de la red privada de AWS Lightsail en us-east-1. Cada tenant tiene su propio project. La base de datos guarda solo una referencia al secreto, nunca su valor. - ¿Quién puede verlas?
- Ninguna persona las ve en operación normal. Los servicios las cargan en memoria en el momento de llamar al proveedor y las descartan al terminar. El acceso a la máquina del vault exige Tailscale, y ese acceso está restringido al equipo de plataforma. Las contraseñas temporales que Mekovault genera son visibles al administrador del cliente durante 15 minutos y luego se purgan.
- ¿Qué pasa si dejo Mekovault?
- Un administrador solicita la eliminación del tenant. El acceso se corta de inmediato; luego se revocan los secretos y se borra el project de Infisical; solo después se borran en cascada los datos en PostgreSQL. Queda registro en la auditoría de plataforma y los administradores reciben un correo con el detalle. Los sub-encargados con datos residuales están listados en la página de sub-encargados.
- ¿Cómo pruebo que no usaron mis permisos para otra cosa?
- Con
credential_usage_log: cada llamada a Google con sus credenciales queda registrada con operación, scopes, quién la originó, motivo, resultado y latencia. La tabla no admite UPDATE ni DELETE. Puede exportarla en CSV o JSON firmado con HMAC-SHA256 y verificar la firma con el id de clave incluido. - ¿Un token de sesión robado sirve para algo?
- El access token caduca en 15 minutos. El refresh token vive solo en una cookie HttpOnly, se rota en cada uso y se puede revocar por
jtien Redis. Las operaciones sensibles exigen además MFA verificado en esa sesión. - ¿Un módulo con fallas puede tumbar la plataforma?
- No está diseñado para poder hacerlo. Cada addon corre con límites de memoria y CPU, se supervisa cada 60 segundos y tiene un kill switch que lo desactiva en 15 segundos o menos sin despliegue. El resto de servicios sigue operando.