Matrix et les ponts de messagerie
Activer le serveur Matrix (Synapse), son client web Element et les ponts mautrix (WhatsApp, Signal, Telegram, Messenger, Discord). Pour l’architecture, voir Matrix dans DNF.
Activer Matrix
Section intitulée « Activer Matrix »-
Déclarer les services sur l’hôte cible dans
etc/config.yaml:etc/config.yaml services:matrix:title: "Matrix"description: "Messagerie instantanée"element: # client webturn: # relais audio/vidéo des clients historiques -
Déclarer les administrateurs Matrix dans la section
network(hors hôtes) :etc/config.yaml network:matrix:admins: ["alice", "bob"] # local parts des administrateursUne seule liste pour les deux administrations, appliquée au rebuild : les ponts et le serveur. Ces comptes reçoivent aussi les alertes de supervision.
-
Créer les secrets requis (voir tableau ci-dessous), puis déployer.
L’authentification passe par le SSO Kanidm (OIDC) : tout utilisateur déclaré se connecte avec son compte réseau, aucune création de compte Matrix n’est nécessaire.
Secrets requis
Section intitulée « Secrets requis »Tous internes : just configure-admin-host les crée dans
usr/secrets/secrets.yaml, sans jamais écraser l’existant.
| Clé sops | Rôle |
|---|---|
oidc-secret-matrix | Secret du client OIDC Kanidm (cf. SSO) |
turn-secret | Secret partagé coturn (si turn activé) |
livekit-secret | Secret partagé MatrixRTC (si matrixRtc activé) |
mautrix-doublepuppet-as-token | Token appservice double puppeting (commun aux ponts) |
mautrix-doublepuppet-hs-token | Idem, requis par Synapse, jamais utilisé |
Authentification (MAS)
Section intitulée « Authentification (MAS) »Toute l’authentification est déléguée au Matrix Authentication Service 🡕 (MAS), sans option : c’est le composant qui manque à Synapse pour Element X 🡕, la connexion par QR code et le portail de compte, et l’authentification interne de Synapse est abandonnée en amont (MSC3861). Kanidm reste la source d’identité : MAS devient son client OIDC à la place de Synapse.
Pour la place de MAS dans l’architecture, voir Matrix dans DNF.
| Sujet | Où ça se passe |
|---|---|
| Connexion SSO | MAS, client OIDC de Kanidm |
| Portail de compte | https://matrix.domain.tld/account/ |
| Jetons d’inscription | dnf-mas sur l’hôte |
| Administrateurs serveur | network.matrix.admins, déclaratif |
| Comptes, sessions, salons | interface Ketesa |
Secrets de MAS
Section intitulée « Secrets de MAS »-
Générer les trois secrets dédiés (idempotent, n’écrase jamais l’existant) :
Fenêtre de terminal just configure-admin-hostClé sops Rôle mas-encryption-secretChiffrement interne MAS mas-synapse-secretSecret partagé MAS ↔ Synapse mas-rsa-private-keyClé de signature des tokens -
Déployer. Sur un serveur neuf, c’est terminé. Sur un serveur qui a déjà des comptes, enchaîner sur la migration sans laisser personne se connecter entre les deux.
Migrer un serveur déjà en production
Section intitulée « Migrer un serveur déjà en production »MAS devient propriétaire des comptes et des sessions : ils doivent être
importés avec l’outil officiel syn2mas, servi par la commande dnf-mas de
l’hôte. Sans cet import, tous les comptes sont invisibles et tout le monde est
déconnecté.
-
Sauvegarder, dans cet ordre (le dump doit entrer dans l’instantané) :
Fenêtre de terminal sudo systemctl start postgresqlBackup.servicesudo systemctl start restic-backups-system-main.service -
Déployer la configuration (cf. ci-dessus). La fenêtre de maintenance commence ici.
-
Arrêter les deux services :
syn2masexige Synapse hors ligne, et MAS ne doit pas provisionner de compte entre-temps.Fenêtre de terminal sudo systemctl stop matrix-synapse matrix-authentication-service -
Vérifier, puis répéter à blanc, puis migrer :
Fenêtre de terminal sudo dnf-mas syn2mas checksudo dnf-mas syn2mas migrate --dry-runsudo dnf-mas syn2mas migrateLe compte-rendu annonce les comptes, liens SSO, jetons d’accès et devices repris. Les centaines d’utilisateurs « ignorés » sont les fantômes des ponts : c’est normal.
-
Redémarrer, MAS d’abord :
Fenêtre de terminal sudo systemctl start matrix-authentication-servicesudo systemctl start matrix-synapse
Inscrire des amis
Section intitulée « Inscrire des amis »En plus des utilisateurs du SSO, on peut ouvrir une inscription sur jeton pour des comptes locaux à mot de passe (les amis). Sans jeton, aucune inscription n’est possible.
# usr/machines/<host>/configuration.nixdarkone.service.matrix.friendRegistration.enable = true;-
Créer un jeton sur l’hôte Matrix. Ici valable une fois et 7 jours (sans option, le jeton est à usage unique et sans expiration) :
Fenêtre de terminal sudo dnf-mas manage issue-user-registration-token --usage-limit 1 --expires-in 604800La réponse affiche le jeton, par exemple
Created user registration token: 9SwPTGIFGQPa. Le transmettre à l’ami. -
L’ami ouvre
https://matrix.domain.tld/registeret choisit la création par mot de passe. -
Il saisit son identifiant et son mot de passe, puis le jeton, réclamé à l’étape suivante.
-
Il se connecte ensuite avec ce couple identifiant / mot de passe depuis n’importe quel client, Element X compris.
Collision d’identifiant avec un utilisateur déclaré
Section intitulée « Collision d’identifiant avec un utilisateur déclaré »Amis et utilisateurs SSO partagent un seul espace de noms :
@bob:domain.tld n’appartient qu’à un compte, quelle que soit son origine. Et
un identifiant Matrix est définitif — la table users de MAS impose son
unicité, et désactiver un compte ne libère pas son identifiant. On ne renomme
pas, on ne recycle pas.
Le cas typique : un ami s’inscrit comme bob, puis bob est déclaré plus
tard comme utilisateur DNF dans etc/config.yaml. Sa première connexion SSO
s’arrête alors sur une page d’erreur User exists :
Upstream account provider returned "bob" as username, which is not linked tothat upstream account. Your homeserver does not allow linking an upstreamaccount to an existing accountVérifier avant de déclarer un utilisateur, sur l’hôte Matrix :
sudo -u postgres psql matrix-authentication-service \ -c "SELECT username FROM users WHERE lower(username) = lower('bob');"Deux issues, selon ce qu’on veut garder :
Rattache l’identité Kanidm au compte ami existant, qui conserve tout : salons, historique, clés de chiffrement, ponts. C’est presque toujours ce qu’on veut, et l’intéressé le fait lui-même.
-
Déclarer d’abord
bobdansetc/config.yamlet déployer, pour qu’il ait des identifiants Kanidm. -
Bob se connecte sur
https://matrix.domain.tld/account/avec son mot de passe d’ami. -
Dans le même navigateur, il ouvre le lien de rattachement du fournisseur IDM :
https://matrix.domain.tld/upstream/authorize/01JDNF0000000000000KAN1DM0 -
Il s’identifie sur Kanidm ; MAS propose alors « Lier à votre compte existant », qu’il valide.
Bob se connecte ensuite par le SSO comme n’importe quel utilisateur déclaré. Son mot de passe d’ami reste valable : le supprimer se fait depuis le portail de compte.
Si le compte ami n’a rien à sauver, il reste plus simple de déclarer
l’utilisateur DNF sous un autre login : l’identifiant bob est perdu
pour de bon, désactiver le compte ami ne le rendra pas disponible.
Verrouiller l’ancien compte pour éviter la confusion :
sudo dnf-mas manage lock-user bobGérer les ponts
Section intitulée « Gérer les ponts »Les ponts WhatsApp, Signal, Telegram et Messenger sont activés par défaut avec Matrix ; Discord est désactivé par défaut. Réglage par hôte dans la configuration nix de la machine :
# usr/machines/<host>/configuration.nixdarkone.service.matrix.bridges = { discord.enable = true; # opt-in messenger.enable = false; # désactiver un pont par défaut};Chaque pont a ses secrets propres :
| Pont | Clés sops | Remarque |
|---|---|---|
mautrix-whatsapp-as-token, mautrix-whatsapp-hs-token, mautrix-whatsapp-encryption-pickle-key | openssl rand -hex 32 chacun | |
| Signal | mautrix-signal-as-token, mautrix-signal-hs-token, mautrix-signal-encryption-pickle-key | openssl rand -hex 32 chacun |
| Messenger | mautrix-meta-as-token, mautrix-meta-hs-token, mautrix-meta-encryption-pickle-key | openssl rand -hex 32 chacun |
| Telegram | mautrix-telegram-api-id, mautrix-telegram-api-hash, mautrix-telegram-as-token, mautrix-telegram-hs-token | API id/hash à créer sur my.telegram.org 🡕 |
| Discord | mautrix-discord-as-token, mautrix-discord-hs-token | openssl rand -hex 32 chacun |
Les tokens as/hs rendent la registration appservice de chaque pont
déterministe (voir
les tokens d’appservice).
Permissions
Section intitulée « Permissions »Rien à gérer : tout compte local (@*:domain.tld) peut utiliser chaque
pont avec son propre compte distant, sans interférence entre
utilisateurs. Les comptes listés dans network.matrix.admins disposent en
plus des commandes d’administration des bots (help en conversation directe
avec le bot pour la liste).
Double puppeting
Section intitulée « Double puppeting »Les ponts partagent un appservice doublepuppet (token
mautrix-doublepuppet-as-token) : les messages envoyés depuis le
téléphone apparaissent dans Matrix comme venant du vrai compte de
l’utilisateur. C’est automatique pour tous les comptes locaux, aucune action
par utilisateur.
Appels audio et vidéo
Section intitulée « Appels audio et vidéo »Deux piles cohabitent, parce qu’aucune ne couvre tous les clients. Elles s’activent séparément et ne se remplacent pas :
| WebRTC historique | MatrixRTC | |
|---|---|---|
| Option | darkone.service.turn.enable | darkone.service.matrix.matrixRtc.enable |
| Composants | coturn | LiveKit (SFU) + service d’autorisation |
| Appels à deux | oui | oui |
| Appels de groupe | non | oui |
| Element Classic (Android) | oui | non |
| Element Web / Desktop | oui | oui |
| Element X (Android, iOS) | non | oui |
Garder les deux activées : Element Classic ne parle que la première, Element X que la seconde.
-
Activer la pile MatrixRTC dans la config nix de l’hôte :
# usr/machines/<host>/configuration.nixdarkone.service.matrix.matrixRtc.enable = true; # requis par Element X -
Générer le secret partagé, puis déployer :
Fenêtre de terminal just configure-admin-hostjust apply <host> switch -
Vérifier que le serveur annonce bien un transport :
Fenêtre de terminal # doit contenir "org.matrix.msc4143.rtc_foci"curl -A Mozilla/5.0 https://domain.tld/.well-known/matrix/client# org.matrix.msc4143 doit valoir truecurl -A Mozilla/5.0 https://matrix.domain.tld/_matrix/client/versions
Aucun sous-domaine ni certificat supplémentaire : le SFU et son service
d’autorisation sont servis sur le vhost Matrix, sous /livekit/sfu et
/livekit/jwt. Côté client, rien à régler non plus — Element X et Element
Web embarquent Element Call et découvrent le transport tout seuls.
Ports média
Section intitulée « Ports média »Le média ne passe pas par le proxy inverse : les clients joignent l’hôte directement. Ces ports doivent donc être ouverts jusqu’à lui, et redirigés s’il est derrière un NAT :
| Port | Pile | Usage |
|---|---|---|
| 30000-30100/udp | MatrixRTC | Média WebRTC, un port par participant |
| 7881/tcp | MatrixRTC | Repli pour les réseaux qui bloquent l’UDP |
| 3478/udp, 3478/tcp, 5349/tcp | coturn | STUN et négociation TURN |
| 61000-65535/udp | coturn | Média relayé |
Appels relayés en permanence
Section intitulée « Appels relayés en permanence »Un client WebRTC découvre son adresse publique avec une requête STUN
Binding, qu’il envoie toujours sans authentification, y compris vers le
serveur TURN. Si coturn la refuse (option secure-stun), le client n’obtient
aucun candidat server-reflexive : il ne lui reste que ses adresses
locales, inutilisables hors du réseau local, et son adresse de relais. Aucun
chemin direct n’est alors jamais tenté : tous les appels traversent
coturn, plus longs à s’établir et sans repli quand le relais sature.
DNF laisse donc secure-stun désactivé. Les allocations TURN, elles, restent
authentifiées par le secret partagé (use-auth-secret) : seul le Binding
est anonyme.
Contrôler depuis une machine extérieure :
nix shell nixpkgs#stuntman --command stunclient turn.domain.tld 3478Binding test: success suivi de l’adresse vue par le serveur : correct.
Binding test: fail : les requêtes anonymes sont refusées, tous les
appels seront relayés.
Pairs privés refusés par le relais
Section intitulée « Pairs privés refusés par le relais »Pendant un appel, coturn journalise des erreurs de cette forme :
ERROR session 000000000000000138: A peer IP 192.168.0.42 denied in the range:192.168.0.0-192.168.255.255 in server 0Ce n’est pas une panne. denied-peer-ip filtre la destination d’une
allocation (CreatePermission et ChannelBind), pas les clients autorisés à se
connecter. Le client a proposé un candidat en adresse privée, coturn a refusé
de relayer vers là, ICE écarte la paire et poursuit avec les candidats
publics.
DNF refuse donc toutes les plages privées et n’autorise que l’adresse publique
de l’hôte, parce que coturn est un relais public uniquement : les
clients le joignent sur cette adresse, et relay-ip est la même. Deux raisons
de ne pas rouvrir 10.0.0.0/8 ni 100.64.0.0/10.
Chemin asymétrique. Le média relayé sort par le relay-ip public alors
qu’un pair du tailnet ou du réseau local n’est joignable que par l’autre
interface. L’appel s’établit, puis meurt quand l’entrée de suivi de connexion
expire : c’est la signature des appels longs qui coupent sans raison.
L’option mobility aggrave le cas, un téléphone qui change de réseau conserve
une permission pointant l’adresse qu’il vient de quitter.
Pivot vers le réseau privé. Un relais qui retransmet vers du RFC1918 ou du
CGNAT devient un point d’entrée pour quiconque détient des identifiants TURN,
c’est-à-dire tout compte du serveur Matrix puisque Synapse les distribue
(use-auth-secret).
Ouvrir le réseau à la fédération
Section intitulée « Ouvrir le réseau à la fédération »Par défaut, le serveur fédère avec tous les serveurs Matrix, tout en restant
invisible dans les recherches (annuaires non exposés). Pour contrôler la
portée, régler darkone.service.matrix.federation dans la config nix de
l’hôte :
# usr/machines/<host>/configuration.nixdarkone.service.matrix.federation = { enable = true; # false = fédération totalement bloquée whitelist = [ ]; # vide = ouverte ; sinon liste blanche};| Objectif | Réglage |
|---|---|
| Réseau isolé (aucune fédération) | enable = false; |
| Ouvert à tout l’écosystème Matrix | enable = true; whitelist = [ ]; |
| Restreint à des serveurs de confiance | enable = true; whitelist = [ "ami.org" "matrix.org" ]; |
Vérifier après déploiement :
-
Tester la fédération entrante avec federationtester.matrix.org 🡕 sur
domain.tld→ tous les voyants verts. -
En liste blanche, un serveur non listé est refusé (journal Synapse :
Federation denied), un domaine listé fonctionne.
Salons publics avec validation (knock)
Section intitulée « Salons publics avec validation (knock) »Un salon public au sens Matrix se rejoint librement : pas de validation. Pour un salon « joignable mais sur approbation », utiliser la règle d’accès knock (toquer) — réglage par salon, côté client, sans configuration serveur.
-
Dans Element : salon → Paramètres du salon → Sécurité & confidentialité.
-
Accès au salon → « Demander à rejoindre » (knock).
-
Un utilisateur (local ou distant fédéré) demande l’accès ; un modérateur approuve ou refuse.
Exploitation
Section intitulée « Exploitation »| Action | Commande |
|---|---|
| État des services | systemctl status matrix-synapse mautrix-whatsapp mautrix-signal mautrix-telegram mautrix-meta-messenger |
| Journaux d’un pont | journalctl -u mautrix-whatsapp -f |
| Journaux Synapse | journalctl -u matrix-synapse -f |
| Journaux MAS | journalctl -u matrix-authentication-service -f |
| Journaux MatrixRTC | journalctl -u livekit -u lk-jwt-service -f |
Administrer les comptes avec MAS
Section intitulée « Administrer les comptes avec MAS »dnf-mas enveloppe l’outil officiel mas-cli sur l’hôte Matrix : il
reconstitue la configuration et les secrets du service, et fonctionne donc
service arrêté comme démarré. Toujours en sudo.
| Action | Commande |
|---|---|
| Créer un jeton d’inscription | sudo dnf-mas manage issue-user-registration-token |
| Lister les administrateurs | sudo dnf-mas manage list-admin-users |
| Retirer une promotion héritée | sudo dnf-mas manage demote-admin alice |
| Créer un compte (bot, service) | sudo dnf-mas manage register-user |
| Changer un mot de passe | sudo dnf-mas manage set-password alice |
| Fermer les sessions d’un compte | sudo dnf-mas manage kill-sessions alice |
| Verrouiller un compte | sudo dnf-mas manage lock-user alice |
| Diagnostic complet | sudo dnf-mas doctor |
mas-cli n’expose aucune commande de lecture : pour voir les comptes,
leurs appareils et leurs sessions, activer le service matrix-admin, l’interface
web Ketesa 🡕 (ex-synapse-admin), qui parle aux
API d’administration de MAS et de Synapse.
# etc/config.yaml, sur l'hôte du serveur Matrixservices: matrix-admin:Elle répond sur https://matrix-admin.domain.tld, depuis le LAN et le tailnet
uniquement. Le sous-domaine doit tout de même résoudre publiquement vers le
HCS : Caddy en a besoin pour le certificat ACME, avant de refuser les
appelants extérieurs. Connexion par le bouton OIDC, avec un compte listé dans
network.matrix.admins : la politique MAS lui accorde
les portées urn:mas:admin et urn:synapse:admin:* dont l’interface a besoin
sur les deux API.
| Côté | Ce que l’interface permet |
|---|---|
| MAS | Sessions (compat, OAuth2, personnelles), e-mails, liens OIDC amont, jetons d’inscription, mot de passe, verrouillage, désactivation |
| Synapse | Comptes et appareils, salons, médias, signalements, limites de débit |
Diagnostiquer une connexion SSO refusée
Section intitulée « Diagnostiquer une connexion SSO refusée »Après une migration, les sessions déjà ouvertes continuent de fonctionner : une connexion cassée ne se voit donc qu’au premier login neuf. Suivre MAS pendant une tentative :
journalctl -u matrix-authentication-service -f| Message | Cause | Correctif |
|---|---|---|
wrong signature alg sur /upstream/callback/… | MAS attend un id_token RS256, Kanidm signe en ES256 | id_token_signed_response_alg du fournisseur amont : réglé sur ES256 par le module |
client registration denied by the policy: invalid redirect_uri | Le client_uri annoncé par le client ne correspond pas à son schéma de redirection | Ne pas forcer oidc_metadata dans la config Element ; ses valeurs par défaut passent |
M_UNRECOGNIZED sur /login/sso/redirect | Le chemin part vers Synapse au lieu de MAS | Vérifier le matcher @masCompat du vhost Caddy |
Page User exists : « returned “bob” as username, which is not linked » | Un compte ami occupe déjà l’identifiant de l’utilisateur déclaré | Fusionner ou renommer (cf. collision d’identifiant) |
| « Le code QR n’est pas pris en charge » côté client | Le canal de rendez-vous MSC4108 est absent | experimental_features.msc4108_enabled : activé par le module avec MAS |
« L’appel n’est pas pris en charge », MISSING_MATRIX_RTC_TRANSPORT | Aucun backend MatrixRTC annoncé | Activer matrixRtc.enable (cf. Appels audio et vidéo) |
| Appel qui sonne mais reste muet, ou coupe au bout de quelques secondes | Les ports média n’atteignent pas l’hôte | Ouvrir 30000-30100/udp et 7881/tcp jusqu’à lui |
| Appel long à s’établir, voire qui échoue, et tout le trafic passe par coturn | Les clients n’obtiennent aucun candidat server-reflexive, donc aucun chemin direct | coturn doit répondre aux requêtes STUN Binding anonymes (cf. Appels relayés en permanence) |
Contrôles rapides, sans se connecter :
# org.matrix.msc4108 doit valoir truecurl -A Mozilla/5.0 https://matrix.domain.tld/_matrix/client/versions
# doit répondre 201 et une url publiquecurl -A Mozilla/5.0 -X POST --data probe \ https://matrix.domain.tld/_matrix/client/unstable/org.matrix.msc4108/rendezvousDiagnostiquer un bot muet
Section intitulée « Diagnostiquer un bot muet »Un bot qui ne répond pas à help alors que son service tourne tombe presque
toujours dans l’un des deux cas : ses tokens ne correspondent plus à la
registration chargée par Synapse, ou il reçoit les messages mais ne peut pas
les déchiffrer. Diagnostic dans l’ordre :
-
Le service tourne-t-il vraiment ?
Fenêtre de terminal systemctl status mautrix-<pont>journalctl -u mautrix-<pont> -n 50S’il redémarre en boucle avec « The as_token was not accepted », l’enregistrement en mémoire de Synapse ne correspond plus aux tokens du pont (typique après un reset ou une migration, voir l’encart plus haut) :
systemctl restart matrix-synapse, puis redémarrer le pont. -
Le message arrive-t-il au pont ? Suivre Synapse pendant l’envoi d’un
helpau bot :Fenêtre de terminal journalctl -u matrix-synapse -f | grep transactionsUne ligne
PUT .../_matrix/app/v1/transactions/... 200doit apparaître. Si rien ne part, l’appservice n’est pas chargé : vérifier la présence de l’enregistrement dans/var/lib/mautrix-<pont>/et redémarrer Synapse. -
Le pont peut-il le déchiffrer ? Si la transaction passe (200) mais que le bot reste muet, suivre ses logs pendant un nouvel envoi :
Fenêtre de terminal journalctl -u mautrix-<pont> -fChercher
decrypt,session,verification,dropping. Un échec de déchiffrement signifie que la clé de session du salon n’a pas été partagée avec le device actuel du bot (cas classique après un reset du pont : son identité e2ee a changé). -
Recréer la conversation. Quitter le message direct avec le bot et en démarrer un nouveau : le client établit une session de chiffrement neuve et partage la clé avec le device actuel du bot. Vérifier au passage que sa propre session Element est vérifiée.
-
Dernier recours : réinitialiser l’état du pont (encart ci-dessous), puis redémarrer Synapse pour recharger l’enregistrement.
Voir aussi
Section intitulée « Voir aussi »- Messagerie Matrix : guide utilisateur : connexion aux ponts pour les utilisateurs
- Matrix dans DNF : architecture et implémentation