506 lines
16 KiB
Markdown
506 lines
16 KiB
Markdown
# 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`** :
|
|
```json
|
|
{ "m.server": "matrix.a5l.fr:443" }
|
|
```
|
|
|
|
**`/.well-known/matrix/client`** :
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```yaml
|
|
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).
|
|
|
|
```yaml
|
|
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 :
|
|
|
|
```yaml
|
|
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` :
|
|
|
|
```yaml
|
|
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`** :
|
|
|
|
```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
|
|
|
|
```yaml
|
|
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) :
|
|
|
|
```json
|
|
{
|
|
"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`.
|
|
|
|
```nginx
|
|
# 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
|
|
|
|
```bash
|
|
# 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 :**
|
|
|
|
1. `https://a5l.fr/.well-known/matrix/client` doit renvoyer le JSON correct
|
|
2. `https://federationtester.matrix.org/#a5l.fr` doit passer au vert
|
|
3. 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é
|
|
4. 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 :
|
|
```bash
|
|
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 ? |