Element Call? MatrixRTC? What?
Ohne Anspruch auf Korrektheit versuche ich mal, ein paar Begrifflichkeiten kurz aufzudröseln.
Früher wurde bei Element (der Legacy-App) noch auf Jitsi für Gruppen-Videotelefonie gesetzt. Das gilt mittlerweile bei den Matrix-Clients als veraltet. Element X verlangt Element Call, eine auf MatrixRTC basierende, dezentrale und föderierte Videokonferenzlösung, die als SFU (Selective Forwarding Unit) fungiert. Sie empfängt eingehende Medienströme von Clients und leitet diese gezielt an andere Teilnehmer weiter.
MatrixRTC (Real-Time Communication) ist das zugrundeliegende Protokoll und Framework, das von Element Call genutzt wird.
Als tatsächlicher Medien-Server für die Echtzeitkommunikation kommt LiveKit zum Einsatz und stellt gewissermaßen die technische Infrastruktur bereit. Und dann gibt es noch JWT, das zur Authentifizierung und Autorisierung von Clients gegenüber dem LiveKit-Server dient.
Zusammenfassend kann man sagen: Um das Element Call Backend einzurichten, muss man die Dienste LiveKit und JWT aufsetzen. Und genau das machen wir jetzt. Vorausgesetzt werden ein bereits laufender Matrix-Dienst sowie entsprechende DNS‑Einträge. Aufgesetzt werden die Dienste hier als rootless Podman Quadlets.
Eigenschaften von Element Call
Bevor wir zu Werke schreiten, sollen noch ein paar Eigenschaften und Vorteile von Element Call aufgelistet werden. Man möchte doch wissen, wozu man sich den Aufwand macht – und ich finde, es ist die Mühe wert:
- Dezentral und föderiert
- Ende-zu-Ende-verschlüsselt
- Standalone- und Widget-Mode (also über eine eigenständige App oder in Matrix-Clients eingebettet verwendbar)
- WebRTC basiert
- Skalierbar mit LiveKit (unterstützt große Meetings mittels SFU)
- Hand heben (Teilnehmer können signalisieren, wenn sie sprechen möchten)
- Emoji-Reaktionen
Die Punkte habe ich der Github-Seite von Element Call entnommen.
Quadlets
Mit Podman-Quadlets kann man Container sehr einfach mittels Systemd erstellen und verwalten. Dabei erstellt man .container-Dateien, die mit dem Befehl systemctl --user daemon-reloadverarbeitet und dann über die üblichen Systemd-Mechanismen gesteuert werden können. Auch werden die Dienste dann (wenn man möchte) automatisch beim Systemstart gestartet, was ansonsten bei reinen Podman-Containern so eine Sache ist, weil es keinen überwachenden Podman-Prozess gibt, wie es bei Docker der Fall ist. Quadlets sind also eine elegante Art, um zu einfach steuerbaren (Systemd-)Containerdiensten zu kommen.
Eigentlich dürfen User-Systemd-Dienste nicht einfach weiterlaufen, wenn der Benutzer ausgeloggt ist. Da wir die Quadlets jedoch – was empfohlen wird – rootless aufsetzen möchten, muss genau das erlaubt werden. Dazu muss man als root lediglich das Kommando loginctl enable-linger USERNAME ausführen.
LiveKit
Um dem Matrix-Dienst die Authentifizierung an LiveKit zu gestatten, muss man einen API Key und ein API Secret erstellen. Das geht am einfachsten mit einem temporären LiveKit-Container:
$ podman run --rm livekit/livekit-server:latest generate-keys
API Key: API47oZq2FVSuAF
API Secret: BpGjiQxfgCNciYv6Pehnnd9hsH2ZS4QbGIFwmMpZfeuB
Für den LiveKit-Dienst erstellen wir die Datei $HOME/.config/container/systemd/matrix-element-call-livekit.container mit folgendem Inhalt:
[Unit]
Description=LiveKit
[Container]
ContainerName=matrix-element-call-livekit
Image=docker.io/livekit/livekit-server:latest
Timezone=Europe/Berlin
PublishPort=127.0.0.1:7880:7880
PublishPort=7881:7881
PublishPort=20800-20900:20800-20900/udp
AddHost=matrix.mydomain.com:host-gateway
AddHost=matrix-rtc.mydomain.com:host-gateway
Volume=/srv/podvols/matrix-element-call-livekit/config.yaml:/etc/livekit.yaml:ro
Exec=/usr/local/bin/livekit-server --config /etc/livekit.yaml
[Service]
Restart=always
[Install]
WantedBy=default.target
Der Port 7880 horcht nur an localhost; Caddy als Reverse Proxy wird später die Verbindungsanfragen von außen weiterleiten. Der Port 7881 dient als TCP-Ausweichport, wenn eine Verbindung über die UDP-Ports nicht möglich ist. Für die UDP-Ports findet man in Internet eigentlich als Default den Bereich 50100-50200. Bei mir hatte ich damit jedoch Probleme, die auf Podman-Eigenheiten zurückzuführen waren. Letztlich habe ich mich für den Bereich 20800-20900 entschieden.
Die AddHost-Zeilen sind auch Podman geschuldet, weil ansonsten keine Verbindung zum eigenen Host möglich ist. matrix.mydomain.com ist der FQDN des Matrix-Dienstes, matrix-rtc.mydomain.com wird von LiveKit sowie JWT verwendet. Alles läuft jedoch auf demselben Server.
Als Volume muss noch die LiveKit-Konfigurationsdatei config.yaml eingebunden werden, welche bei mir so ausschaut:
port: 7880
bind_addresses:
- ""
rtc:
tcp_port: 7881
port_range_start: 20800
port_range_end: 20900
use_external_ip: true
enable_loopback_candidate: false
logging:
level: info
keys:
API47oZq2FVSuAF: BpGjiQxfgCNciYv6Pehnnd9hsH2ZS4QbGIFwmMpZfeuB
Hier erkennt man die Portangaben wieder, sowie den API-Key und das API-Secret. Es sind auch andere/weitere Angaben denkbar, etwa anstelle von use_external_ip eine konkrete IP-Adresse oder auch als weitere Verbindungsalternative Angaben zum TURN-Server. In meinem Fall war jedoch obiger Inhalt ausreichend und funktionierte zuverlässig.
JWT
Für den JWT-Dienst erstellen wir folgende Datei $HOME/.config/container/systemd/matrix-element-call-jwt.container:
[Unit]
Description=JWT
[Container]
ContainerName=matrix-element-call-jwt
Image=ghcr.io/element-hq/lk-jwt-service:latest
Timezone=Europe/Berlin
PublishPort=8091:8091
AddHost=matrix.mydomain.com:host-gateway
AddHost=matrix-rtc.mydomain.com:host-gateway
Environment=LIVEKIT_URL=wss://matrix-rtc.mydomain.com
Environment=LIVEKIT_KEY=API47oZq2FVSuAF
Environment=LIVEKIT_SECRET=BpGjiQxfgCNciYv6Pehnnd9hsH2ZS4QbGIFwmMpZfeuB
Environment=LIVEKIT_JWT_BIND=:8091
Environment=LIVEKIT_FULL_ACCESS_HOMESERVERS=matrix.mydomain.com
[Service]
Restart=always
[Install]
WantedBy=default.target
Standardmäßig verwendet JWT den Port 8080. Da dieser bei mir jedoch bereits belegt war, musste ich auf Port 8091 ausweichen. LIVEKIT_FULL_ACCESS_HOMESERVERS sorgt dafür, dass nur Benutzer des eigenen Matrix-Servers neue Anrufe starten dürfen (Nutzer von anderen Matrix-Servern dürfen aber teilnehmen).
Matrix-Server für Element Call fit machen
Damit der Matrix-Server mit Element Call umgehen kann, müssen in dessen homeserver.yaml noch folgende Einträge gemacht werden:
experimental_features:
msc3266_enabled: true
msc4222_enabled: true
msc4140_enabled: true
max_event_delay_duration: 24h
Reverse-Proxy
Damit Caddy auf dem Server die entsprechenden Weiterleitungen an die Container vornimmt, sind im Caddyfile noch folgende Einträge notwendig. Nginx-Benutzer finden im Internet sicherlich die äquivalenten Angaben:
matrix-rtc.mydomain.com {
@jwt_service path /sfu/get* /healthz* /get_token*
route @jwt_service {
reverse_proxy 127.0.0.1:8091
}
reverse_proxy 127.0.0.1:7880
}
Außerdem muss man den Clients noch mitteilen, wo sie LiveKit finden können. Das geht über eine entsprechende .well-known Angabe und kann ebenfalls im Caddyfile gemacht werden. Hier der relevante Auszug (wichtig ist der org.matrix.msc4143.rtc_foci-Teil):
matrix.mydomain.com {
respond /.well-known/matrix/client `{"m.homeserver": {"base_url": "https://matrix.mydomain.com"}, "org.matrix.msc4143.rtc_foci": [ { "type": "livekit", "livekit_service_url": "https://matrix-rtc.mydomain.com" } ]}`
}
Nun sollte man nicht vergessen, die Ports 7880/7881 TCP sowie 20800-20900 (und 50100-50200) UDP an der Firewall zu öffnen. Bei ufw funktioniert das mittels ufw allow 7880:7881/tcp bzw. ufw allow 20800:20900/udp.
Fertig. Fortan kann man in 1:1-Chats direkt das Kamerasymbol Element X verwenden, um über Element Call Videoanrufe zu tätigen. Bei Räumen mit mehreren Nutzern müssen evtl. die Berechtigungen für das Starten und/oder Beitreten von Videokonferenzen angepasst werden.
Fazit
Das war eine Menge Stoff für das Aufsetzen der beiden Container. Blut, Schweiß und Tränen sind geflossen, bis das Ganze bei mir funktioniert hat. Insbesondere für die konkrete Umsetzung mit der Kombination Caddy und Quadlets sind die Informationen im Internet doch etwas rar. Ich hoffe, dass die Anleitung dem einen oder der anderen beim Einrichten hilfreich ist.
Quellen:
