TPTito Pinardo Gutiérrez
← Volver al rack
English
1U · MATRIXSynapse · Element · E2EE En producción

Posición 6 de 25 en el rack · En producción

Unidad 1U · Comunicación

Matrix · mi servidor de mensajería

Privado de verdad: sin registro ni federación

Un servidor de chat propio, cerrado: nadie puede registrarse y no habla con otros servidores. Desde el móvil hablo con Hermes, recibo los avisos importantes del homelab cifrados de extremo a extremo y me entero de los mensajes de esta web.

Por qué propio
Los avisos del servidor hablan de mi infraestructura y Hermes lee mi correo. Eso no tenía que pasar por un servicio de terceros.
Cómo está montado
Synapse con su PostgreSQL en un contenedor, Element en otro: el que tiene estado no se mezcla con un sitio estático. Paquetes oficiales, sin Docker.
Quién escribe
Cada bot con su usuario sin permisos de administración: el de alertas, Hermes y el de esta web. Si uno cae, no arrastra a los demás.
  • 0registros abiertos y servidores federados
  • E2EEen las salas de alertas, verificado en los eventos
  • 3bots, cada uno con su usuario y su sala
  • 5/5avisos fallidos que nadie había visto

Su código, en abierto Versión pública del repositorio: sin IPs ni secretos.

La mensajería por dentro

Aquí llegan Hermes, los avisos del servidor y los mensajes de esta web. Elige un recorrido.

Hermesde visita en la unidad

