Aller au contenu

Matrix et les ponts mautrix

Comment DNF implémente le serveur Matrix (Synapse) et les ponts mautrix. Pour l’exploitation au quotidien, voir Matrix et les ponts.

Tout est porté par dnf/modules/service/matrix.nix, avec deux modules satellites : element.nix (client web statique) et turn.nix (relais coturn pour l’audio/vidéo).

Diagram
  • Caddy route /_matrix/* et /_synapse/client/* vers Synapse et sert les fichiers /.well-known/matrix/{client,server} (découverte des clients et fédération).
  • Synapse écoute sur dnfConfig.network.ports.matrix, base PostgreSQL locale (créée avec collation C par une unité matrix-db-init).
  • Kanidm fournit l’authentification : client OIDC provisionné via darkone.service.idm.oauth2.matrix, local part dérivée du preferred_username (voir Authentification & IDM).
  • coturn est branché automatiquement si le service turn est déclaré (turn_uris, secret partagé via sops).

matrix.nix est un lib.mkMerge de blocs indépendants :

BlocConditionContenu
Basetoujoursclient OIDC, reverse proxy, persistance
Serveurmatrix.enableSynapse, PostgreSQL, secrets communs, appservice doublepuppet
Un bloc par pontmatrix.enable && bridges.<x>.enablesecrets sops + service mautrix du pont

Chaque pont est donc débrayable individuellement (darkone.service.matrix.bridges.{whatsapp,signal,telegram,messenger,discord}.enable), ses secrets sops n’étant déclarés que si le pont est actif.

DNF s’appuie sur les modules nixpkgs services.mautrix-*, qui génèrent la registration appservice et l’enregistrent auprès de Synapse au démarrage. Deux générations de ponts cohabitent, avec des clés de configuration différentes :

PontGénérationDouble puppetingNiveau
whatsappbridgev2 (Go)double_puppet.secretsuser
signalbridgev2 (Go)double_puppet.secretsuser
messenger (meta)bridgev2 (Go)double_puppet.secretsuser
telegramlegacy (Python)bridge.login_shared_secret_mapfull
discordlegacy (Go)bridge.login_shared_secret_mapuser

Le helper mkBridgePermissions (dans matrix.nix) construit la même politique pour tous les ponts :

permissions:
"domain.tld": user # tout compte local, avec son propre compte distant
"@alice:domain.tld": admin # le matrix.admin déclaré dans etc/config.yaml

Les sessions distantes sont isolées par utilisateur Matrix : la généralisation à tous les comptes du domaine est sans risque d’interférence.

Méthode officielle appservice 🡕 : un appservice doublepuppet est enregistré dans Synapse (app_service_config_files) avec un namespace couvrant @.*:domain.tld. Son as_token autorise chaque pont à émettre des événements au nom du vrai compte de l’utilisateur.

Diagram

Le token est unique pour tous les ponts (mautrix-doublepuppet-as-token) et injecté dans chaque config par substitution de variables d’environnement (as_token:$MAUTRIX_DOUBLEPUPPET_AS_TOKEN), les modules nixpkgs passant la configuration par envsubst avec le environmentFile du service.

La registration d’un appservice est le contrat entre Synapse et le pont. Elle contient deux tokens, un par sens d’authentification :

TokenSensUsage
as_tokenpont → Synapsele pont s’authentifie sur l’API client (envoi de messages, sync…)
hs_tokenSynapse → pontSynapse s’authentifie quand il pousse les événements au pont (/transactions)

Par défaut, les modules nixpkgs génèrent ces tokens aléatoirement au premier démarrage et les stockent dans /var/lib/mautrix-*. Conséquence : toute réinitialisation du répertoire d’état change la paire de tokens, alors que Synapse — qui ne lit les registrations qu’au démarrage — garde l’ancienne en mémoire ; le pont est alors rejeté (« The as_token was not accepted »).

DNF fixe donc les deux tokens de chaque pont via sops : la registration devient une fonction pure des secrets. Réinitialiser l’état d’un pont n’invalide plus rien côté Synapse, et l’ensemble est reproductible (redéploiement, migration, restauration de sauvegarde).

  • Secrets : déclarés par bloc dans matrix.nix (sops.secrets.*), et assemblés en fichiers d’environnement par des sops.templates.* possédés par l’utilisateur système du pont. Liste complète dans la page d’exploitation.
  • Ports : matrix, matrixTelegram et matrixDiscord sont fixés par le registre dnf/config/network.nix ; les ports appservice par défaut des autres ponts (29318, 29319, 29328) y sont listés en reserved pour éviter toute collision future.
  • Statuts WhatsApp : network.enable_status_broadcast = false dans la config du pont, sinon le bridge recrée indéfiniment le salon « WhatsApp Status Broadcast » pour chaque utilisateur.