Homelab-Dokumentation und Konfigurationsvorlagen
Dokumentation aus dem Wiki uebernommen und um die Dienste auf dem Server
erweitert. Alle Anleitungen sind so geschrieben, dass sich der jeweilige
Dienst allein daraus neu aufsetzen laesst.
docs/ Dienste im Heimnetz (aus dem Wiki, anonymisiert)
services/ Dienste auf dem Server mit Konfigurationsvorlagen
- matrix-synapse: Homeserver, Postgres, Well-Known-Delegation
- coturn: TURN-Relay fuer Anrufe
- element-web: Web-Client
- wikijs, gitea, nginx
Durchgehend anonymisiert: echte Domain durch example.com ersetzt, IP-Adressen
und E-Mail-Adressen durch Platzhalter. Konfigurationsdateien liegen nur als
.example mit Platzhaltern statt echter Secrets vor.
Die Anleitungen halten die Stolpersteine fest, die beim Aufbau tatsaechlich
aufgetreten sind, unter anderem:
- Synapse verlangt LC_COLLATE=C, sonst startet es nicht
- server_name ist nachtraeglich nicht aenderbar -> Delegation noetig
- register_new_matrix_user liest conf.d nicht, Secret muss per -k kommen
- coturn braucht external-ip, sonst kommen keine Medien durch
- Gruppenmitgliedschaft fuer den Zertifikatszugriff wirkt erst beim Neustart
- nginx vererbt add_header nicht in location-Bloecke mit eigenen Direktiven
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
# 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: `<ablaufzeitpunkt>:<benutzer-id>`
|
||||
- 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 '<benutzername>' -w '<passwort>' -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
|
||||
```
|
||||
|
||||
Der Domainname muss im Zertifikat stehen:
|
||||
|
||||
```bash
|
||||
openssl s_client -connect turn.example.com:5349 </dev/null 2>/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 <access-token>" \
|
||||
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 |
|
||||
@@ -0,0 +1,62 @@
|
||||
# ===========================================================================
|
||||
# coturn fuer Matrix/Element-Anrufe (turn.example.com)
|
||||
# ===========================================================================
|
||||
|
||||
listening-port=3478
|
||||
tls-listening-port=5349
|
||||
|
||||
# Auf allen Adressen lauschen, aber die oeffentliche IP in Kandidaten melden.
|
||||
# Ohne external-ip kuendigt der Server hinter NAT die private IP an und
|
||||
# Verbindungen scheitern.
|
||||
external-ip=HIER-DIE-OEFFENTLICHE-IP
|
||||
|
||||
realm=example.com
|
||||
server-name=turn.example.com
|
||||
|
||||
# Zeitbegrenzte Anmeldedaten. Synapse leitet daraus mit demselben Secret
|
||||
# kurzlebige Zugangsdaten fuer die Clients ab -- es gibt keine festen Nutzer.
|
||||
use-auth-secret
|
||||
static-auth-secret=HIER-DAS-TURN-SECRET
|
||||
|
||||
# TLS-Material (SAN turn.example.com liegt im Matrix-Zertifikat)
|
||||
cert=/etc/letsencrypt/live/matrix.example.com/fullchain.pem
|
||||
pkey=/etc/letsencrypt/live/matrix.example.com/privkey.pem
|
||||
|
||||
# Veraltete/schwache Verfahren abschalten
|
||||
no-tlsv1
|
||||
no-tlsv1_1
|
||||
no-sslv3
|
||||
fingerprint
|
||||
|
||||
# Relay-Portbereich
|
||||
min-port=49152
|
||||
max-port=65535
|
||||
|
||||
# --------------------------- Absicherung ----------------------------------
|
||||
# Ohne diese Sperren laesst sich der TURN-Server als Sprungbrett in das
|
||||
# eigene interne Netz und auf localhost missbrauchen.
|
||||
no-multicast-peers
|
||||
no-cli
|
||||
no-tcp-relay
|
||||
|
||||
denied-peer-ip=0.0.0.0-0.255.255.255
|
||||
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||
denied-peer-ip=100.64.0.0-100.127.255.255
|
||||
denied-peer-ip=127.0.0.0-127.255.255.255
|
||||
denied-peer-ip=169.254.0.0-169.254.255.255
|
||||
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||
denied-peer-ip=192.0.0.0-192.0.0.255
|
||||
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
denied-peer-ip=198.18.0.0-198.19.255.255
|
||||
denied-peer-ip=240.0.0.0-255.255.255.255
|
||||
denied-peer-ip=::1
|
||||
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
|
||||
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
|
||||
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
|
||||
# Missbrauch begrenzen
|
||||
user-quota=12
|
||||
total-quota=1200
|
||||
|
||||
syslog
|
||||
Reference in New Issue
Block a user