Jitsi: una piattaforma OpenSource di videoconferenza – Server STUN e TURN in proprio

0

Ora che abbiamo visto come si usa e come si installa un server Jitsi, possiamo completare la nostra infrastruttura di videoconferenza con un server STUN/TURN in proprio.

Inquadramento tecnico

Forse conviene partire nell’esplicitare un dettaglio importante, ovvero che esistono (a livello pratico) due tipi di connessioni IP (chiamati protocolli):

  1. TCP: canale bidirezionale (e consegna garantita);
  2. UDP: consenga non garantita e canale unidirezionale.

La normale navigazione via browser usa TCP, mentre una conversazione audio/video (di solito) UDP. E per avere una comunicazione bidirezionale UDP, è l’applicazione a dover preparare due canali: uno di ricezione ed uno di trasmissione.

Quando (come succede spesso) entrambi i dispositivi sono dietro NAT, la comunicazione UDP diretta richiesta da applicazioni come Jitsi è difficile (se non impossibile), perché non è possibile creare il canale in ricezione direttamente.

Piccola digressione tecnica: NAT

Quando un computer si trova in una rete, i computer raggiungibili direttamente sono solo quelli della propria rete.
Per raggiungere altre dispositivi in altre reti si deve passare da un dispositivo (gateway – cancello), che conosce la strada (route – rotta) verso il punto di accesso (il gateway) dell’altra rete; succede anche di dover passare da più di un gateway, facendo diversi salti (hop). Internet è proprio questo insieme di reti che parlano tra loro.

Il più delle volte, la rete locale è privata, usa cioé degli indirizzi IP che non sono validi per Internet (si dice che usa indirizzi IP pubblici). Il gateway (per noi il router di casa, fornito dalla compagnia telefonica) fa quindi anche un altro lavoro: NAT (Network Address Translation – Traduzione dell’indirizzo di rete). E si dice che il dispositivo è dietro NAT.
Da un punto di vista tecnico ne esistono varie implementazioni, ma il meccanismo di base è sempre lo stesso.

Ogni volta che un dispositivo della rete locale vuole contattare un host via internet, il gateway:

  1. si segna quale dispositivo abbia fatto la richiesta;
  2. sostituisce l’indirizzo (privato) del dispositivo richiedente con il proprio (pubblico);
  3. manda la richiesta;
  4. il dispositivo contattato vede solo il gateway, e risponderà ad esso;
  5. il gateway, appena ricevuta la risposta, la inoltrerà al dispositivo che aveva fatto la richiesta (visto che se lo era segnato nel punto 1).

Il meccanismo funziona molto bene quando è un dispositivo della rete interna a dover contattare un dispositivo della rete esterna, ma il contrario non funziona perché il gateway non sa a chi consegnare la richiesta.
Quindi, se vogliamo offrire un servizio (ospitare un server) dietro NAT, dobbiamo usare anche un altro meccanismo: il Port Forwarding (inoltro della porta) – detto anche Port Mapping (mappatura della porta). In pratica si istruisce il gateway ad inoltrare le richieste che arrivano su una certa porta (e con un certo protocollo) ad un certo host (talvolta cambiando anche la porta).

Cosa sono STUN/TURN

Per aggirare il problema esistono proprio i protocolli STUN (Session Traversal Utilities for NATs – utility per [dispositivi] NAT per [gestione di] una sessione di attraversamento) e TURN (Traversal Using Relays around NAT – Aggiramento di NAT usando ripetitori).
N.d.T.: le traduzioni cercano di esplicitare acronimi che, di per loro, hanno poco significato.

Un server STUN permette a un dispositivo di conoscere l’indirizzo pubblico con cui si presenta, e dietro a quale tipo di NAT si trova. Il server, semplicemente, vedendo il pacchetto ricevuto, risponderà con i dati di connessione presenti (IP, porta), in modo che il client sappia con quale indirizzo pubblico (e su quale porta) è raggiungibile. A questo punto il compito del server è finito, e i dispositivi sapranno come dovranno apparire per contattarsi direttamente.
Per la grandissima parte delle situazioni, questo aiuto è sufficiente.

Ma quando questo non basta, viene usato il server TURN che fa proprio da ripetitore (relay): il dispositivo stabilisce una connessione bidirezionale al server TURN, e manda il suo flusso a questo, che a sua volta lo inoltrerà al vero dispositivo da raggiungere. La risposta farà la stessa strada, a ritroso.

Entrambe le tecnologie sono le fondamenta delle definizioni ICE (Interactive Connectivity Establishment – instauarazione di una connessione interattiva), che a loro volta fanno parte di webRTC (web Real Time Comunication – Comunicazione in tempo reale via web), ovvero la tecnologia che usa Jitsi (come praticamente tutti gli altri software di videocomunicazione attuali). Questo è da tenere a mente per la configurazione del server, in quanto per le funzioni TURN sono necessarie delle impostazioni specifiche, dettate da webRTC.

