Gabriel Cachadiña

Servidor de XMPP en NixOS

· Gabriel Cachadiña

Recientemente se ha realizado una denuncia a Whatsapp por, presuntamente, mentir sobre la encriptación que ofrecen. Siempre hay que desconfiar de cualquier programa que no sea abierto y desconfiar 2 veces si se trata de algo que toca directamente nuestro derecho a la privacidad. No me baso en que Whatsapp mienta o no, sino que no podemos verificarlo. Hace ya varios años he tenido como meta alejarme lo máximo posible de las compañias que controlan toda nuestra vida, ya sea corriendo servicios que nos independizan de plataformas de streaming, usando programas como Syncthing para sincronizar nuestra información de forma anónima, VPNs para tunelar nuestra información,… Es solo natural que ahora quiera evolucionar para tener una mensajería segura y privada. Por esto es que he decidido correr en mi VPS mi propia instancia de XMPP.

¿Por qué XMPP?

Estos han sido los criterios que he utilizado para escoger una solución a mi problema:

Configuración

Esta configuración está inspirada en el blog de rokibulroni, su guía es mucho más general, ya que permite correr el servidor en cualquier* distribución, mientras que yo mostraré la configuración para un servidor que use la distribución NixOS.

Prosody

El servidor de prosody correrá dentro de un contenedor docker:

 1virtualisation.docker.enable = true;
 2virtualisation.oci-containers = {
 3backend = "docker";
 4containers.prosody = {
 5  image = "prosodyim/prosody:13.0";
 6  environment = { TZ = "Europe/Madrid"; };
 7  volumes = [
 8	"/home/gabriel/Docker/Prosody/data:/var/lib/prosody"
 9	"${FullchainPEM}:/certs/fullchain.pem:ro"
10	"${keyPEM}:/certs/key.pem:ro"
11	"${prosodyConfig}:/etc/prosody/prosody.cfg.lua:ro"
12  ];
13  networks = [ "host" ];
14  autoStart = true;
15};
16};

Denotar que el lector deberá configurar las llaves que usará el servidor (definidas en el siguiente punto) y además de eso requerirá de añadir la configuración del servidor prosody.cfg.lua. Recomiendo usar la configuración de ejemplo y/o mi propia documentación encontrada en mi repositorio.

Denotar que la imagen es prosodyim/prosody:13.0, ya que prosody/prosody no recibe actualizaciones automáticas y lleva 4 años sin actualizarse.

Certificados

A continuación se muestra una forma de generar los certificados necesarios para el servidor:

 1services.nginx.enable = true;
 2services.nginx.virtualHosts."xmpp.gabrielcachadina.com" = {
 3  addSSL = true;
 4  enableACME = true;
 5  serverAliases = [
 6    "upload.xmpp.gabrielcachadina.com"
 7    "conference.xmpp.gabrielcachadina.com"
 8  ];
 9
10  # Optionally proxy WebSocket/File share if needed
11  locations."/" = {};
12};
13security.acme = {
14  acceptTerms = true;
15  defaults.email = "gabrielcachadina@protonmail.com";
16};

En la primera parte se definen los dominios utilizados, en mi caso usaré el subdominio xmpp para XMPP, mientras que los subsubdominios upload.xmpp y conference.xmpp serán usado para poder subir archivos a los distintos chats y para crear grupos respectivamente.

Para leer los certificados desde el contenedor de docker se podrán establecer las siguientes variables:

 1certDir = "/var/lib/acme/xmpp.gabrielcachadina.com"; # Real certificates
 2
 3# Read the ACME files at evaluation time and write to Nix store
 4FullchainPEM =
 5  if builtins.pathExists "${certDir}/fullchain.pem"
 6  then pkgs.writeText "fullchain.pem" (builtins.readFile "${certDir}/fullchain.pem")
 7  else "/dev/null";
 8
 9keyPEM =
10  if builtins.pathExists "${certDir}/key.pem"
11  then pkgs.writeText "key.pem" (builtins.readFile "${certDir}/key.pem")
12  else "/dev/null";

Se tiene un condicional debido a que la primera vez que se corra el comando nixos-rebuild switch los certificados no existirán y el compilado fallará.

Puertos

Finalmente se deberán abrir los siguientes puertos para conseguir una comunicación servidor-cliente y servidor-servidor:

Dominio

Por supuesto habrá que configurar el subdominio y subsubdominio para que apunte a nuestro servidor, para que los certificados se puedan crear de forma correcta.

Una vez todo esto esté configurado tendremos nuestro servidor corriendo dentro de nuestro dominio.

Cuentas

Para generar una cuenta dentro del contenedor deberemos ejecutar el comando:

1sudo docker exec -it prosody /bin/bash
2prosodyctl adduser gabriel@xmpp.gabrielcachadina.com

Para que el segundo comando funcione deberemos haber configurado el contenedor para usar el módulo admin_shell, si esto no es así no tendremos acceso al shell de nuestro cliente.

Clientes

Una vez todo esto esté configurado tendremos un servidor operativo. Para probarlo recomiendo los clientes Dino para GNU/Linux y Conversations para Android.

Nos registraremos con el usuario y contraseñas configurados actualmente y podremos chatear con cualquier persona dentro de la red XMPP. Cuando la conversación empiece NO estará encriptada, por lo que el/los servidores podrán ver la información que se está enviando.

Esto puede parecer un fallo crucial, ¿Cómo que no estamos encriptando nuestra información?¿ No se supone que XMPP es E2EE? Si lo es, pero necesitamos intercambiar llaves primero, un proceso similar al explicado en MQTTs. XMPP es un protocolo federado. La E2EE depende de extensiones como OMEMO, que implementan clientes modernos. Este proceso es un intercambio de llaves públicas voluntario, pero en los clientes anteriormente mencionados aparecerá un candado que indica si la comunicación es o no E2EE, evitando posibles errores por parte del cliente.

Con esto, ahora sí, tendremos configurado un servidor XMPP con el que podremos chatear fuera y dentro de nuestro dominio. El contenido de los mensajes no es accesible al servidor, pero sí ciertos metadatos como quién habla con quién y cuándo1.

¿Quieres probarlo? Ahora mismo tengo mi propio servidor configurado dentro de la red XMPP, escríbeme en gabriel@xmpp.gabrielcachadina.com o si prefieres validar tu servidor he encontrado una página fantástica que permite evaluar el cumplimiento de las especificaciones de tu servidor.

XMPP:Example


  1. Esto se podría reducir si se quitaran aún más módulos dentro de la configuración XMPP (hasta cierto punto), pero threat model personal no requiere de tanto. En el peor de los casos ningún usuario podría tener acceso a nuestros mensajes siempre que estén cifrados por OMEMO antes de que se realice el ataque. ↩︎

#self-hosted #gnu/linux

Reply to this post by email ↪