16 KiB
Tutoriel Matrix sur a5l.fr — Synapse + Element + Element Call avec SSO Keycloak
Je vais d'abord vérifier l'état actuel de l'écosystème Matrix (les choses bougent vite côté Element Call et MSC3861), puis construire le tutoriel sur des bases à jour.Information importante : Element X (les nouveaux clients mobiles Element) n'accepte pas le SSO Synapse classique — il faut passer par MAS (Matrix Authentication Service). C'est devenu le standard depuis 2024-2025. Je vérifie aussi l'état d'Element Call.J'ai ce qu'il me faut. Le paysage a évolué : Element X (les nouvelles apps mobiles) exige MAS (Matrix Authentication Service) pour le SSO, et Element Call exige un backend MatrixRTC = LiveKit SFU + lk-jwt-service. Je vous propose un tutoriel structuré et réaliste.
Déployer Matrix sur a5l.fr avec SSO Keycloak et Element Call
Vue d'ensemble de l'architecture
Voici la pile complète à monter, et les sous-domaines associés :
a5l.fr → délégation .well-known vers matrix.a5l.fr
matrix.a5l.fr → Synapse (homeserver)
auth.a5l.fr (ou matrix-auth.a5l.fr) → MAS (Matrix Authentication Service)
chat.a5l.fr → Element Web
mrtc.a5l.fr → MatrixRTC backend (lk-jwt-service + LiveKit SFU)
idp.a5l.fr → Keycloak (déjà en place)
turn.a5l.fr → coturn (optionnel mais recommandé)
Et les briques applicatives :
| Brique | Rôle |
|---|---|
| Synapse | Homeserver Matrix : messages, rooms, fédération |
| PostgreSQL | Base de données de Synapse |
| MAS (Matrix Authentication Service) | Délègue l'auth à Keycloak en OIDC, sert d'OP aux clients Matrix (obligatoire pour Element X) |
| Element Web | Client web (les desktop et mobiles l'embarquent) |
| LiveKit SFU | Serveur de média WebRTC pour les appels audio/vidéo |
| lk-jwt-service | Émet les JWT permettant aux utilisateurs Matrix d'accéder à LiveKit |
| coturn | Serveur TURN pour les NAT difficiles (utile derrière FAI) |
| Keycloak | Votre IdP existant sur idp.a5l.fr |
Warning
Pourquoi MAS et pas directement OIDC dans Synapse ? Synapse supporte historiquement OIDC nativement, mais Element X (les nouvelles apps mobiles iOS et Android) refuse de s'authentifier sans MAS. Element X mise sur le mécanisme "next-gen auth" de Matrix, qui passe par MAS. Comme votre cible est "tous appareils", MAS est non-négociable. Les clients legacy (Element Web/Desktop) acceptent aussi MAS.
Prérequis matériels et réseau
Serveur :
- 4 Go RAM minimum, 8 Go recommandé pour confort
- 50 Go SSD pour démarrer (PostgreSQL grossit avec l'historique fédéré)
- Docker + Docker Compose installés
- Un reverse proxy (Nginx ou Caddy) sur le serveur
Réseau / FAI :
- IP publique non-CGNAT (vérifiez)
- Ports à ouvrir et rediriger vers le serveur :
| Port | Protocole | Usage |
|---|---|---|
| 443 | TCP | HTTPS pour tous les services web |
| 8448 | TCP | Fédération Matrix |
| 3478 | UDP/TCP | coturn STUN/TURN |
| 5349 | UDP/TCP | coturn STUN/TURN sur TLS |
| 50000-50100 | UDP | LiveKit (plage WebRTC) |
DNS chez votre registrar :
matrix IN A <IP_PUBLIQUE>
auth IN A <IP_PUBLIQUE>
chat IN A <IP_PUBLIQUE>
mrtc IN A <IP_PUBLIQUE>
turn IN A <IP_PUBLIQUE>
; Découverte fédération Matrix
_matrix._tcp.a5l.fr IN SRV 10 5 8448 matrix.a5l.fr.
Étape 1 — Déléguer Matrix depuis a5l.fr
Pour que @user:a5l.fr fonctionne (alors que Synapse tourne sur matrix.a5l.fr), le domaine racine doit servir deux fichiers de découverte.
Sur le serveur qui répond à https://a5l.fr/, exposez :
/.well-known/matrix/server :
{ "m.server": "matrix.a5l.fr:443" }
/.well-known/matrix/client :
{
"m.homeserver": {
"base_url": "https://matrix.a5l.fr"
},
"org.matrix.msc4143.rtc_foci": [
{
"type": "livekit",
"livekit_service_url": "https://mrtc.a5l.fr/livekit/jwt"
}
]
}
Le bloc rtc_foci (MSC4143) est ce qui dit aux clients Element X d'utiliser votre backend MatrixRTC plutôt que celui d'Element.
Servez ces deux fichiers avec Content-Type: application/json et Access-Control-Allow-Origin: *.
Étape 2 — Configurer Keycloak
Dans votre realm Keycloak (disons homelab), créez deux clients OIDC :
Client matrix-auth-service (pour MAS)
- Client ID :
matrix-auth-service - Client authentication : ON (confidentiel)
- Standard flow : activé
- Valid redirect URIs :
https://auth.a5l.fr/upstream/callback/01HXXXXXX(l'UUID sera donné par MAS au premier démarrage, ajustez) - Web origins :
https://auth.a5l.fr - Récupérez le Client Secret
Vérifiez que les mappers exposent bien preferred_username, email, name dans l'access token.
Restriction d'accès
Créez un rôle client matrix-user et assignez-le aux comptes Keycloak autorisés à utiliser Matrix. Configurez un Client Scope pour n'autoriser que ces rôles à obtenir un token.
Étape 3 — Déployer Synapse, MAS, et PostgreSQL
Voici un docker-compose.yml de base. Adaptez les chemins de volumes à votre arborescence.
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: synapse
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: synapse
POSTGRES_INITDB_ARGS: "--lc-collate=C --lc-ctype=C --encoding=UTF8"
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- matrix
synapse:
image: ghcr.io/element-hq/synapse:latest
restart: unless-stopped
depends_on:
- postgres
environment:
SYNAPSE_SERVER_NAME: a5l.fr
SYNAPSE_REPORT_STATS: "no"
volumes:
- ./data/synapse:/data
ports:
- "127.0.0.1:8008:8008"
networks:
- matrix
mas:
image: ghcr.io/element-hq/matrix-authentication-service:latest
restart: unless-stopped
depends_on:
- postgres
command: ["server", "--config=/config/config.yaml"]
volumes:
- ./data/mas:/config
ports:
- "127.0.0.1:8080:8080"
networks:
- matrix
networks:
matrix:
driver: bridge
Configuration MAS (./data/mas/config.yaml)
MAS est l'élément le plus subtil de la pile. Il joue deux rôles : client OIDC vers Keycloak (en upstream) et serveur OAuth/OIDC pour Synapse et les clients Matrix (en downstream).
http:
listeners:
- name: web
resources:
- name: discovery
- name: human
- name: oauth
- name: compat
- name: graphql
- name: assets
binds:
- address: '[::]:8080'
public_base: https://auth.a5l.fr/
issuer: https://auth.a5l.fr/
database:
uri: postgresql://synapse:${POSTGRES_PASSWORD}@postgres/mas
matrix:
homeserver: a5l.fr
endpoint: http://synapse:8008/
secret: ${MATRIX_SHARED_SECRET}
passwords:
enabled: false # On délègue entièrement à Keycloak
upstream_oauth2:
providers:
- id: 01HXXXXXXKEYCLOAK
issuer: https://idp.a5l.fr/realms/homelab
human_name: a5l SSO
client_id: matrix-auth-service
client_secret: ${KEYCLOAK_CLIENT_SECRET}
token_endpoint_auth_method: client_secret_basic
scope: "openid profile email"
claims_imports:
subject:
template: "{{ user.sub }}"
localpart:
action: require
template: "{{ user.preferred_username }}"
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
clients:
- client_id: 0000000000000000000SYNAPSE
client_auth_method: client_secret_basic
client_secret: ${SYNAPSE_MAS_SECRET}
Générez l'UUID 01HXXXXXXKEYCLOAK avec mas-cli config generate (ou un ULID), et mettez la même valeur dans la Valid redirect URI côté Keycloak : https://auth.a5l.fr/upstream/callback/01HXXXXXXKEYCLOAK.
Configuration Synapse (./data/synapse/homeserver.yaml)
Points clés à ajouter ou modifier après l'initialisation :
server_name: "a5l.fr"
public_baseurl: "https://matrix.a5l.fr/"
listeners:
- port: 8008
type: http
tls: false
bind_addresses: ['0.0.0.0']
x_forwarded: true
resources:
- names: [client, federation, openid]
compress: false
database:
name: psycopg2
args:
user: synapse
password: ${POSTGRES_PASSWORD}
database: synapse
host: postgres
cp_min: 5
cp_max: 10
# Délégation à MAS (remplace oidc_providers + password_config)
experimental_features:
msc3861:
enabled: true
issuer: https://auth.a5l.fr/
client_id: 0000000000000000000SYNAPSE
client_auth_method: client_secret_basic
client_secret: ${SYNAPSE_MAS_SECRET}
admin_token: ${MAS_ADMIN_TOKEN}
account_management_url: https://auth.a5l.fr/account/
# Requis par Element Call
msc3266_enabled: true
msc4222_enabled: true
max_event_delay_duration: 24h
# Désactiver l'auth locale puisque MAS gère tout
password_config:
enabled: false
enable_registration: false
Étape 4 — Déployer le backend MatrixRTC (Element Call)
Ajoutez au docker-compose.yml :
livekit:
image: livekit/livekit-server:latest
restart: unless-stopped
command: --config /etc/livekit.yaml
volumes:
- ./data/livekit/livekit.yaml:/etc/livekit.yaml:ro
network_mode: host # nécessaire pour la plage UDP WebRTC
lk-jwt-service:
image: ghcr.io/element-hq/lk-jwt-service:latest
restart: unless-stopped
environment:
LK_JWT_PORT: 8081
LIVEKIT_URL: https://mrtc.a5l.fr/livekit/sfu
LIVEKIT_KEY: ${LIVEKIT_KEY}
LIVEKIT_SECRET: ${LIVEKIT_SECRET}
LIVEKIT_FULL_ACCESS_HOMESERVERS: a5l.fr
ports:
- "127.0.0.1:8081:8081"
networks:
- matrix
./data/livekit/livekit.yaml :
port: 7880
bind_addresses:
- "127.0.0.1"
rtc:
tcp_port: 7881
port_range_start: 50000
port_range_end: 50100
use_external_ip: true
keys:
${LIVEKIT_KEY}: ${LIVEKIT_SECRET}
turn:
enabled: false # On utilise coturn dédié
Étape 5 — Element Web
element:
image: vectorim/element-web:latest
restart: unless-stopped
volumes:
- ./data/element/config.json:/app/config.json:ro
ports:
- "127.0.0.1:8082:80"
networks:
- matrix
./data/element/config.json (minimal) :
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.a5l.fr",
"server_name": "a5l.fr"
}
},
"features": {
"feature_video_rooms": true,
"feature_element_call_video_rooms": true,
"feature_group_calls": true
},
"element_call": {
"url": "https://chat.a5l.fr",
"use_exclusively": true,
"participant_limit": 8
}
}
Étape 6 — Reverse proxy
Voici un squelette Nginx. La partie sensible est le routage /livekit/jwt/ et /livekit/sfu/ sur mrtc.a5l.fr.
# matrix.a5l.fr
server {
listen 443 ssl http2;
listen 8448 ssl http2; # Fédération
server_name matrix.a5l.fr;
ssl_certificate /etc/letsencrypt/live/matrix.a5l.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matrix.a5l.fr/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;
}
}
# auth.a5l.fr (MAS)
server {
listen 443 ssl http2;
server_name auth.a5l.fr;
ssl_certificate /etc/letsencrypt/live/auth.a5l.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.a5l.fr/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
}
# mrtc.a5l.fr (Element Call backend)
server {
listen 443 ssl http2;
server_name mrtc.a5l.fr;
ssl_certificate /etc/letsencrypt/live/mrtc.a5l.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mrtc.a5l.fr/privkey.pem;
location ^~ /livekit/jwt/ {
proxy_pass http://127.0.0.1:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ^~ /livekit/sfu/ {
proxy_pass http://127.0.0.1:7880/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_send_timeout 120;
proxy_read_timeout 120;
proxy_buffering off;
}
}
# chat.a5l.fr (Element Web)
server {
listen 443 ssl http2;
server_name chat.a5l.fr;
ssl_certificate /etc/letsencrypt/live/chat.a5l.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.a5l.fr/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8082;
}
}
Étape 7 — Premier démarrage et test
# Génération des secrets une fois pour toutes
openssl rand -hex 32 # pour chaque secret du .env
docker compose up -d postgres
sleep 5
docker compose exec postgres psql -U synapse -c "CREATE DATABASE mas;"
# Initialiser Synapse
docker compose run --rm synapse generate
# Éditez data/synapse/homeserver.yaml avec la conf ci-dessus
# Démarrer
docker compose up -d
Tests :
https://a5l.fr/.well-known/matrix/clientdoit renvoyer le JSON correcthttps://federationtester.matrix.org/#a5l.frdoit passer au vert- Sur Element Web (
https://chat.a5l.fr), cliquez "Continuer avec a5l SSO" → vous devez être redirigé vers Keycloak → après login, retour sur Element connecté - Créez un salon, lancez un appel vidéo → vérifiez les logs LiveKit
Pièges spécifiques au homelab derrière FAI
NAT et WebRTC. Vos appels passeront mal sans coturn si vos correspondants sont derrière des NAT symétriques (4G/5G notamment). Déployez coturn sur le port 3478/5349 et déclarez-le dans la config LiveKit (section turn_servers) ou laissez le client utiliser le TURN annoncé par Synapse via turn_uris.
IP dynamique. Configurez un client DDNS qui met à jour vos enregistrements A. Pour la plupart des registrars, un cron toutes les 5 minutes suffit. Les enregistrements SRV n'ont pas besoin d'être touchés.
Fédération bavarde. Si vous rejoignez #matrix:matrix.org ou un autre gros salon, votre base PostgreSQL va grossir vite. Activez mjolnir ou configurez state_compressor après quelques semaines.
Sauvegarde. Critique :
docker compose exec postgres pg_dumpall -U synapse | gzip > backup-$(date +%F).sql.gz
tar czf data-$(date +%F).tar.gz data/synapse/media_store data/mas data/livekit
Pour aller plus vite
Sincèrement, si vous n'êtes pas attaché à comprendre chaque brique, utilisez matrix-docker-ansible-deploy de spantaleev. Le playbook gère MAS, Synapse, Element Web, Element Call, LiveKit, coturn, le SSO Keycloak, les .well-known, certbot — tout, avec les bonnes versions ensemble. Vous éditez un vars.yml, vous lancez ansible-playbook setup.yml --tags=setup-all,start, et c'est en ligne. Le tutoriel ci-dessus vous donne la compréhension de ce que le playbook fait sous le capot, ce qui reste précieux pour le débogage.
Et après ?
Une fois que ça tourne, des extensions utiles :
- synapse-admin pour gérer les utilisateurs en interface web
- mautrix-signal / mautrix-whatsapp : ponts vers Signal, WhatsApp, Telegram, etc., pour centraliser vos messageries dans Element
- hookshot pour les notifications GitHub/GitLab dans des salons
- maubot pour des bots utilitaires
Voulez-vous que je détaille la configuration coturn (importante pour la qualité des appels derrière votre FAI), ou la mise en place du playbook Ansible qui vous épargnerait tout le travail manuel ci-dessus ?