Jitsi Meet Server - Stable-4101

Diese Installation habe ich auf einem Ubuntu 18.04.4 gemacht.

Voraussetzungen

  1. Ein ADSL-Router mit Portforwarding
    Ich möchte mein Heimnetz schützen und habe deswegen natürlich einen normalen ADSL-Router mit NAT-Funktion, durch den ich gezielte Löcher bohre.
  2. Ein x86 PC mit Ubuntu 18.04.4
    Es geht bestimmt auch mit anderen Systemen aber meine Anleitung bezieht sich auf dieses System.
    Ein Raspberry Pi scheidet aus zwei Gründen aus:
    1. Die Jitsi Meet Server Debian Installationspakete gibt es nur für x86
    2. Der Raspberry Pi 3 hat sogar als NAT-Router zu wenig Dampf
  3. Mindestens zwei Stunden Zeit
    - wenn alles gut geht. Ansonsten dauert es sehr schnell länger.
  4. Abgeschaltetes IP6
    Ich kann es nicht exakt sagen, aber die Schnellinstallation ging bei mir nur mit IP4.
    Letztendlich verwende ich hier aber einen älteren Stable Release (4101).
  5. Ein älterer Stable Release (4101)
    Dieser Stand verwendet die Jitsi-Videobridge 1. Die Jitsi-Videobridge2 wollte bei meinen internen Tests mit einem Self-Signed-Certificate nicht laufen.
    Da Jitsi aktuell (04/2020) intensiv weiter entwickelt wird, kann sich das auch wieder ändern.

Installation

Jetzt wird es haarig! Wie eingangs erwähnt, nutze ich nicht die aktuelle Stable Version, sondern eine frühere Stable Version. Falls es einen eleganteren Weg für die Installation gibt, bitte ich um Erleuchtung. Aktuell weiß ich es nicht besser.

Als erstes hole ich mir die Pakete auf meinen lokalen PC. Sie werden hier zum Download bereit gestellt: https://download.jitsi.org/jitsi/debian/

jitsi-videobridge_1126-1_amd64.deb jitsi-meet-prosody_1.0.3729-1_all.deb jitsi-meet-web_1.0.3729-1_all.deb jitsi-meet-web-config_1.0.3729-1_all.deb jicofo_1.0-508-1_amd64.deb jitsi-meet_1.0.4101-1_all.deb

Damit es keine weiteren Probleme gibt, führe ich die gesamte Installation als Root aus.

sudo su

Dann fehlen noch ein paar vorher zu installierenden Paket:

apt install openjdk-8-jre-headless ca-certificates-java java-common apt install prosody lua-expat lua-filesystem lua5.1 lua5.1-bitop lua5.1-sec lua5.1-socket apt install nginx nginx-core nginx-common libnginx-mod-stream libnginx-mod-mail libnginx-mod-http-xslt-filter libnginx-mod-http-image-filter libnginx-mod-http-geoip

Nun wird das erste Paket installiert. Bei der Installation wird nach dem Namen des Servers gefragt. Hier ist der <dynamic name> einzutragen.

dpkg -i jitsi-videobridge_1126-1_amd64.deb

Anschließend werden die anderen Pakete in dieser Reihenfolge installiert.

dpkg -i jitsi-meet-prosody_1.0.3729-1_all.deb dpkg -i jitsi-meet-web_1.0.3729-1_all.deb dpkg -i jitsi-meet-web-config_1.0.3729-1_all.deb dpkg -i jicofo_1.0-508-1_amd64.deb dpkg -i jitsi-meet_1.0.4101-1_all.deb

Wenn ich mich richtig entsinne, kann man sich bei der Installation von jitsi-meet-web-config entscheiden, ob man ein Self-Signed-Certificate oder ein vorhandenes Zertifikat nutzen möchte. Dazu muss man wissen, dass:

  1. Die Handy-App kein Self-Signed-Certificate akzeptiert
  2. Das Script /usr/share/jitsi-meet/scripts/install-letsencrypt-cert.sh einen Webserver hinter den Standard Ports (HTTP=80 / HTTPS=443) erwartet

Da führt kein Weg vorbei! Ich habe mich entschlossen mein Let's Encrypt Zertifikat auf einem Dummy Server erzeugen zu lassen. Die beiden signierten Zertifikate (CRT / KEY) habe ich anschließend in die Jitsi Meet Server Installation hineinkopiert.

sudo cp /etc/letsencrypt/live/<dynamic name>/fullchain.pem /etc/ssl/<dynamic name>.crt sudo cp /etc/letsencrypt/live/<dynamic name>/privkey.pem /etc/ssl/<dynamic name>.key

Damit sie auch zum Einsatz kommen, ist noch etwas anzupassen:

sudo nano /etc/nginx/sites-available/<dynamic name>.conf
ssl_certificate /etc/ssl/<dynamic name>.crt; ssl_certificate_key /etc/ssl/<dynamic name>.key;

Da die signierten Zertifikate nach weniger als drei Monaten auslaufen, empfiehlt es sich den Dummy Server für die Erneuerung am Leben zu lassen ...

Private Port

