Eigener TURN Server für Jitsi Meet bereitstellen

[Letzte Aktualisierung: 09.05.2020, 12:20 Uhr] Ein eigener TURN Server macht Sinn, wenn man diese Leistung nicht über einen Dienstleister beziehen möchte. Sozusagen die wichtige Infrastruktur selbst betreiben und unter Kontrolle haben möchte. Es gibt natürlich neben den kostenpflichtigen Service auf diverse Server von T-Online, HostEurope, 1und1, Easybell, etc… die kostenlos zur Verfügung gestellt werden. In wie weit diese verfügbar, ausgelastet und performant sind, ist vemutlich in diesen Wochen von der Tageszeit abhängig.

In diesem Fall soll der TURN-Server in Verbindung mit Jitsi Meet betrieben werden. Dazu habe ich bereits diesen Beitrag geschrieben, welcher als Basis für diesen Artikel gilt.

Die Basis ist für den Service ist eine weitere virtuelle Maschine mit 2vCPU, 4GB RAM und 10GB Festplattenspeicherplatz. Die Netzwerkanbindung ist 1000Mbit/s und der Server hat eine IPv4 und IPv6 Adresse. Als Betriebssystem  ist Ubuntu Server 18.04.4 LTS vorinstalliert.

Die A und PTR-Einträge habe ich in der DNS-Zone angelegt. In meinem Beispiel lautet der FQDN: turn.meet.wydler.eu. Den Namen müsst ihr selbstverständlich bei eurer Installation anpassen. Dies bezieht sich natürlich auch die Dateinamen, in den Änderungen vorgenommen werden.

Installation von Coturn

Definition des DNS-Namen, auf den der TURN Server hören soll.

export FqdnTurnServer=turn.meet.wydler.eu
echo $FqdnTurnServer

Installation der Anwendung.

apt-get -y update
apt-get -y install vim htop sudo coturn gnupg2

Zuerst muss der TURN-Server aktiviert werden:

cp /etc/default/coturn /etc/default/coturn.$(date '+%Y-%m-%d_%H-%M-%S')
cat << \EOF >> /etc/default/coturn
TURNSERVER_ENABLED=1
EOF

Abrufen von Let’s Encrypt Zertifikat

Zuerst muss das Tool ‚certbot-auto‘ auf dem Server installieren, falls noch nicht geschehen.

cd /usr/local/sbin

wget https://dl.eff.org/certbot-auto
wget -N https://dl.eff.org/certbot-auto.asc

gpg2 --keyserver pool.sks-keyservers.net --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2
gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.asc certbot-auto

Abrufen des Zertifikats.

chmod a+x ./certbot-auto
./certbot-auto certonly --standalone --noninteractive --rsa-key-size 4096 --email hostmaster@domain.de -d $FqdnTurnServer --agree-tos

Damit Coturn die bereitgestellten Zertifikate von Let’s Encrypt nutzen kann, müssen die Dateien noch kopiert werden.

mkdir -p /etc/coturn/certs/
chown -R turnserver:turnserver /etc/coturn/
chmod -R 700 /etc/coturn/

cp -Lr /etc/letsencrypt/live/$FqdnTurnServer/fullchain.pem /etc/coturn/certs/$FqdnTurnServer.fullchain.pem
cp -Lr /etc/letsencrypt/live/$FqdnTurnServer/privkey.pem /etc/coturn/certs/$FqdnTurnServer.privkey.pem

chown turnserver /etc/coturn/certs/$FqdnTurnServer.fullchain.pem /etc/coturn/certs/$FqdnTurnServer.privkey.pem
chmod 400 /etc/coturn/certs/$FqdnTurnServer.fullchain.pem /etc/coturn/certs/$FqdnTurnServer.privkey.pem

Wie bekannt ist, sind Zertifikate welche von Let’s Encrypt ausgestellt worden sind, immer nur 90 Tage gültig. Durch die Verwendung von certbot erfolgt die Erneuerung ohne weitere manuelle Eingriffe. Damit Coturn dies auch mitbekommt ist ein sogenannter Hook notwendig. Dabei handelt es sich um Interaktionen die nach erfolgreichem Abruf des neuen Zertifikats ausgeführt werden sollen.

