Doce construcciones

Un homeserver de Matrix cuya federación realmente funciona

Synapse en una pequeña instancia Ryzen con Postgres, delegación hecha mediante archivos well-known en lugar de registros SRV, y verificación que alcanza a otros servidores.

Qué construye esto

Un servidor doméstico en example.com, ejecutándose en matrix.example.com, federando correctamente con los demás. Postgres debajo, nginx delante, la delegación gestionada por dos pequeños archivos JSON, y el registro cerrado para que tu servidor no se convierta en el relé gratuito de alguien en una semana.

La federación es donde estos despliegues suelen fallar, y casi nunca por culpa de Synapse. El servidor funciona, los mensajes locales van, y luego nada de fuera llega. En la práctica, la causa casi siempre es la delegación: la identidad del servidor y la dirección donde vive son dos cosas diferentes, y el mecanismo que las conecta es fácil de configurar mal sutilmente.

Antes de empezar

  • Un R-4 es suficiente para unas pocas decenas de usuarios. Synapse es más voraz de memoria que de CPU, y unirse a habitaciones federadas grandes es lo único que le hará sudar.
  • Dos nombres en DNS: example.com, que es la identidad de tu servidor y aparece en cada id de usuario, y matrix.example.com, que es donde el software realmente escucha.
  • Certificados para ambos. El primero solo necesita servir dos archivos estáticos.

1. Base de datos, con la collation correcta

apt update && apt install -y postgresql matrix-synapse nginx certbot
sudo -u postgres psql -c "CREATE USER synapse_user WITH PASSWORD '<a long password>';"
sudo -u postgres psql -c "CREATE DATABASE synapse ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' TEMPLATE=template0 OWNER synapse_user;"

La collation no es un detalle. Synapse se niega a arrancar en una base de datos creada con una collation distinta de C, y el error que imprime cuando te equivocas llega después de que ya hayas importado datos. Hazlo ahora, correctamente, de una vez.

2. Configuración del homeserver

El paquete de Debian pide el nombre del servidor durante la instalación; responde example.com, no el hostname de la máquina. Esa respuesta se convierte en parte de cada id de usuario y de habitación en el servidor y no se puede cambiar después sin abandonar el servidor.

Luego edita /etc/matrix-synapse/homeserver.yaml:

server_name: "example.com"
public_baseurl: "https://matrix.example.com/"
pid_file: /run/matrix-synapse.pid

listeners:
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    bind_addresses: ['127.0.0.1']
    resources:
      - names: [client, federation]
        compress: false

database:
  name: psycopg2
  args:
    user: synapse_user
    password: "<a long password>"
    database: synapse
    host: 127.0.0.1
    cp_min: 5
    cp_max: 10

enable_registration: false
registration_shared_secret: "<a long random string>"
report_stats: false
suppress_key_server_warning: true
url_preview_enabled: false

media_store_path: /var/lib/matrix-synapse/media
max_upload_size: 100M
media_retention:
  remote_media_lifetime: 30d

rc_message:
  per_second: 0.5
  burst_count: 15

Dos de esos ajustes te ahorrarán disco y quebraderos. La retención de medios remotos descarta copias en caché de los archivos de otros servidores después de un mes, y sin ella tu almacén de medios crece para siempre con contenido que nunca pediste. Las vistas previas de URL están desactivadas porque hacen que tu servidor busque direcciones arbitrarias en nombre de cualquiera que pueda publicar un enlace, lo cual es un motor de falsificación de peticiones con una interfaz de chat pegada.

x_forwarded: true también importa: sin él, Synapse limita la velocidad a todos los usuarios del mundo como si fueran un solo cliente, porque cada petición parece venir de nginx.

3. Delegación

Tu identidad es example.com y tu servidor está en matrix.example.com. Dos archivos cierran la brecha. Sírvelos desde example.com, sobre HTTPS, con el tipo de contenido correcto.

/.well-known/matrix/server:

{ "m.server": "matrix.example.com:443" }