Im Sinne meines Bedarfs an Datenschutz möchte ich natürlich nicht den Standard Port für HTTPS (443) verwenden. Dafür sind diese Dateien anzupassen:

sudo nano /etc/nginx/sites-available/<dynamic name>.conf
listen <private port> ssl;
sudo nano /etc/jitsi/videobridge/sip-communicator.properties
org.jitsi.videobridge.SINGLE_PORT_HARVESTER_PORT=<private port>+2 org.jitsi.videobridge.TCP_HARVESTER_PORT=<private port>+1 org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<internal ip address> org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<external ip address>
sudo nano /etc/nginx/sites-available/<dynamic name>.js
bosh: '//<dynamic name>:<private port>/http-bind',

Im NAT-Router sind der <private port> für TCP und der <private port>+2 für UDP aus dem Internet weiter zu leiten zum Jitsi Meet Server.

Tja und dann haben wir im Privatbereich noch das Problem der dynamischen IP Adresse.
Also einmal am Tag wird die Internet Verbindung unterbrochen und unser DSL-Router bekommt unter Umständen eine neue externe IP Adresse, die in der Konfiguration anzugeben ist. Was nun?

Das Problem der Zuordnung eines festen Namens zu dieser wechselnden Adresse wird durch viele Anbieter gelöst. Darauf will ich hier nicht eingehen.

Aber was mache ich in den sip-communicator.properties? Dort wird eine IP-Adresse und kein Name gefordert. Auch das läßt sich unter Linux elegant lösen.
Ich gehe davon aus, daß:

  1. Der Jitsi Meet Server läuft nicht durch, sondern wird immer wieder neu gestartet.
  2. Der Wechsel der IP-Adresse erfolgt außerhalb der Laufzeit des Jitsi Meet Servers.

Unter diesen Umständen genügt es die aktuelle, externe IP-Adresse beim Hochlauf des Jitsi Meet Servers zu setzen. Das mache ich hiermit:

sudo nano /etc/init.d/updateextern
#!/bin/bash

### BEGIN INIT INFO
# Provides:          updateextern
# Required-Start:    $remote_fs $network $syslog
# Required-Stop:     $remote_fs $network $syslog
# Should-Start:      $local_fs slapd $named
# Should-Stop:       $local_fs slapd
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Update the Jitsi Videobridge
# Description:       Update the configured Jitsi Videobridge external IP Address to the current value
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin

. /lib/lsb/init-functions

NAME=updateextern
DESC="Update the Jitsi Videobridge"

NATROUTER_PID="/var/run/updateextern.pid"

updateaddress()
{
	# Compare the current external IP adress with the configured value
	SETIP=`cat /etc/jitsi/videobridge/sip-communicator.properties | grep NAT_HARVESTER_PUBLIC_ADDRESS | sed -e "s#.*=##g"`
	CURRENTIP=`dig @resolver1.opendns.com A myip.opendns.com +short -4`
	if [ "$SETIP" != "$CURRENTIP" ]; then
	    # Set the new value and reboot
	    REMAIN=`cat /etc/jitsi/videobridge/sip-communicator.properties | grep -v NAT_HARVESTER_PUBLIC_ADDRESS`
	    PREFIX=`cat /etc/jitsi/videobridge/sip-communicator.properties | grep NAT_HARVESTER_PUBLIC_ADDRESS | sed -e 's#=.*#=#g'`
	    NEW=`echo -e "$REMAIN\n$PREFIX$CURRENTIP"`
	    echo "$NEW" > /etc/jitsi/videobridge/sip-communicator.properties
	    sleep 2
	    reboot
	fi
}

case "$1" in
	start)
	        log_daemon_msg "Starting $DESC" "$NAME"
	        start-stop-daemon --start --quiet --pidfile "$NATROUTER_PID"
	        updateaddress
	        sleep 2
	        ;;
	stop)
	        log_daemon_msg "Stopping $DESC" "$NAME"
	        start-stop-daemon --stop --quiet --pidfile "$NATROUTER_PID"
	        log_end_msg $?
	        rm -f "$NATROUTER_PID"
	        ;;
	restart | force-reload)
	        $0 stop
	        sleep 2
	        $0 start
	        if [ "$?" != "0" ]; then
	                exit 1
	        fi
	        ;;
	status)
	        echo -n "Status of $DESC: "
	        check_status -v
	        exit "$?"
	        ;;
	*)
	        echo "Usage: $0 {start|stop|restart|force-reload|status}"
	        exit 1 
esac

exit 0
sudo chmod +x /etc/init.d/updateextern sudo update-rc.d updateextern defaults

Mit diesen Kommandos läuft die Funktion updateaddress() einmal beim Hochlauf des PCs. Sollte eine Differenz vorliegen, wird die neue Adresse eingetragen und der PC neu gebootet.

Soll der PC durchlaufen, so muss der Inhalt der Funktion updateaddress() per Root CronJob einmal am Tag durchlaufen werden. Auch dafür gibt es viele Anleitungen im Internet.

RAM Disk

Nach der normalen Installation des Grundsystems habe ich für den Betrieb noch die SSD meines PCs entlastet.