cat << \EOF > /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh
#!/bin/sh
 
set -e
 
for domain in $RENEWED_DOMAINS; do
        case $domain in
        turn.meet.wydler.eu)
                daemon_cert_root=/etc/coturn/certs
 
                # Make sure the certificate and private key files are
                # never world readable, even just for an instant while
                # we're copying them into daemon_cert_root.
                umask 077
 
                cp "$RENEWED_LINEAGE/fullchain.pem" "$daemon_cert_root/$domain.fullchain.pem"
                cp "$RENEWED_LINEAGE/privkey.pem" "$daemon_cert_root/$domain.privkey.pem"
 
                # Apply the proper file ownership and permissions for
                # the daemon to read its certificate and key.
                chown turnserver "$daemon_cert_root/$domain.fullchain.pem" \
                        "$daemon_cert_root/$domain.privkey.pem"
                chmod 400 "$daemon_cert_root/$domain.fullchain.pem" \
                        "$daemon_cert_root/$domain.privkey.pem"
 
                service coturn restart
                ;;
        esac
done
EOF

Das Skript stammt von Servervault. In Zeile 7 bitte den DNS-Namen anpassen.

Damit die Datei auch ausfühbar ist, muss das entsprechende Attribut gesetzt werden.

chmod 700 /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh

Konfiguration von Coturn

Die Original Konfiguration sichere ich wie immer vorher weg.

mv /etc/turnserver.conf /etc/turnserver.conf.$(date '+%Y-%m-%d_%H-%M-%S')

Der Service soll zu dem seine Protokolle in einen separaten Ordner ablegen, damit die Übersicht gewahrt wird.

mkdir -p /var/log/coturn/
chown -R turnserver:turnserver /var/log/coturn/

Es muss vorweg noch ein stärkerer DHE-Parameter erzeugt werden. Das kann ein bisschen Zeit in Anspruch nehmen. Zeit für eine Tasse Tee. 😉

openssl dhparam -out /etc/coturn/certs/dhparam4096.pem 4096

Nachstehend eine vollständige Musterkonfiguration, die bei meinen Tests problemlos funktioniert hat.

cat << EOF > /etc/turnserver.conf
# Listener IP address of relay server. Multiple listeners can be specified.
external-ip=116.203.191.22
external-ip=2a01:4f8:c0c:1c8b::1
 
# TURN listener port for UDP and TCP (Default: 3478).
listening-port=3478
 
# TURN listener port for TLS (Default: 5349).
tls-listening-port=443
 
# The default realm to be used for the users when no explicit origin/realm relationship was found in the database. Must be used with long-term
# credentials mechanism or with TURN REST API.
realm=${FqdnTurnServer}
 
# Certificate file.
cert=/etc/coturn/certs/${FqdnTurnServer}.fullchain.pem
 
# Private key file.
pkey=/etc/coturn/certs/${FqdnTurnServer}.privkey.pem
 
#Do not allow an TLS/DTLS version of protocol
no-tlsv1
no-tlsv1_1
 
# Allowed OpenSSL cipher list for TLS/DTLS connections.
cipher-list="ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA"
 
# Use custom DH TLS key, stored in PEM format in the file.
dh-file=/etc/coturn/certs/dhparam4096.pem
 
# Uncomment to use long-term credential mechanism.
lt-cred-mech
 
# This allows TURN credentials to be accounted for a specific user id.
use-auth-secret
 
# 'Static' authentication secret value (a string) for TURN REST API only.
static-auth-secret=replace-this-secret
 
# Flag that can be used to disallow peers on the loopback addresses (127.x.x.x and ::1).
no-loopback-peers
 
# Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*).
no-multicast-peers
 
# Uncomment to use fingerprints in the TURN messages.
fingerprint
 
# Total allocation quota.
total-quota=50
 
