# TURN-Server (coturn) TURN-Relay für Sprach- und Videoanrufe in Matrix/Element. | Punkt | Wert | |---|---| | Software | `coturn` | | Öffentlich | `turn.example.com` | | Ports | 3478 TCP/UDP (STUN/TURN), 5349 TCP/UDP (TURNS), 49152–65535 UDP (Relay) | | Authentifizierung | Zeitbegrenzt über gemeinsames Secret | ## Wozu überhaupt? WebRTC verbindet zwei Teilnehmer möglichst direkt. Sitzen beide hinter NAT oder einer restriktiven Firewall, klappt das nicht – der Anruf klingelt, aber es kommt kein Ton an. Der TURN-Server leitet den Medienstrom dann weiter. Er ist damit ein **Relay für fremden Datenverkehr**. Ohne Absicherung ist das ein offener Proxy, mit dem sich das interne Netz des Servers erreichen lässt. Der Abschnitt „Absicherung“ unten ist deshalb nicht optional. ## Kein festes Passwort coturn und Synapse teilen sich ein Secret (`static-auth-secret` bzw. `turn_shared_secret`). Fragt ein Client Zugangsdaten an, erzeugt Synapse daraus per HMAC ein kurzlebiges Paar: - Benutzername: `:` - Passwort: HMAC-SHA1 über den Benutzernamen mit dem Secret, Base64-kodiert coturn rechnet dasselbe nach. Es gibt also **keine angelegten Benutzer** – beide Seiten müssen nur exakt dasselbe Secret kennen. ## 1. Installation ```bash sudo apt-get install -y coturn # Das Debian-Paket startet ohne diesen Schalter nicht. sudo sed -i 's/^#\?TURNSERVER_ENABLED=.*/TURNSERVER_ENABLED=1/' /etc/default/coturn ``` ## 2. Secret erzeugen ```bash openssl rand -hex 32 ``` Derselbe Wert muss in `/etc/turnserver.conf` (`static-auth-secret`) **und** in der Synapse-Konfiguration (`turn_shared_secret`) stehen. ## 3. Konfiguration `turnserver.conf.example` nach `/etc/turnserver.conf` kopieren und anpassen: ```bash sudo cp turnserver.conf.example /etc/turnserver.conf sudo chown root:turnserver /etc/turnserver.conf sudo chmod 640 /etc/turnserver.conf ``` Zwingend zu ersetzen: - `static-auth-secret` – der oben erzeugte Wert - `external-ip` – die **öffentliche** IP des Servers - `realm` / `server-name` – die eigene Domain ### external-ip nicht vergessen Ohne `external-ip` kündigt coturn hinter NAT seine private Adresse als Relay-Kandidat an. Der Anruf baut sich dann scheinbar auf, aber es fließen keine Medien. Zu ermitteln mit: ```bash curl -s -4 ifconfig.me ``` ## 4. Zertifikat lesbar machen `turns:` auf Port 5349 braucht Zertifikat und Key. coturn läuft als Benutzer `turnserver` und kommt an Let's-Encrypt-Material standardmäßig nicht heran. Statt die Rechte global aufzuweichen, eine eigene Gruppe verwenden: ```bash sudo groupadd -f ssl-cert sudo usermod -aG ssl-cert turnserver sudo chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive sudo chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive sudo find /etc/letsencrypt/archive -name 'privkey*.pem' -exec chmod 640 {} \; ``` > Gruppenmitgliedschaften greifen erst beim **Start eines Prozesses**. Läuft > coturn schon, ist danach ein `systemctl restart coturn` nötig – ein `reload` > reicht nicht. Prüfen, ob es wirklich klappt: ```bash sudo -u turnserver head -c 30 /etc/letsencrypt/live/matrix.example.com/privkey.pem ``` ### Erneuerung berücksichtigen coturn liest das Zertifikat nur beim Start. Nach einer Erneuerung würde es mit dem alten weiterarbeiten, bis es irgendwann abläuft. Deshalb ein Deploy-Hook nach `/etc/letsencrypt/renewal-hooks/deploy/10-reload-services.sh`: ```sh #!/bin/sh set -e chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive || true chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive || true find /etc/letsencrypt/archive -name 'privkey*.pem' -exec chmod 640 {} \; || true systemctl reload nginx || true systemctl restart coturn || true ``` Ausführbar machen: `sudo chmod +x …/10-reload-services.sh` ## 5. Absicherung Die `denied-peer-ip`-Zeilen in der Beispielkonfiguration sperren alle privaten und reservierten Netze. **Ohne sie kann jeder, der Zugangsdaten bekommt, über den TURN-Server auf `localhost` und das interne Netz zugreifen** – inklusive Diensten, die nur lokal lauschen. Weitere gesetzte Schalter: | Schalter | Wirkung | |---|---| | `no-tcp-relay` | Kein TCP-Relay (RFC 6062). WebRTC braucht es nicht. | | `no-multicast-peers` | Kein Relay an Multicast-Adressen | | `no-cli` | Telnet-Verwaltungsschnittstelle aus | | `no-tlsv1`, `no-tlsv1_1`, `no-sslv3` | Veraltete Protokolle aus | | `user-quota`, `total-quota` | Begrenzt den Missbrauch | ## 6. Start und Prüfung ```bash sudo systemctl enable --now coturn sudo systemctl status coturn ``` Lauschen sollte er auf beiden Ports: ```bash ss -tuln | grep -E '3478|5349' ``` Im Log darf **nicht** stehen „cannot start TLS and DTLS listeners“. Das heißt, dass coturn das Zertifikat nicht lesen kann: ```bash sudo journalctl -u coturn | grep -iE 'certificate|private key|TLS' ``` ### Relay tatsächlich testen Ein Blick auf offene Ports genügt nicht – erst eine echte Allocation beweist, dass Authentifizierung und Relay funktionieren. Zugangsdaten selbst berechnen: ```bash python3 - 'HIER-DAS-TURN-SECRET' <<'PY' import hashlib, hmac, base64, time, sys secret = sys.argv[1] user = f"{int(time.time()) + 3600}:testuser" pw = base64.b64encode( hmac.new(secret.encode(), user.encode(), hashlib.sha1).digest()).decode() print(user); print(pw) PY ``` Damit eine Allocation anfordern: ```bash turnutils_uclient -u '' -w '' -y -n 2 turn.example.com -p 3478 ``` Erfolg sieht so aus – gleich viele gesendete und empfangene Nachrichten, keine verlorenen Pakete: ``` start_mclient: tot_send_msgs=8, tot_recv_msgs=8 Total lost packets 0 (0.000000%) ``` Gegenprobe mit falschem Passwort muss `Cannot complete Allocation` liefern. > `turnutils_uclient -T …` schlägt mit `error 442` fehl. Das ist **kein** > Fehler: `-T` fordert ein TCP-Relay an, das per `no-tcp-relay` gesperrt ist. TLS-Listener separat prüfen: ```bash openssl s_client -connect turn.example.com:5349 -servername turn.example.com /dev/null \ | openssl x509 -noout -ext subjectAltName ``` ## 7. Verbindung zu Synapse In `/etc/matrix-synapse/conf.d/homelab.yaml` stehen `turn_uris` und `turn_shared_secret`. Nach dem Neustart lässt sich prüfen, was ein Client wirklich ausgeliefert bekommt: ```bash curl -s -H "Authorization: Bearer " \ https://matrix.example.com/_matrix/client/v3/voip/turnServer ``` Die zurückgegebenen Zugangsdaten sollten mit `turnutils_uclient` funktionieren – das ist der eigentliche Ende-zu-Ende-Nachweis. ## Firewall Freizugeben sind: - 3478 TCP **und** UDP - 5349 TCP **und** UDP - 49152–65535 UDP (Relay-Bereich) Der Relay-Bereich wird oft vergessen. Ohne ihn kommt die Allocation zustande, aber es fließen keine Medien. ## Stolpersteine | Symptom | Ursache | |---|---| | Port 5349 fehlt, Log meldet „certificate file is not set properly“ | `turnserver` darf den Key nicht lesen, oder Dienst lief schon vor der Gruppenänderung | | Anruf klingelt, kein Ton/Bild | `external-ip` fehlt oder Relay-Ports gesperrt | | `error 442` im Test | `-T` verwendet; TCP-Relay ist absichtlich aus | | Authentifizierung schlägt fehl | Secret in coturn und Synapse verschieden | | Nach Zertifikatserneuerung bricht TURNS ab | Deploy-Hook fehlt |