Je WebRTC-app werkt op je eigen wifi en faalt bij precies die ene klant die vanuit een hotel belt. Peer-to-peer-media hebben een route tussen beide kanten nodig; zitten beide achter strenge NAT, of laat een firewall UDP vallen, dan is er geen route en komt het gesprek gewoon niet tot stand. Niemand ziet een foutmelding — alleen een eindeloos „verbinden…”.
Een TURN-server is de terugvaloptie voor precies die gesprekken: beide kanten sturen hun media ernaartoe en hij stuurt ze door. Bouw je video of spraak in een app, draai je Matrix of Jitsi voor een team of maak je voicechat voor een game, dan wil je er een. coturn is de standaard opensource-implementatie, en die is licht.
Welk plan
TURN vraagt weinig CPU en geheugen. Wat het gebruikt is bandbreedte: elke doorgestuurde stream gaat twee keer door de server, erin en eruit. Verkeer is op onze plannen onbeperkt op 1 Gbit/s, dus de vraag is hoeveel doorgestuurde gesprekken er tegelijk lopen, niet hoeveel je gefactureerd krijgt.
| Tegelijk doorgestuurde gesprekken | Plan |
|---|---|
| Een handvol — een team, een kleine app | Nano-IP: 2 vCPU, 1 GB, 15 GB ($8/mnd) |
| Tientallen | Micro-IP: 2 vCPU, 2 GB, 25 GB ($10/mnd) |
| Meer, of TURN naast je appserver | Small-IP: 4 vCPU, 4 GB, 35 GB ($16/mnd) |
Het moet een plan met een eigen IPv4 zijn — bij TURN draait alles om inkomende verbindingen op veel poorten, en NAT-plannen accepteren van buitenaf alleen SSH. Op onze plannen met een eigen IP staat het publieke adres direct op de interface van de server, dus coturn vindt het zonder external-ip-regel. Zet de server dicht bij je gebruikers: Duitsland of Finland, de beschikbaarheid zie je bij het bestellen. Betalen met crypto, zonder KYC, als je dat belangrijk vindt.
Te openen poorten: 3478 UDP en TCP, 49152–65535 UDP voor de doorgestuurde media en 5349 TCP als je TLS toevoegt.
Installeren en configureren
Als root op Ubuntu 24.04 of Debian 12:
apt install -y coturn
ip -4 addr # je publieke IP hoort op de interface te staan
Schrijf de configuratie met een vers gedeeld geheim:
SECRET=$(openssl rand -hex 32)
cat > /etc/turnserver.conf <<EOF
listening-port=3478
fingerprint
use-auth-secret
static-auth-secret=$SECRET
realm=turn.example.com
min-port=49152
max-port=65535
no-cli
no-multicast-peers
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.168.0.0-192.168.255.255
EOF
echo "$SECRET" # je backend heeft deze waarde nodig
systemctl restart coturn
ufw allow 22/tcp && ufw allow 3478 && ufw allow 49152:65535/udp && ufw enable
Sla de denied-peer-ip-regels niet over. Zonder die regels kan iedereen met geldige inloggegevens via je relay private adressen bereiken — ook diensten aan de interne kant van de server zelf. We hebben het gecontroleerd: met deze regels wordt een relay naar een privéadres geweigerd met „403 Forbidden IP”.
Inloggegevens die verlopen
Met use-auth-secret accepteert coturn een gebruikersnaam die een verlooptijd bevat en een wachtwoord dat daar een HMAC van is. Je backend genereert ze; het geheim verlaat nooit de serverkant. In Node.js:
const crypto = require("crypto");
// Tijdelijke TURN-gebruikersnaam en -wachtwoord (het TURN REST API-schema dat coturn via use-auth-secret begrijpt).
function turnCredentials(secret, user, ttlSeconds = 3600) {
const username = `${Math.floor(Date.now() / 1000) + ttlSeconds}:${user}`;
const credential = crypto.createHmac("sha1", secret).update(username).digest("base64");
return { username, credential };
}
console.log(JSON.stringify(turnCredentials(process.env.TURN_SECRET, "alice")));
Vraag in de browser je backend om een vers paar en geef het aan RTCPeerConnection:
const { username, credential } = await fetch("/api/turn-credentials").then((r) => r.json());
const pc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:turn.example.com:3478" },
{
urls: ["turn:turn.example.com:3478?transport=udp", "turn:turn.example.com:3478?transport=tcp"],
username,
credential,
},
],
});
Testen
Vanaf de server, met hetzelfde schema via openssl (het levert precies hetzelfde wachtwoord als de Node.js-code — we hebben het vergeleken):
U="$(( $(date +%s) + 3600 )):test"
P=$(echo -n "$U" | openssl dgst -binary -sha1 -hmac "$SECRET" | base64)
turnutils_uclient -y -n 10 -u "$U" -w "$P" YOUR_SERVER_IP
Aan het eind wil je „Total lost packets 0” zien. Met een verkeerd wachtwoord stopt de client bij „Cannot complete Allocation” — dat is de server die zijn werk doet.
We hebben ook getest vanuit een echte Chromium: met iceTransportPolicy: "relay" en verse inloggegevens kreeg de browser relay-kandidaten van de server; met een fout wachtwoord geen enkele. Wil je je eigen opzet op dezelfde manier testen, zet die optie dan even aan — ze dwingt elk gesprek via TURN.
TLS, als je het nodig hebt
Sommige netwerken blokkeren UDP en zijn kieskeurig over gewone TCP; TURN over TLS komt door meer daarvan heen. Haal een certificaat voor turn.example.com (certbot volstaat). De bestanden ervan zijn alleen leesbaar voor root, terwijl coturn draait als gebruiker turnserver, dus geef coturn een eigen kopie:
mkdir -p /etc/coturn
cp /etc/letsencrypt/live/turn.example.com/fullchain.pem /etc/letsencrypt/live/turn.example.com/privkey.pem /etc/coturn/
chown turnserver: /etc/coturn/*.pem && chmod 640 /etc/coturn/privkey.pem
cat >> /etc/turnserver.conf <<'EOF'
tls-listening-port=5349
cert=/etc/coturn/fullchain.pem
pkey=/etc/coturn/privkey.pem
EOF
systemctl restart coturn && ufw allow 5349/tcp
Voeg turns:turn.example.com:5349?transport=tcp toe aan de urls-lijst. Certificaten zijn kortlevend, dus zet het kopiëren in een certbot-deploy-hook — anders gaat TLS bij de eerste vernieuwing stilletjes stuk.
Goed om te weten
- TURN maakt van een slechte verbinding geen goede. Het voegt één hop toe, dus zet de server dicht bij de mensen die hem nodig hebben.
- Draai je een SFU — de videobridge van Jitsi bijvoorbeeld —, dan is dat een apart onderdeel. TURN helpt de clients die die niet direct kunnen bereiken.
- Het geheim is het hele beveiligingsmodel. Houd het uit de frontend en vervang het als het ooit uitlekt.
Gerelateerd
- VPS voor Jitsi Meet: videogesprekken op je eigen server
- Matrix Synapse zelf hosten: ook gesprekken in Matrix hebben een TURN-server nodig
- VPS voor een website op een eigen IP: de app die de inloggegevens uitdeelt
- Een ufw-firewall configureren
Reacties
Nog geen reacties. Wees de eerste.