/.well-known/matrix/client:

{ "m.homeserver": { "base_url": "https://matrix.example.com" } }

El puerto en el primer archivo es obligatorio. Si lo omites, otros servidores recurren al puerto 8448, no encuentran nada escuchando, y se rinden silenciosamente; luego tus usuarios informan que la federación está rota mientras que todos los registros de tu máquina tienen un aspecto perfectamente sano.

server {
  listen 443 ssl;
  server_name example.com;
  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  location /.well-known/matrix/ {
    root /var/www/wellknown;
    default_type application/json;
    add_header Access-Control-Allow-Origin *;
  }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name matrix.example.com;
  ssl_certificate     /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
  client_max_body_size 100m;

  location ~ ^(/_matrix|/_synapse/client) {
    proxy_pass http://127.0.0.1:8008;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $host;
  }
}

La cabecera Access-Control-Allow-Origin en la ubicación well-known es requerida por los clientes web y olvidada por casi todos. Sin ella, los clientes de escritorio funcionan y los clientes de navegador no pueden encontrar tu servidor en absoluto.

mkdir -p /var/www/wellknown/.well-known/matrix
systemctl restart matrix-synapse nginx

4. Un usuario

register_new_matrix_user -c /etc/matrix-synapse/homeserver.yaml http://127.0.0.1:8008

Responde a las indicaciones, haz que la primera cuenta sea de administrador, y deja el registro abierto apagado. Un homeserver con registro abierto es una fuente de spam en cuestión de días, y otros servidores empezarán a rechazar tu tráfico mucho antes de que te des cuenta.

Verifícalo

Trabaja hacia afuera. Primero, ¿está respondiendo el software?

curl -s https://matrix.example.com/_matrix/client/versions | head -c 200
curl -s https://matrix.example.com/_matrix/federation/v1/version

Después, ¿se está sirviendo la delegación correctamente, con el puerto presente y el tipo de contenido correcto?

curl -si https://example.com/.well-known/matrix/server | grep -E "content-type|m.server"
curl -s https://example.com/.well-known/matrix/client

Luego, ¿resuelve tu clave de firma? Otros servidores la buscan antes de hablar contigo:

curl -s https://matrix.example.com/_matrix/key/v2/server | head -c 300

Finalmente, la única prueba que cuenta. Inicia sesión con un cliente, únete a una habitación pública alojada en un homeserver que no sea el tuyo, y envía un mensaje. Luego lee el estado de la federación desde la API de administración, usando el token de acceso que tu cliente muestra en sus ajustes:

curl -s -H "Authorization: Bearer <admin token>" \
  "https://matrix.example.com/_synapse/admin/v1/federation/destinations" | head -c 400

Cada destino debe mostrar una transacción exitosa reciente y sin intervalo de reintento. Los destinos con retry_interval creciente son servidores a los que no puedes llegar; si todos los destinos parecen así, el problema es la delegación más que la red, y el paso tres es donde hay que mirar.

journalctl -u matrix-synapse --since "10 min ago" | grep -i "federation" | tail -20

Después

Unirse a una habitación muy grande la primera vez fijará un núcleo durante varios minutos y traerá mucho estado. Eso es normal, ocurre una vez por habitación, y es la razón principal por la que la gente concluye que Synapse es lento. Voz y vídeo necesitan un servidor TURN junto a este; coturn en la misma instancia maneja una comunidad pequeña, aunque quiere su propio rango de puertos UDP en el cortafuegos. La federación es charlatana en ambas direcciones, lo cual vale la pena recordar al elegir el sitio: nuestro índice de ubicaciones lista los tiempos de ida y vuelta que medimos nosotros mismos.

Listo cuando tú lo estés

Elige una ciudad. Elige un tamaño. Paga con monedas.

Sin fórmulas sobre quién eres, sin esperar a que un humano te apruebe, sin llamada telefónica para verificar nada. La factura se liquida y las credenciales llegan a tu bandeja de entrada.