# Set this option to limit the nonce lifetime.
# It defaults to 600 secs (10 min) if no value is provided. After that delay,
# the client will get 438 error and will have to re-authenticate itself.
stale-nonce=600
 
# Maximum server capacity.
# Total bytes-per-second bandwidth the TURN server is allowed to allocate
bps-capacity=0
 
# Flag to prevent stdout log messages.
#no-stdout-log
 
# Uncomment to run TURN server in 'normal' 'moderate' verbose mode.
#verbose
 
# Option to set the log file name.
log-file=/var/log/coturn/coturn.log
 
# User name to run the process.
proc-user=turnserver
 
# Group name to run the process.
proc-group=turnserver
 
EOF

Die beiden Platzhalter für die Parameter ’static-auth-secre‘ und ‚user‘ werden nun durch Zufallspasswörter ersetzt. Dazu nehme ich OpenSSL als technische Hilfe zur Hand.

sed -i "s/replace-this-secret/$(openssl rand -hex 32)/" /etc/turnserver.conf

Vor dem Test der Konfiguration nicht vergessen, den Service neu zu starten.

service coturn restart
service coturn status

Testen der Konfiguration

Jeder Konfiguration muss selbstverständlich auch ausprobiert werden. Dazu greife ich auf die  Trickle ICE (WebRTC) zurück.

Die vorhandenen Einträge entfernen. Anschließend die Zugangsdaten für den eigenen Server hinzufügen.

Den TURN username und TURN password muss mit folgendem Befehl auf dem TURN-Server ausgelesen werden.

secret=$(awk -F "=" '/static-auth-secret/ {print $2}' /etc/turnserver.conf) && time=$(date +%s) && expiry=8400 && \
username=$(( $time + $expiry )) && echo username:$username && \
echo password : $(echo -n $username | openssl dgst -binary -sha1 -hmac $secret | openssl base64)

Als Ausgabe wird ein Benutzername und Passwort ausgegeben. Die Zugangsdaten sind für 24 Stunden (=86400 Sekunden) gültig. Sind die Angaben vollständig über „Add Server“ die Angaben übernehmen.

Der Test wird mit „Gather candidates“ gestartet. Im besten Fall sieht das Resultat so aus.

Wichtig ist, dass in der letzten Zeile, in der Splate „Priority“das Wort „Done“ steht. Anderenfalls war der Test nicht erfolgreich.

Anpassen der Jitsi Meet Konfiguration

Ist der TURN-Server einsatzbereit, muss dieser Jitsi Meet natürlich noch ‚bekannt‘ gemacht werden.

Zuerst lasse ich nochmals auf Kommandozeile die beiden Passwörter aus der Coturn Konfiguration ausgeben. Somit können diese Copy & Paste wendet werden. Die Platzhalter in den nachstehenden Konfigurationen dazu heißen <replace-this-password> und <replace-this-secret>.

awk -F "=" '/static-auth-secret/ {print $2}' /etc/turnserver.conf

Nachfolgende Schritte sind auf dem Server auszuführen, auf dem Jitsi Meet installiert ist.

Definition des DNS-Namen, unter dem die Jitsi Installation erreichbar ist. Somit sind in der Regel keine Anpassungen an den weiteren Befehlen notwendig.

export FqdnJitsiServer=meet.wydler.eu
echo $FqdnJitsiServer

export FqdnTurnServer=turn.meet.wydler.eu
echo $FqdnTurnServer

 

cp /etc/jitsi/meet/$FqdnJitsiServer-config.js /etc/jitsi/meet$FqdnJitsiServer-config.js.$(date '+%Y-%m-%d_%H-%M-%S')

sed -i -e 234c"\    useStunTurn: true," /etc/jitsi/meet/$FqdnJitsiServer-config.js
sed -i -e 344i"\            { urls: 'stun:$FqdnTurnServer:443' }," /etc/jitsi/meet/$FqdnJitsiServer-config.js

sed -i '345,347 s/^/\/\//' /etc/jitsi/meet/$FqdnJitsiServer-config.js
cp /etc/prosody/conf.avail/$FqdnJitsiServer.cfg.lua /etc/prosody/conf.avail/$FqdnJitsiServer.cfg.lua.$(date '+%Y-%m-%d_%H-%M-%S')

