Die bisherigen Teile der Serie brachten Jitsi Meet für öffentliche Konferenzen zum Laufen. Diesmal geht es darum, nur befugten Nutzern Räume zu öffnen und externe Anrufer über SIP einzuladen.
|
Teil 1 |
LM 07/2020 |
Einführung in Jitsi Meet |
|
|
Teil 2 |
LM 08/2020 |
Installation unter Debian |
|
|
Teil 3 |
LM 10/2020 |
SIP und sichere Räume |
Als Vater eines schulpflichtigen Kinds betrifft auch den Autor dieses Artikels der Fernunterricht. Da es an der Schule des Nachwuchses (und im ganzen Bundesland) anfangs kein zentral vorgegebenes System für Videounterricht gab, setzte der Autor einen Jitsi-Meet-Server [1] auf und stellte ihn der Schule zur Verfügung.
Eine solche Lösung muss allerdings etwas mehr Kontrolle und Funktionen bieten als ein rein öffentlicher Jitsi-Server: Zum Beispiel sollen nur autorisierte Benutzer (also konkret Lehrer) dort Konferenzen eröffnen dürfen. Der Zutritt zum Konferenzlink lässt sich innerhalb der Konferenz ja noch über ein Passwort steuern.
Da derselbe Server auch für Sitzungen des Elternbeirats dienen soll, die eine Telefoneinwahl erfordern, erweiterte der Autor ihn noch um die Jigasi-Komponente [2]. Sie erlaubt es Eltern, sich über einen kostenfreien Sipgate-Account per Telefon in die Konferenz einzuklinken.
Installation
Die Grundinstallation der Software folgte im Wesentlichen der Beschreibung in den letzten beiden Artikeln. Es gibt jedoch einen kleinen Unterschied: Der Server läuft bei Amazon Web Services, da die Schule keinen eigenen Server betreibt.
Technisch bedeutet der Einsatz von AWS, dass die eigene IP-Adresse nicht direkt die öffentlich erreichbare IP-Adresse ist, sondern eingehend eine Network Address Translation (NAT) stattfindet. Jitsi Meet kommt mit solchen NAT-Szenarien zurecht, der Admin muss das aber in der Konfiguration explizit angeben.
Die »/etc/jitsi/videobridge/sip-communicator.properties« enthält in einer Standardinstallation gleich als erste Zeile die Anweisung, den AWS-Harvester zu deaktivieren (Wert »true«). Diesen Wert sollte der Admin beim Einsatz in einer VM bei AWS auf »false« umstellen (Listing 1, erste Zeile). Außerdem fügt er die beiden letzten Zeilen aus Listing 1 hinzu.
Listing 1
sip-communicator.properties
org.ice4j.ice.harvest.DISABLE_AWS_HARVESTER=false [...] org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=Private_IP_der_Instanz org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=Öffentliche_IP_der Instanz
Die beiden Werte findet der Admin im AWS-Dashboard wieder oder liest sie per CLI aus. Per Skript klappt das auch automatisiert, was Änderungen an der IP-Adresse berücksichtigt. Die URL http://169.254.169.254/latest/meta-data/public-ipv4 liefert die öffentliche IP, http://169.254.169.254/latest/meta-data/local-ipv4 die private. Mit diesen beiden Angaben funktioniert eine Jitsi-Meet-Instanz bei AWS dann genauso wie eine Instanz auf dem eigenen Rechner.
Es darf nicht jeder
Jitsi Meet besteht aus mehreren Komponenten. Den Zugriff steuert Prosody. Der Standardinstallationsprozess konfiguriert Prosody auf anonymen Zugriff. Das bedeutet, jeder Nutzer darf ein neues Meeting starten, indem er die URL https://jitsi-server.mein/>MeinMeeting in einem Webbrowser eingibt.
Dabei bietet der selbstdefinierte Ausdruck MeinMeeting bereits eine Option, Dritte aus dem Meeting fernzuhalten: Die müssten den Ausdruck erraten oder durch Probieren herausfinden. Dass bedeutet aber womöglich auch, dass Schüler im Homeschooling heimlich eigene Channels einrichten und sich dort mit ihren Peers vergnügen, statt den wertvollen Hinweisen des Lehrkörpers zu lauschen.
Die Konsequenz: Nur angemeldete Nutzer sollen neue Kanäle erzeugen dürfen. Dazu passt der Admin im ersten Schritt die Datei »/etc/prosody/conf.avail/jitsi.example.com.cfg.lua« an. Deren Struktur zeigt bereits, dass das System auf einem Host auch mehrere Sites zulässt (wie ein Webserver). Die Zuordnung der verschiedenen Komponenten erfolgt dabei über den Hostnamen der URL. Für den Rest des Artikels sei dies »jitsi.example.com«. Will der Admin diese Anpassung am eigenen Jitsi-Server nachvollziehen, muss er also in allen Dateinamen und Inhalten »jitsi.example.com« durch den eigenen Server-Namen plus Subdomain ersetzen.
Die für das Beispiel generierte Datei »/etc/prosody/conf.avail/jitsi.example.com.cfg.lua« sieht initial so aus wie in Listing 2 gezeigt. Dabei markiert die Zeichenfolge »–« den Beginn von Kommentaren.
Listing 2
jitsi.example.com.cfg.lua
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
-- domain mapper options, must at least have domain base set to use the mapper
muc_mapper_domain_base = "jitsi.example.com";
turncredentials_secret = "Passwort";
turncredentials = {
{ type = "stun", host = "jitsi.example.com", port = "4446" },
{ type = "turn", host = "jitsi.example.com", port = "4446", transport = "udp" },
{ type = "turns", host = "jitsi.example.com", port = "443", transport = "tcp" }
};
cross_domain_bosh = false;
consider_bosh_secure = true;
VirtualHost "jitsi.example.com"
-- enabled = false -- Remove this line to enable this host
authentication = "anonymous"
-- Properties below are modified by jitsi-meet-tokens package config
-- and authentication above is switched to "token"
-- app_id="example_app_id"
-- app_secret="example_app_secret"
-- Assign this host a certificate for TLS, otherwise it would use the one
-- set in the global section (if any).
-- Note that old-style SSL on port 5223 only supports one certificate, and
-- will always use the global one.
ssl = {
key = "/etc/prosody/certs/jitsi.example.com.key";
certificate = "/etc/prosody/certs/jitsi.example.com.crt";
[...]
Den Parameter »authentication« ändert der Admin nun von »anonymous« auf »internal_plain«, um auf die lokale Datenbank von Prosody zu verweisen. In größeren Installationen lässt sich Prosody aber auch unter Einsatz von Plugins zu einer Zusammenarbeit mit LDAP-Servern überreden.
Bliebe der geänderte Eintrag die einzige Änderung, müssten sich fortan sämtliche Benutzer anmelden, und der Admin müsste sie damit auch einpflegen. Ziel war aber, dass sich nur die Erzeuger neuer Konferenzen anmelden müssen. Deswegen folgt nun ein zweiter »VirtualHost«-Eintrag am Ende der Datei, der wie in Listing 3 aussieht.
Listing 3
Zweiter VirtualHost
VirtualHost "guest.jitsi.example.com"
authentication = "anonymous"
Über diese Änderungen muss der Admin nun auch die anderen Jitsi-Meet-Komponenten informieren. Damit beginnt er in der Datei »/etc/jitsi/meet/jitsi.example.com-config.js«. Sie enthält die ersten sechs Zeilen aus Listing 4. Unter die mit »domain« beginnende Zeile 6 gehört nun noch eine weitere für den neuen Virtualhost (Zeile 7).
Listing 4
jitsi.example.com-config.js
var config = {
// Connection
//
hosts: {
// XMPP domain.
domain: 'jitsi.example.com',
anonymousdomain: 'guest.jitsi.example.com',
[...]
So erfährt das System, in welche Domain unangemeldete Benutzer gehören. Noch fehlt ihm aber die Information, wo beziehungsweise wie es denn Benutzer anmelden kann. Anwender, die die Webseite zum Zugriff benutzen, haben ja noch keinen Durchgriff auf Prosody, das die Anmeldung über das XMPP-Protokoll erlaubt. Dazu nimmt der Admin den folgenden Eintrag in der Datei »/etc/jitsi/jicofo/sip-communicator.properties« vor:
org.jitsi.jicofo.auth.URL=XMPP:jitsi.example.com
Anschließend sollte der Admin alle beteiligten Dienste neu starten, also »jicofo.service«, »prosody.service« und »jitsi-videobridge2.service«.
Benutzer einrichten
Jetzt fehlen nur noch die Benutzer selbst, die sich anmelden dürfen. Das Kommando »prosodyctl« hilft dabei, sie einzurichten. Es benötigt einen Benutzernamen, den »VirtualHost«, zu dem der Benutzer gehört, sowie das Passwort. Allerdings muss der Admin dies alles im Klartext auf der Kommandozeile eingeben; ein Beispiel zeigt Listing 5.
Listing 5
Benutzer einrichten
# prosodyctl register Benutzer jitsi.example.com Passwort
Die Benutzerdaten finden sich auch im Klartext auf der Festplatte, typischerweise in der Datei »/var/lib/prosody/jitsi%2eexample%2ecom/accounts/username.dat«. Beim Namen des »VirtualHost«-Verzeichnisses schreibt Prosody die Punkte als Escape-Sequenzen.
Das schließt diesen Teil der Konfiguration ab. Startet ein Anwender auf dem Server nun eine neue Jitsi-Konferenz, präsentiert der Server eine Meldung wie in Abbildung 1. Klickt der Anwender auf den Button Ich bin Organisator, fordert ihn ein weiteres Fenster (Abbildung 2) zur Anmeldung auf. Ist die Konferenz bereits im Gange, wenn ein anonymer Anwender beitritt, erscheint der Dialog nicht.

Abbildung 2: Im Authentisierungsdialog gibt zum Beispiel der Lehrer seine Zugangsdaten ein, um einen neuen Raum zu erstellen.
Telefonie
Zwar zeigen die Dateinamen der Konfigurationsdateien, dass das Session Initiation Protocol aus der IP-Telefonie eine Rolle in Jitsi spielt. Trotzdem erfordert die Kopplung mit der Telefonanlage eine weitere Komponente. Sie heißt Jigasi. Als Nutzer einer Debian-basierten Distribution installiert der Admin die Software einfach mittels »sudo apt install jigasi«.
Jigasi meldet sich als Telefon bei einer SIP-Telefonanlage an. Es nimmt dann Anrufe entgegen und stellt sie in eine laufende Jitsi-Konferenz durch. Umgekehrt kann Jitsi Anrufe starten, um Teilnehmer per Telefon in eine Konferenz zu holen.
Beim Installieren der Software erscheint ein Menüdialog, der direkt die Anmeldung beim SIP-Server abfragt. Der Installationsprozess regelt auch die Integration in die laufende Jitsi-Installation. Dazu trägt das Installationsskript in die Datei »/etc/prosody/conf.available/jitsi.example.com.cfg.lua« die zwei Zeilen aus Listing 6 ein.
Listing 6
jitsi.example.com.cfg.lua
[...] Component "callcontrol.jitsi.example.com" component_secret = "K65e4S5i" [...]
Das Skript generiert auch das »component_secret«. Dieses Passwort, mit dem sich Jigasi über Prosody anmeldet, steht in der Datei »/etc/jitsi/jigasi/config«. Die im Installationsdialog eingegebenen Zugangsdaten zum SIP-Server (»User@Server« und »Passwort«) finden sich ebenfalls in dieser Datei wieder, aber auch – mit zusätzlichen SIP-Konfigurationsdetails – in der Datei »/etc/jitsi/jigasi/sip-communicator.properties« (Listing 7).
Listing 7
sip-communicator.properties
# Sample config with one XMPP and one SIP account configured
# Replace {sip-pass-hash} with SIP user password hash
# as well as other account properties
# Name of default JVB room that will be joined if no special header is included
# in SIP invite
org.jitsi.jigasi.DEFAULT_JVB_ROOM_NAME=siptest
net.java.sip.communicator.impl.protocol.SingleCallInProgressPolicy.enabled=false
# Should be enabled when using translator mode
# net.java.sip.communicator.impl.neomedia.audioSystem.audiosilence.captureDevice_list=["AudioSilenceCaptureDevice:noTransferData"]
# Adjust opus encoder complexity
net.java.sip.communicator.impl.neomedia.codec.audio.opus.encoder.COMPLEXITY=10
# Disables packet logging
net.java.sip.communicator.packetlogging.PACKET_LOGGING_ENABLED=true
net.java.sip.communicator.impl.protocol.sip.acc1403273890647=acc1403273890647
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.ACCOUNT_UID=SIP\:sip@sip-test.de
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PASSWORD=dGVzdDEyMzQ=
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PROTOCOL_NAME=SIP
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.SERVER_ADDRESS=sip-test.de
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.USER_ID=sip@sip-test.de
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.KEEP_ALIVE_INTERVAL=25
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.KEEP_ALIVE_METHOD=OPTIONS
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.VOICEMAIL_ENABLED=false
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.AMR-WB/16000=750
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.G722/8000=700
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.GSM/8000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.H263-1998/90000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.H264/90000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.PCMA/8000=600
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.PCMU/8000=650
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.SILK/12000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.SILK/16000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.SILK/24000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.SILK/8000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.VP8/90000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.iLBC/8000=10
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.opus/48000=1000
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.red/90000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.speex/16000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.speex/32000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.speex/8000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.telephone-event/8000=1
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.Encodings.ulpfec/90000=0
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.OVERRIDE_ENCODINGS=true
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.DEFAULT_ENCRYPTION=false
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.DOMAIN_BASE=jitsi.example.com
org.jitsi.jigasi.xmpp.acc.IS_SERVER_OVERRIDDEN=true
org.jitsi.jigasi.xmpp.acc.SERVER_ADDRESS=127.0.0.1
org.jitsi.jigasi.xmpp.acc.VIDEO_CALLING_DISABLED=true
org.jitsi.jigasi.xmpp.acc.JINGLE_NODES_ENABLED=false
org.jitsi.jigasi.xmpp.acc.AUTO_DISCOVER_STUN=false
org.jitsi.jigasi.xmpp.acc.IM_DISABLED=true
org.jitsi.jigasi.xmpp.acc.SERVER_STORED_INFO_DISABLED=true
org.jitsi.jigasi.xmpp.acc.IS_FILE_TRANSFER_DISABLED=true
[...]
Die Datei ist normalerweise deutlich länger, aber aus Platzgründen fehlen die auskommentierten Zeilen. Die Komponente »acc1403273890647« steht hier lediglich für die eindeutige ID, die den Account identifiziert. Hier könnte ebenso gut überall eine »1« stehen. Wer diese Datei per Hand bearbeiten möchte, muss das SIP-Passwort Base64-kodiert eintragen.
Auf der Seite der Telefonanlage muss der Admin dem Benutzer sip eine Nummer zuordnen, damit es bei Jigasi “klingelt”, wenn jemand auf dieser Nummer anruft.
Zu klären bleibt dann noch die Frage: Wie ordnet Jigasi einem Teilnehmer, der auf der »12345678« anruft, die richtige Konferenz zu? Hierfür muss der Admin an der Telefonanlage schrauben. Jigasi erwartet den SIP-Header »Jitsi-Conference-Room«. Steht also im SIP-Header »Jitsi-Conference-Room: Meeting1«, landet der Anrufer in »https://jitsi.example.com/Meeting1«.
Wie der Administrator dieses Mapping abbildet, hängt von der Telefonanlage ab. Große Konferenzanbieter offerieren dem Anrufer die Option, über Touchtone einen Code einzugeben, der dann über eine Tabelle auf den Header umgeleitet wird. Das klappt zum Beispiel mit der Telefonanlage Asterisk [3].
Fehlt dieser Header, dann landet der Anrufer im Raum, den der Admin unter der Variablen »org.jitsi.jigasi.DEFAULT_JVB_ROOM_NAME« hinterlegt hat, im Beispiel also in »siptest«.
Mit Sipgate
Im improvisierten Setup des Autors gab es allerdings keine öffentlich zugängliche Telefonanlage, die SIP spricht und gleichzeitig eine normale Telefoneinwahl erlaubt. Daher verwendete er einen Account des Anbieters Sipgate [4].
Da SIP-Telefonie und Adress Translation (der Jitsi-Server steht bei AWS auch hinter ausgehendem NAT) immer etwas Probleme bereiten, musste Jigasi noch die SIP-Proxy-Einstellungen erlernen. Listing 8 zeigt die entsprechenden Einstellungen, die der Admin in »sip-communicator.properties« ergänzen muss.
Listing 8
Zusammenspiel mit Sipgate
[...] net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PROXY_ADDRESS=sipgate.de net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PROXY_PORT=5060 net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PROXY_AUTO_CONFIG=true net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PROTOCOL_NAME=SIP net.java.sip.communicator.impl.protocol.sip.acc1403273890647.SERVER_ADDRESS=sipgate.de [...]
Da es für den Schuleinsatz nur eine Konferenz mit Telefoneinwahl geben sollte, wurde diese in den Raum »Default« eingetragen. Da der Server zudem nicht im “Jeder-darf-alles”-Modus lief, musste Jigasi noch Anweisungen erhalten, um sich anzumelden. Hierbei helfen die zwei Zeilen aus Listing 9 in der bereits erwähnten Datei »sip-communicator.properties«. Nach einem Neustart aller Komponenten sollte auch die Einwahl funktionieren.
Listing 9
Jigasi anmelden
[...] org.jitsi.jigasi.BREWERY_ENABLED=true org.jitsi.jigasi.xmpp.acc.BREWERY=JigasiBreweryRoom@internal.auth.jitsi.DOMAIN.com=JvbBrewery@internal.auth.jitsi.example.com [...]
Leistungsdaten
Jitsi benötigt CPU und Bandbreite. Grobe Messungen ergaben, dass pro Teilnehmer auf der Seite des Servers etwa 2 Mbit/s Traffic anfallen, wenn der Teilnehmer seine Kamera verwendet. Dabei spielt auch die Qualität der Kamera eine Rolle.
Das System bei AWS lief mit 4 Kernen und 16 GByte Arbeitsspeicher. Konferenzen mit bis zu 20 Teilnehmern brachten es auf eine Auslastung von 32 Prozent (gemessen mit Top und den AWS-Tools). Die angeschlossene Bandbreite stellte mit 25 Gbit/s bei AWS nicht den Flaschenhals dar. Bei zu vielen Teilnehmern geht allerdings irgendwann die CPU in die Knie, die Videobridge ist dabei der Ressourcenfresser.
Über das Problem haben die Autoren von Jitsi Meet aber bereits nachgedacht. Ein Admin kann die Videobridge auf einem oder mehreren weiteren Servern installieren und konfiguriert dabei die »sip-communicator.properties« überall analog. So zeigen alle Instanzen auf den Hauptserver, besitzen aber eindeutige Werte in der Variablen »org.jitsi.videobridge.xmpp.user.shard.MUC_NICKNAME«. Damit kann Jitsi die Last für neue Konferenzen verteilen. Dabei ist wichtig, dass die Namensauflösung für alle Beteiligten funktioniert; in unserem Beispiel müssen also alle zu »jitsi.example.com« finden.
Nichtsdestotrotz limitiert letztlich die CPU des ausführenden Hosts die Anzahl der Teilnehmer pro Konferenz. Die effizienteste Lösung wäre ein Kubernetes-Deployment der Videobridge-Komponente mit einem Autoscaling. Dann würden, abhängig von der Menge der gleichzeitig stattfindenden Konferenzen, nur so viele Ressourcen verbraucht, wie unbedingt nötig. Hier wäre allerdings noch zu testen, wie Kubernetes die Last verteilt, damit nicht mehrere Teilnehmer derselben Konferenz in verschiedenen Pods landen.
Fazit
Jitsi ist mehr als nur ein Public Server, es kommt auch mit geschlossenen Benutzergruppen zurecht. Der Anschluss an eine Telefonanlage rundet das Bild für den professionellen Einsatz ab. Das ermöglicht eine datenschutzkonforme Lösung für Videokonferenzen, ohne auf große internationale Anbieter wie Zoom oder Microsoft zurückgreifen zu müssen.
Besteht die Möglichkeit, sollte aus Datenschutzsicht eine Bildungseinrichtung aber nicht auf eine Public Cloud wie AWS zurückgreifen, sondern eine On-Premise-Installation oder einen Provider mit Sitz in Deutschland nutzen. In einem größeren Szenario mit vielen Anwendern sollte der Admin sicherlich den Anschluss an ein LDAP-Verzeichnis in Erwägung ziehen, um die Benutzer zu authentisieren. (kki)
Der Autor
Konstantin Agouros arbeitet als Head of Open Source & AWS Projects bei der Matrix Technology AG und berät dort mit seinem Team Kunden zu Open-Source-, Sicherheits- und Cloud-Themen. Sein Buch “Software Defined Networking: Praxis mit Controllern und OpenFlow” ist bei de Gruyter erschienen.
Infos
- Jitsi Meet: https://jitsi.org/jitsi-meet/
- Jigasi: https://github.com/jitsi/jigasi
- Asterisk und Jigasi zusammenführen: https://community.jitsi.org/t/working-configuration-for-jitsi-jigasi-with-asterisk-sip/53039
- Sipgate: https://www.sipgate.de