Requisiti

Proprio perché questi server devono essere una boa in Internet, il primo requisito è la disponibilità di un IP pubblico conosciuto. L’ideale è averne uno statico, cosa che esclude le connessioni la maggioranza delle connessioni casalinghe (che in genere usano IP pubblico dinamici).
Questo non vuol dire che non è possibile farselo anche in casa, ma solo che ad ogni riconnessione del router la probabilità di aver cambiato l’indirizzo pubblico ci costringerà ad una modifica di configurazione del server STUN/TURN.
Nota: essendo un servizio su internet, deve essere raggiungibile da chiunque come indicato nella configurazione. Noi, negli esempi, useremo l’hostname per coerenza con quanto fatto finora, ma non funzionerà: il dominio non è registrato. In questi casi è perfettamente valido usare l’IP pubblico direttamente, al posto dell’hostname.

Per offrire il servizio useremo lo standard open-source de facto, ovvero coturn.
Questa applicazione è in grado di gestire entrambi i protocolli, anche se le risorse richieste per i due usi sono parecchio diverse.

  • STUN è leggero, serve solo nella fase iniziale, non ha bisogno di nessun altro dato.
  • TURN richiede di tenere traccia (in un proprio DB) dei device connessi, con un’autenticazione a tempo, e di instaurare e mantenere una connessione per ognuno. Inoltre, può lavorare con UDP e TCP. Non solo richiede un po’ di CPU e memoria, quindi, ma anche banda per ogni device.

Per comodità noi installeremo il server Jitsi e il server coturn sulla stessa macchina, ma questo non è affatto richiesto. Anzi: ha perfettamente senso installare Jitsi su una macchina in casa, dietro NAT e con IP pubblico dinamico, mentre il server STUN su un server con IP statico, con meno risorse proprio perché deve fare meno lavoro.

Installazione

Il pacchetto su Debian si chiama proprio coturn, quindi l’installazione con il fidato apt-get è facile:

root@jitsi:~$ apt-get install --no-install-recommends coturn
Lettura elenco dei pacchetti... Fatto
Generazione albero delle dipendenze       
Lettura informazioni sullo stato... Fatto
I seguenti pacchetti aggiuntivi saranno inoltre installati:
  libevent-core-2.1-6 libevent-extra-2.1-6 libevent-openssl-2.1-6 libevent-pthreads-2.1-6 libhiredis0.14 libmariadb3 libpq5 mariadb-common mysql-common sqlite3
Pacchetti suggeriti:
  sip-router sqlite3-doc
I seguenti pacchetti NUOVI saranno installati:
  coturn libevent-core-2.1-6 libevent-extra-2.1-6 libevent-openssl-2.1-6 libevent-pthreads-2.1-6 libhiredis0.14 libmariadb3 libpq5 mariadb-common mysql-common sqlite3
0 aggiornati, 11 installati, 0 da rimuovere e 0 non aggiornati.
È necessario scaricare 2.037 kB di archivi.
Dopo quest'operazione, verranno occupati 6.315 kB di spazio su disco.
[...]
Elaborazione dei trigger per man-db (2.8.5-2)...
Elaborazione dei trigger per libc-bin (2.28-10)...

Da poco tempo il repository Jitsi fornisce un pacchetto per l’installazione e la configurazione di coturn: jitsi-meet-turnserver.
Il vantaggio (facilmente immaginabile) ad usare questo componente è che la configurazione di Jitsi è (quasi) automatica, ma prevede anche tutti i componenti sullo stesso server.
Pertanto, chi volesse usare il pacchetto jitsi-meet-turnserver, potrà farlo: troverà semplicemente alcune operazioni già fatte.

Configurazione base: STUN

L’applicazione coturn è configurata da opzioni a linea di comando; le stesse opzioni possono essere specificate in un file: /etc/turnserver.conf. Questa è la cosa più comoda.
La sintassi è semplice: opzione=valore. Per le opzioni che non prevedono valori da impostare, si usa solo il nome dell’opzione.

L’elenco di scelte disponibili è piuttosto lungo e complesso, anche perché coturn può esporre una serie di API e interfacce per configurazione e controlli avanzati; nel file fornito le troverete più o meno tutte, spiegate e commentate (cioé non attive).
Se volete studiarvi il file, suggeriamo di rinominarlo o copiarlo da qualche parte: noi costruiremo un file ex-novo, partendo quindi da uno vuoto.

Per prima cosa il logging. Abbiamo due scelte:

  1. file di log
  2. syslog