sed -i -e 6c"turncredentials_secret = \"<replace-this-secret>\";" /etc/prosody/conf.avail/$FqdnJitsiServer.cfg.lua
sed -i -e 9c"\  { type = \"stun\", host = \"$FqdnTurnServer\", port = \"443\" }," /etc/prosody/conf.avail/$FqdnJitsiServer.cfg.lua
sed -i -e 10c"\  { type = \"turn\", host = \"$FqdnTurnServer\", port = \"443\", transport = \"udp\" }," /etc/prosody/conf.avail/$FqdnJitsiServer.cfg.lua
sed -i -e 11c"\  { type = \"turns\", host = \"$FqdnTurnServer\", port = \"443\", transport = \"tcp\" }" /etc/prosody/conf.avail/$FqdnJitsiServer.cfg.lua
cp /etc/jitsi/videobridge/sip-communicator.properties /etc/jitsi/videobridge/sip-communicator.properties.$(date '+%Y-%m-%d_%H-%M-%S')
sed -i -e 2c"org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES=$FqdnTurnServer:443" /etc/jitsi/videobridge/sip-communicator.properties

Zu guter Letzt müssen die betroffenen Services neu gestartet werden, damit die Änderungen wirksam werden.

service prosody restart
service jicofo restart
service jitsi-videobridge2 restart

Nachdem Neustart wird im Protokoll des TURN Service folgende Einträge geschrieben.

tail -f /var/log/coturn/coturn_2020-04-18.log

482: handle_udp_packet: New UDP endpoint: local addr 45.12.48.z:443, remote addr 45.12.48.z:58373
482: session 000000000000000001: realm <turn.meet.wydler.eu> user <>: incoming packet BINDING processed, success

Das ist schon einmal die halbe Miete. Um die Funktionalität zu prüfen, muss ein Jitsi Konferenz mit zwei Teilnehmern ausgebaut werden. Sobald die Verbindung steht

114: IPv4. tcp or tls connected to: 37.201.4.z:23881
114: session 000000000000000003: realm <turn.meet.wydler.eu> user <>: incoming packet message processed, error 401: Unauthorized
114: IPv4. Local relay addr: 116.203.191.z:11411
114: session 000000000000000003: new, realm=<turn.meet.wydler.eu>, username=<1587063894>, lifetime=3600, cipher=ECDHE-RSA-AES256-GCM-SHA384, method=TLSv1.2
114: session 000000000000000003: realm <turn.meet.wydler.eu> user <1587063894>: incoming packet ALLOCATE processed, success
114: session 000000000000000003: peer 116.203.40.y lifetime updated: 300
114: session 000000000000000003: realm <turn.meet.wydler.eu> user <1587063894>: incoming packet CREATE_PERMISSION processed, success
115: IPv4. tcp or tls connected to: 109.41.195.z:6046
115: session 001000000000000001: TLS/TCP socket disconnected: 109.41.195.z:6046
115: session 001000000000000001: closed (2nd stage), user <> realm <turn.meet.wydler.eu> origin <>, local 116.203.191.z:443, remote 109.41.195.z:6046, reason: TLS/TCP socket buffer operation error (callback)

Einrichten von Logrotate für Coturn

Es wird grundsätzlich beim Start des Service eine Logdatei angelegt. Die Ausgaben werden fortlaufend in diese geschrieben, bis der Service neu gestartet wird. Je nachdem wird solch eine Datei groß und übersichtlich werden. Zudem wird beim Logging kein Datum und Uhrzeit angegeben. Somit ist eine Zuordnung schwierig. Mit Hilfe von Logrotate kann täglich eine neue Protokolldatei erzeugt werden.

cat << EOF > /etc/logrotate.d/coturn
/var/log/coturn/coturn_*.log
{
    su turnserver turnserver
    rotate 30
    daily
    missingok
    notifempty
    delaycompress
    compress
    postrotate
    create 0640 turnserver:turnserver
    systemctl kill -sHUP coturn.service
    endscript
}
EOF