Pulsa una pieza o elige un recorrido. Si vienes de una habilidad, ya están encendidas las piezas donde la uso.

      Hablo con Hermes
      1. Element en el móvil — Escribo en Element desde el móvil, fuera de casa y sin VPN.
      2. Cloudflare — Cloudflare recibe la petición.
      3. Traefik — Traefik la pasa al servidor de Matrix.
      4. Solo la API de cliente — Solo existe la API de cliente: es lo que usa el móvil.
      5. Synapse — Synapse recibe el mensaje.
      6. PostgreSQL local — Lo guarda en su PostgreSQL.
      7. Synapse — Y lo reparte a los dispositivos de la sala.
      8. Sala del asistente — La sala del asistente.
      9. Hermes — Hermes lo recibe y empieza su turno.
      Una alerta grave
      1. Monitorización — La monitorización detecta un problema de severidad alta.
      2. Relay de alertas — Lo pasa al relay local, con un secreto compartido para que nadie de la red pueda inyectar avisos falsos.
      3. Claves del dispositivo — El relay lo cifra con las claves de su dispositivo.
      4. Synapse — Synapse lo guarda y lo reparte cifrado: solo los dispositivos de la sala pueden leerlo.
      5. Sala de alertas — Llega a la sala de alertas.
      6. Element en el móvil — Me suena el móvil.
      Alguien me escribe por la web
      1. Esta web — Alguien envía el formulario de esta web.
      2. Bot de esta web — El bot propio de la web manda el aviso.
      3. Synapse — Synapse confirma con un identificador de evento. Sin esa confirmación, la web lo reintenta.
      4. Sala de la web — Llega a la sala de la web.
      5. Element en el móvil — Lo veo en el móvil.
      Alguien husmea el servidor
      1. Element en el móvil — Alguien en internet abre el dominio de la mensajería.
      2. Traefik — Traefik lo recibe.
      3. Página de prueba bloqueada — Busca la página de «está funcionando»: recibe un 404.
      4. Federación apagada — Intenta hablar como otro servidor de Matrix: ese servicio no existe.
      5. Registro cerrado — Intenta registrarse: el registro está cerrado. Solo puede ver la pantalla de inicio de sesión.
      Ansible monta el servidor
      1. Configuración por encima — Ansible despliega un fichero de ajustes al lado de la configuración que generó el paquete, sin tocar sus claves.
      2. Registro cerrado — Registro cerrado y secreto de alta, desde el vault.
      3. Synapse — Synapse arranca y fusiona las dos configuraciones.
      4. PostgreSQL local — Conectado a su PostgreSQL local.
      5. Element Web — Element se despliega en su propio contenedor, mínimo.
      1. Element en el móvil

        Desde aquí hablo con Hermes y recibo los avisos, sin VPN.

      2. Cloudflare

        Delante del dominio de la mensajería.

      3. Traefik

        La puerta pública: solo deja pasar lo que el cliente necesita.

        • Traefik
      4. Monitorización

        Manda sus problemas graves a la sala de alertas.

        • Zabbix
      5. Seguridad

        Manda los eventos de nivel alto, agrupados para no inundar.

        • Wazuh
      6. Esta web

        Avisa cuando alguien escribe por el formulario.

      7. Element Web

        El cliente web, servido por nginx en un contenedor mínimo. Sin estado: se puede reconstruir en minutos.

        • Linux (Debian, Alpine, Arch)
      8. Página de prueba bloqueada

        Synapse enseña de serie una página de «está funcionando» que no se puede desactivar. Una regla de Traefik la reescribe para que dé 404 sin tocar lo que necesita el cliente.

        • Traefik
        • Linux / SSH hardening
      9. Solo la API de cliente

        El servidor solo sirve lo que usan las aplicaciones. Ni siquiera expone la parte que habla con otros servidores.

        • Linux / SSH hardening
      10. Federación apagada

        No es un filtro: el servicio de federación no existe. Ningún otro servidor puede hablar con este.

        • Linux / SSH hardening
      11. Synapse

        El servidor de referencia de Matrix, del paquete oficial.

        • Linux (Debian, Alpine, Arch)
      12. PostgreSQL local

        Historial, salas y claves de dispositivos. Local porque es solo de Synapse: no hay que compartirlo.

        • PostgreSQL
      13. Configuración por encima

        Ansible no toca el fichero principal, que guarda las claves que genera el paquete: despliega solo un fichero de ajustes que Synapse fusiona.

        • Ansible
      14. Registro cerrado

        Las cuentas solo se crean con un secreto que vive cifrado en el vault de Ansible.

        • Ansible
        • Linux / SSH hardening
      15. Relay de alertas

        Un demonio con su propio dispositivo Matrix. Las herramientas de monitorización le pasan el aviso y él lo envía cifrado de verdad.

        • Python
      16. Claves del dispositivo

        Para cifrar hace falta un dispositivo con sesiones vivas. Por eso el relay sincroniza todo el rato: si me uno a una sala más tarde, se entera.

        • Python
      17. Hermes

        El asistente, con su propio usuario. Solo responde en la sala a la que lo invité primero.

        • Python
      18. Bot de esta web

        Un usuario propio que solo puede escribir en su sala. Si alguien comprometiera la web, el token no sirve para nada más.

      19. Sala de alertas

        Solo lo grave: severidad alta en la monitorización y nivel 12 o más en seguridad.

      20. Sala del asistente

        Donde hablo con Hermes.

      21. Salas por tema

        Una por vigilante (copias, certificados, red…), más correo y eventos. Cada una solo habla cuando hay algo.

      22. Sala de la web

        Los mensajes del formulario de contacto.

      Lo que uso de mi árbol

      Pulsa una habilidad: se encienden en el mapa las piezas donde la uso y te cuento para qué.

      Terraform
      Crea los contenedores de Synapse y de Element.
      Ansible
      Despliega Synapse con su configuración por encima de la del paquete, cierra el registro y guarda el secreto de alta cifrado.
      LXC
      Synapse y Element en contenedores separados: el que tiene estado no se mezcla con el estático.
      Linux (Debian, Alpine, Arch)
      Paquetes oficiales y nginx con systemd, sin Docker.
      Traefik
      La puerta pública, con una regla dedicada que esconde la página de prueba de Synapse.
      Zabbix
      Manda sus problemas de severidad alta a la sala de alertas a través del relay.
      Wazuh
      Manda sus eventos de nivel 12 o más, agrupados para no repetir.
      Linux / SSH hardening
      Registro cerrado, federación inexistente, solo la API de cliente, página de prueba oculta y bots sin administración.
      Python
      El relay de alertas con matrix-nio: sesión persistente, sincronización continua y un endpoint local protegido con secreto.
      PostgreSQL
      La base de datos de Synapse, local y solo suya.

      Bitácora de incidencias

      Averías reales. Lee el síntoma, elige la causa que creas y mira cómo se arregló.

      1. #1

        Un canal de alertas que nunca había funcionado

        17-09-2026 · alertas

        El canal de alertas graves hacia Matrix llevaba dos días creado. El relay estaba encendido y probado. Pero al revisar el histórico real, sus 5 avisos habían fallado todos.

        ¿Qué crees que pasaba?

        Ver la solución
        Causa
        Al crear el canal en Zabbix faltaron las plantillas de «problema», «recuperado» y «actualización». Sin texto que generar, Zabbix ni siquiera invocaba el script, y el error solo se veía en su histórico.
        Solución
        Añadir las tres plantillas con el mismo formato que el canal antiguo y probar con un aviso real, no con un «el bot está encendido».
        Lección
        Que un componente esté encendido no dice que funcione. Para saber si un canal de alertas funciona hay que mirar si llegaron las alertas.
      2. #2

        «No se puede descifrar el mensaje»

        17-09-2026 · cifrado

        Un audio de Hermes apareció en Element como «Unable to decrypt message», en el móvil y en el PC. Reiniciar el bot no lo arreglaba: los siguientes mensajes cifrados también fallaban.

        ¿Qué crees que pasaba?

        Ver la solución
        Causa
        Un script que mandó un mensaje «desde fuera» abrió el almacén de cifrado del bot mientras el proceso seguía corriendo, y dejó corrupto en disco su estado de sesiones. Cada reinicio volvía a cargar el mismo estado roto.
        Solución
        Poner en cuarentena el almacén corrupto (sin borrarlo) y dejar que el bot entrara con un dispositivo nuevo desde cero. Lo volví a verificar en Element; aquel audio se perdió.
        Lección
        El estado de cifrado es de un solo proceso. Nunca se toca sin parar el servicio y comprobar que de verdad se paró.
      3. #3

        «No se encuentra el servidor» recién creado

        15-09-2026 · DNS

        Acababa de crear el registro público del cliente web. Resolvía bien en cualquier DNS público, pero desde casa el navegador decía que no existía.

        ¿Qué crees que pasaba?

        Ver la solución
        Causa
        Alguien había preguntado por ese nombre antes de que existiera, y el DNS de casa guardó la respuesta negativa. Reiniciar el servicio no basta: esa caché se guarda en disco y se recarga.
        Solución
        Vaciar la caché por su API. Desde entonces, al crear un registro nuevo, es un paso más del procedimiento.
        Lección
        Las cachés también guardan los «no». Y una caché persistente sobrevive al reinicio que debería limpiarla.

      Cómo se construyó

      1. 15-09-2026

        Cliente y servidor

        • Element Web en un contenedor mínimo con nginx.
        • Synapse con su PostgreSQL en otro, privado: sin registro ni federación.
        • Todo creado con Terraform y configurado con Ansible.
      2. 16 y 17-09-2026

        Avisos cifrados

        • Relay con dispositivo propio para enviar avisos cifrados de extremo a extremo.
        • Monitorización y seguridad conectadas, con umbrales más estrictos que el canal antiguo: solo lo que hay que revisar.
        • El canal arreglado de verdad tras mirar su histórico.
      3. Septiembre y octubre de 2026

        Un espacio para la casa

        • Salas por tema para Hermes y sus vigilantes.
        • El bot propio de esta web, en su sala.
      4. 15 y 18-10-2026

        Avisos ordenados y un diagnóstico

        • Salas aparte para el CI/CD y CrowdSec: la de incidencias solo recibe lo que hay que mirar.
        • Un bot que, al pedírselo, recorre la cadena de un servicio (DNS, Cloudflare, router, la puerta, el contenedor) y dice el primer punto que falla. Todo con permisos de solo lectura.

      Decisiones (y por qué)

      • Qué servidor

        Synapse, el de referencia

        El más maduro y documentado. El argumento clásico de la alternativa, gastar menos, no pesa en este servidor.

      • ¿Público o detrás de la VPN?

        Público, pero cerrado

        El objetivo era usarlo desde el móvil sin VPN. Se compensa con registro cerrado, federación apagada de verdad y solo la API de cliente expuesta.

      • Un bot para todo o uno por función

        Uno por función, sin administración

        El relay de alertas, Hermes y el bot de la web tienen usuarios distintos. Si uno se compromete, solo puede escribir en lo suyo.

      Lo que aprendí

      • Comprobar los canales de aviso por lo que llega, no por lo que está encendido.
      • El cifrado de extremo a extremo tiene estado: se trata como una base de datos de un solo proceso.
      • Público no tiene por qué significar abierto: lo que no se sirve no se puede atacar.

      Lo siguiente

      • Avisos de «recuperado» en la sala de alertas, para cerrar cada problema.
      • Copias del almacén de claves de los bots, con el servicio parado.

      ¿Hablamos?

      Puedo contar cualquiera de estas decisiones en detalle en una entrevista.

      Descargar CV Contactar ← Volver al rack

      Reconstrucción a partir de mi documentación: sin dominios, salas, usuarios ni direcciones.