La prima opzione scrive nel file di log in una posizione conosciuta; in questo caso si potrebbe voler aggiungere simple-log per evitare che ad ogni avvio venga generato un file nuovo, con la data nel nome.

log-file=/var/log/turnserver/turn.log
simple-log

Noi sfrutteremo la seconda opzione

syslog

Coturn di defautl si mette in ascolto su tutti gli IP che trova, ma possiamo indicare quali usare; l’opzione viene semplicemente ripetuta per ogni IP.

[listening-ip=<ip>]

La porta di ascolto è quella sulla quale viene offerto il servizio; ne sono necessarie due, e di default viene usata come alternativa la porta successiva.
In caso di NAT dovremo fare il Port Forwarding di entrambe.

listening-port=3478
alt-listening-port=3479

Sempre in caso di NAT, è necessario specificare il mapping tra IP eseterno ed IP interno; anche in questo caso possiamo specificarlo più volte, una per ogni IP pubblico.

external-ip=<ip-pubblico>/<ip-privato>

Già con queste poche opzioni attive, il servizio STUN è pronto.
Se si volesse usare il server solo come STUN (e in effetti dovrebbe bastare per il 90% dei casi), possiamo usare l’opzione no-turn.
Possiamo disattivare esplcitamente anche altre funzioni, come la crittografia dei pacchetti con no-tls e no-dtls.

Quindi, per ora, il nostro file si presenta così:

syslog
listening-ip=192.168.xxx.xxx
listening-port=3478
alt-listening-port=3479
external-ip=2.45.xxx.xxx/192.168.xxx.xxx
no-turn
no-tls
no-dtls

Riavvio del server…

root@jitsi:~$ systemctl restart coturn

…e siamo online!

Configurazione Jitsi: STUN

Ora che abbiamo il server STUN in piedi, configuriamo i tre componenti di Jitsi coinvolti:

  1. prosody;
  2. videobridge;
  3. jitsi-meet.

prosody

Il file di confgurazione in questo caso è uno di quelli che contiene l’hostname: /etc/prosody/conf.avail/jitsi.mmul.local.cfg.lua.
Dobbiamo creare (o modificare) l’opzione turncredentials, ovvero una lista di server STUN/TURN. Possiamo decidere di aggiungere prima o dopo quelli eventualmente presenti il nostro server. Ma, visto che siamo in autoarchia, noi li sostituiremo tutti.

turncredentials = {
  { type = "stun", host = "jitsi.mmul.local", port = "3478" },
};

videobridge

Nel file /etc/jitsi/videobridge/sip-communicator.properties inseriamo il nostro server:

org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES=jitsi.mmul.local:3478

L’opzione accetta una lista di server, seprati da virgola ,.
Anche in questo caso, noi optiamo per la sostituzione delle impostazioni eventualmente già presenti.

jitsi-meet

Infine, la configurazione del client: anche lui deve essere informato!
Nel file /etc/jitsi/meet/jitsi.mmul.local-config.js cerchiamo stunServers, per inserire il nostro server nella lista:

// [...]
    p2p: {
        // [...]
        stunServers: [
            { urls: 'stun:jitsi.mmul.local:3478' },
        ],
        // [...]

La modifica è necessaria (e valida) solo per la parte p2p (peer-to-peer – conversazione diretta a due), in quanto per quella di gruppo abbiamo già configurato prosody e videobridge.

Riavvio di prosody e videobridge per rendere effettive le modifiche:

root@jitsi:~$ systemctl restart prosody jitsi-videobridge2

Configurazione avanzata coturn: TURN

La configurazione diventa un poco più complicata, ora, ma allo stesso tempo gli stretti vincoli per la compatibilità con webRTC ci lasciano una strada molto stretta.
Ovviamente, casomai fosse presente, togliamo l’opzione no-turn.

Ogni singolo dispositivo che si colleghi deve autenticarsi, e come parte dell’autenticazione è richiesto un dominio, ma non è richiesto che sia il client a fornirlo. Pertanto, ne va definito uno di default. Possiamo usare quello che vogliamo, ma l’hostname sembra una scelta ovvia.
Quindi, nel file di configurazione di coturn aggiungiamo:

realm=jitsi.mmul.local

Ora bisogna impostare il meccanismo di autenticazione, che è piuttosto complesso e studiato per dare autorizzazioni temporanee con password scambiate su internet sempre diverse.
Questo viene raggiunto generando le credenziali grazie ad un segreto condiviso tra client (videobridge) e server TURN.
Un modo rapido per generare una buona chiave è usare una stringa random, per esempio con openssl:

root@jitsi:~$ openssl rand -hex 32
da75511a961e53eedc64b762dc5b9dee87fd97ce566c6766a1a29c00792b67dd

Ce lo segnamo da qualche parte e lo impostiamo in coturn:

use-auth-secret
static-auth-secret=da75511a961e53eedc64b762dc5b9dee87fd97ce566c6766a1a29c00792b67dd

Aggiungiamo le ultime disposizioni necessarie per webRTC…

fingerprint
mobility

Se avete necessità di limitare le porte da usare per fare il lavoro di relay (per esempio, per inoltrare via NAT solo un range di porte e non tutte), i paramentri min-port e max-port fanno al caso vostro. Nel nostro caso, li metteremo ma commentati (giusto per ricordarci che, se ci servono, ci sono).

Ecco quindi il nostro file ora:

syslog
listening-ip=192.168.xxx.xxx
listening-port=3478
alt-listening-port=3479
external-ip=2.45.xxx.xxx/192.168.xxx.xxx
no-tls
no-dtls
realm=jitsi.mmul.local
use-auth-secret
static-auth-secret=da75511a961e53eedc64b762dc5b9dee87fd97ce566c6766a1a29c00792b67dd
fingerprint
mobility
fingerprint
mobility
# min-port=44000
# max-port=44999

Riavvio.

root@jitsi:~$ systemctl restart coturn

Configurazione Jitsi: TURN

Stavolta modifichiamo solo prosody.

prosody

Dobbiamo inserire la chiave e indicare che ora il nostro server è anche TURN.
Aggiungiamo (o modifichiamo) l’opzione turncredentials_secret, sempre in /etc/prosody/conf.avail/jitsi.mmul.local.cfg.lua:

turncredentials_secret = "da75511a961e53eedc64b762dc5b9dee87fd97ce566c6766a1a29c00792b67dd";

ed aggiungiamo anche il server TURN, specificando sia il protocollo UDP che il protocollo TCP:

turncredentials = {
  { type = "stun", host = "jitsi.mmul.local", port = "3478" },
  { type = "turn", host = "jitsi.mmul.local", port = "3478", transport = "udp" },
  { type = "turn", host = "jitsi.mmul.local", port = "3478", transport = "tcp" },
};

Riavviamo:

root@jitsi:~$ systemctl restart prosody

jitsi-meet

Non tocchiamo la configurazione di jitsi-meet perché già pronto per usare STUN. Per l’uso di TURN sono necessarie le credenziali, e la chiave segreta che (per l’appunto) non deve essere distribuita. Quindi, come fa? Jitsi si appoggia a prosody: chiede a questo componente (che conosce la chiave) il server e le credenziali da usare.

Note finali e TLS

La possibilità di effettuare chimate p2p è comoda per scaricare il server Jitsi dal carico di conversazioni con solo due persone, dove un accentratore non è necessario, ma è disattivabile: utile per fare qualche test del server senza coinvolgere una terza o quarta connessione.

// [...]
   p2p: {
        enabled: false,
// [...]

Come già specificato, coturn è parecchio complesso, ed ha anche altre possibilità, come l’uso di comunicazione criptate (TLS). Però…

  • È necessario comunque un supporto da parte dei vari client; per Jitsi, per esempio, è ancora in fase sperimentale.
  • Non si tratta di criptazione end-to-end, ma semmai a pezzi: ogni connessione al server TURN sarà criptata indipendentemente, quindi il flusso sarà decriptato e poi recriptato prima dell’inoltro. Oltre alla questione sicurezza (se qualcuno dovesse riuscire ad avere accesso al server, avrebbe accesso anche ai dati in chiaro), si apre un problema prestazionale: quelle due fasi aggiungono parecchio overhead e ritardi di trasmissione.

Ma, dato che essere pronti non fa male, ecco una configurazione usabile (ovviamente al posto di eventuali no-tls e/o no-dtls).

tls-listening-port=5349
alt-tls-listening-port=5350
pkey=/path/to/PEM/private.key
cert=/path/to/PEM/certificate_chain.crt
dh-file=/path/to/PEM/dh.pem

Infine Jitsi ha una serie di altre possibiltà, che non tratteremo:

  • registrazione video (sul server o DropBox);
  • integrazione con SIP (chiamate VoIP);
  • server videobridge multipli e load balancing;
  • monitoraggio prestazioni e utenti;
  • integrazione con altri sistemi di chat;
  • etc…

Voi siete liberi di sperimentare, magari contribuire al progetto… e farci sapere: potreste essere proporio voi a proseguire questa serie!

Ho coltivato la mia passione per l’informatica fin da bambino, coi primi programmi BASIC. In età adulta mi sono avvicinato a Linux ed alla programmazione C, per poi interessarmi di reti. Infine, il mio hobby è diventato anche il mio lavoro.
Per me il modo migliore di imparare è fare, e per questo devo utilizzare le tecnologie che ritengo interessanti; a questo scopo, il mondo opensource offre gli strumenti perfetti.