Ob die Konfiguration sollte natürlich geprüft werden. Entweder man wartet einen Tag ab oder führt folgenden Befehl aus:

logrotate -v -f /etc/logrotate.d/coturn

Firewall

Last but not Least kommen wir nochmals auf den Punkt Sicherheit zurück.  Selbstverständlich darf die Grundkonfiguration einer Firewall grundsätzlich nicht fehlen.  Ich greife ich hier auf UFW zurück. Diese ist inzwischen standardmäßig bei der eingesetzten Distribution und Version standardmäßig dabei.

ufw allow ssh
ufw allow https
ufw allow in 443/udp
ufw allow in 3478/udp
ufw allow in 3478/tcp

echo y | ufw enable
ufw status

Fertig ist der TURN-Server. Viel Spaß beim Ausprobieren. 🙂

25
Hinterlasse einen Kommentar

avatar
5 Kommentar Themen
20 Themen Antworten
0 Follower
 
Kommentar, auf das am meisten reagiert wurde
Beliebtestes Kommentar Thema
6 Kommentatoren
TobiasdwJohannesMarkoJan Letzte Kommentartoren
  Abonnieren  
neueste älteste
Benachrichtige mich bei
Johannes
Gast
Johannes

Ich habe eine sehr ähnliche Konfiguration (Ubuntu 20.04); leider meldet der Server im Logfile

0: log file opened: /var/log/turn_70603_2020-05-17.log
0: You cannot define external IP more than once in the configuration

das liegt wohl daran, dass ich wie bei Ihnen eine IPv4 und eine IPv6 konfigurieren will. Wissen Sie woran das liegt?

Tobias
Gast
Tobias

Hallo, erstmal vielen vielen Dank für diesen Artikel. Gefühlt bringt er mich in meiner Sache jetzt schon weiter. Wir betreiben einen Jitsi in einer DMZ, haben allerdings das Problem , das dieser nur funktioniert wenn man im „gleichen“ Netzwerk ist. Also meetings zwischen Internen (Firmennetzwerk) und Externen(Internet) funktionieren nicht. Ich hoffe das ich dieses Problem mit einem Seperaten Turn Server beseitigen kann. Und dazu hätte ich eine Frage, der sperate Turn Server benötigt eine eigne öffentliche IP? Welche Ports müssen weiter geleitet werden ? Reicht 443 oder bedarf es noch mehr ? Ich bin jetzt schon mega Dankbar und sollte… Weiterlesen »

Marko
Gast
Marko

Hallo, leider bekomme ich nicht den STUN Server zum laufen..
Die ersten Probleme fangen schon an den USER und das PW auszulesen. Da bekomme ich nur den USER das PW bleibt leer.
Des weiteren kann ich den STUN Server nicht erfolgreich testen da mir die srflx Einträge fehlen.

Auch sehe ich nicht das sich Jitsi Meet mit dem STUN verbindet.

Jan
Gast
Jan

Hallo, vielen Dank für die tolle Beschreibung.. Reicht es eigentlich aus wenn ich den tls-listening-port=443 verwende und auf den listening-port=3478 verzichte? Oder sind da beide Ports zwingend notwendig?

Eduard Itrich
Gast
Eduard Itrich

Vielen Dank für diese sehr ausführliche Anleitung. Leider erhalte ich lediglich den Fehler: TLS/TCP socket buffer operation error (callback)
Ich nutze coturn 4.5.0.7-1ubuntu2.18.04.1 und betreibe diesen in der Azure Cloud. Lokale und externe IP habe ich eigentlich richtig gesetzt, trotzdem kommt zwischen zwei Teilnehmern kein Austausch zustande.

Ich habe lediglich diesen Issue auf Github https://github.com/coturn/coturn/issues/113#issuecomment-610762334 entdeckt.

Liegt es an einem Fehler in dieser Version von coTURN oder nutzt du dieselbe Version?

Back to top