Natürlich habe ich das Rad nicht neu erfunden: Raspberry Pi: Extending the life of the SD card

Allerdings habe ich es etwas angepasst.

  1. Das Verzeichnis "/var/spool/mqueue" gibt es bei Ubuntu nicht.
  2. Verschiedene Größen musste ich erweitern.

Herausgekommen ist:

sudo nano /etc/fstab
tmpfs /tmp tmpfs defaults,noatime,nosuid,size=100m 0 0 tmpfs /var/tmp tmpfs defaults,noatime,nosuid,size=300m 0 0 tmpfs /var/log tmpfs defaults,noatime,nosuid,mode=0755,size=100m 0 0 tmpfs /var/run tmpfs defaults,noatime,nosuid,mode=0755,size=2m 0 0

Das zog aber noch weitere Kreise, da bei der Installation des Jitsi Meet Servers mehrere Unterverzeichnis von "/var/log" mit besonderen Rechten angelegt werden.
Nach einem Reboot sind die natürlich alle weg und müssen nach jedem Boot rechtzeitig, neu angelegt werden. Da das unter unterschiedlichsten Benutzern sattfinden muss, habe ich drei Tools geschrieben.

Ich möchte das Sticky-Bit verwenden um zum Zeitpunkt der Ausführung Root Rechte zu haben. Unter Ubuntu sind Sticky-Bits nur noch für binär Programme zuläßig. Also habe ich mir diese schnell geschrieben, sie mit den passenden Rechten versehen und am passenden Ort abgelegt.

nano jitsi_mkdir.c
#include #include int main(int argc, char** argv) { int result = -1; if (argc <= 1) { printf("%s \n",argv[0]); } else { char command[100]; result = snprintf(command,sizeof(command),"/var/log/%s",argv[1]); if ((result >= 0) && (result < (int)sizeof(command))) { result = execlp("/bin/mkdir","/bin/mkdir","-p",command,NULL); } } return(result); }
gcc -g -Wall -Wextra -pedantic-errors jitsi_mkdir.c -o jitsi_mkdir sudo chown root:root jitsi_mkdir sudo chmod 4755 jitsi_mkdir sudo mv jitsi_mkdir /usr/sbin/jitsi_mkdir
nano jitsi_chown.c
#include #include int main(int argc, char** argv) { int result = -1; if (argc <= 2) { printf("%s \n",argv[0]); } else { char command[100]; result = snprintf(command,sizeof(command),"/var/log/%s",argv[1]); if ((result >= 0) && (result < (int)sizeof(command))) { result = execlp("/bin/chown","/bin/chown",argv[2],command,NULL); } } return(result); }
gcc -g -Wall -Wextra -pedantic-errors jitsi_chown.c -o jitsi_chown sudo chown root:root jitsi_chown sudo chmod 4755 jitsi_chown sudo mv jitsi_chown /usr/sbin/jitsi_chown
nano jitsi_chmod.c
#include #include int main(int argc, char** argv) { int result = -1; if (argc <= 2) { printf("%s \n",argv[0]); } else { char command[100]; result = snprintf(command,sizeof(command),"/var/log/%s",argv[1]); if ((result >= 0) && (result < (int)sizeof(command))) { result = execlp("/bin/chmod","/bin/chmod",argv[2],command,NULL); } } return(result); }
gcc -g -Wall -Wextra -pedantic-errors jitsi_chmod.c -o jitsi_chmod sudo chown root:root jitsi_chmod sudo chmod 4755 jitsi_chmod sudo mv jitsi_chmod /usr/sbin/jitsi_chmod

Nach einem Boot müssen die fehlenden Verzeichnisse rechtzeitig neu angelegt werden. Im Details habe ich dazu diese Dateien anpassen müssen:

sudo nano /etc/init.d/jicofo
echo -n "Starting $NAME: " # Create log dir on RAMDISK and set rights /usr/sbin/jitsi_mkdir jitsi /usr/sbin/jitsi_chown jitsi jvb:jitsi /usr/sbin/jitsi_chmod jitsi 775
sudo nano /lib/systemd/system/nginx.service
ExecStartPre=/usr/sbin/jitsi_mkdir nginx ExecStartPre=/usr/sbin/jitsi_chown nginx root:adm ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
sudo nano /lib/systemd/system/jitsi-videobridge.service
# Create log dir on RAMDISK and set rights ExecStartPre=/usr/sbin/jitsi_mkdir jitsi ExecStartPre=/usr/sbin/jitsi_chown jitsi jvb:jitsi ExecStartPre=/usr/sbin/jitsi_chmod jitsi 775 ExecStartPre=/usr/sbin/jitsi_mkdir prosody ExecStartPre=/usr/sbin/jitsi_chown prosody prosody:adm ExecStart=/bin/bash -c ...

So, das war's. Wenn der PC nun neu gestartet wird, so sollte alles laufen. Für einen Test gebt ihr den Link https://<dynamic name> als Adresse in einem Mozilla Firefox oder Chrome Browser ein und freut euch am Ergebnis.

Wie immer bin ich neugierig, was bei der Reproduktion so alles auffällt. Ich würde mich also über eine kurze eMail sehr freuen.