diff --git a/a8a8ecf4-3b88-457d-8d5b-4e1012e44de1/draft_overlay.json b/a8a8ecf4-3b88-457d-8d5b-4e1012e44de1/draft_overlay.json new file mode 100644 index 0000000..f0e2ce9 --- /dev/null +++ b/a8a8ecf4-3b88-457d-8d5b-4e1012e44de1/draft_overlay.json @@ -0,0 +1,5 @@ +{ + "title": "Déployer Matrix sur a5l.fr avec SSO Keycloak et Element Call", + "slug": "tutoriel-matrix-sur-a5l-fr-synapse-element-element-call-avec-sso-keycloak", + "_updated_at": "2026-05-16 08:12:32" +} diff --git a/a8a8ecf4-3b88-457d-8d5b-4e1012e44de1/draft_overlay.md b/a8a8ecf4-3b88-457d-8d5b-4e1012e44de1/draft_overlay.md new file mode 100644 index 0000000..56233b5 --- /dev/null +++ b/a8a8ecf4-3b88-457d-8d5b-4e1012e44de1/draft_overlay.md @@ -0,0 +1,498 @@ +# 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 +auth IN A +chat IN A +mrtc IN A +turn IN A + +; 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