Infrastruktur

Server bbb

Zum 16.05.2024 wird der BBB-Server abgeschaltet.

Date: Wed, 17 Apr 2024 21:05:06 +0200
From: Alvar Penning <REDACTED>
To: public@lists.hsmr.cc
Subject: Abschaltung BigBlueButton

Hej Liste,

zum 16.05. wird der BigBlueButton-Server bbb.hsmr.cc abgeschaltet.

Dieser wurde vor nun knapp drei Jahren im Mai 2021 aufgrund des
damaligen Bedarfs in Betrieb genommen und lief seitdem mit kurzen
Updatepausen durch.

Gründe für die Abschaltung gibt es zwei (oder drei):

1. Glücklicherweise hat sich seit der Anschaffung die Gesamtsituation
   verändert und somit wurde der Server im letzten Jahr nicht verwendet.
2. Der Software-Stack ist etwas in die Jahre gekommen. Das Webfrontend
   greenlight hat einen Major-Versionssprung genommen und dabei leider
   den für uns notwendigen LDAP-Support unter die Räder kommen
   lassen[0]. Ohne LDAP ist die greenlight in der Hackspace-Infra aber
   weitgehend wertlos.
3. Der Server kostete Geld, welches jedoch bei Nichtbenutzung
   verschwendet wirkte.

Falls jemand das Projekt übernehmen mag, gibt es die Doku im Wiki[1] und
natürlich bin ich für Rückfragen auch erreichbar.

Viele Grüße
Alvar

[0] https://github.com/bigbluebutton/greenlight/issues/4817
[1] https://hsmr.cc/Infrastruktur/ServerBbb

Öffentlicher Raum unter https://bbb.hsmr.cc/b/general.

Netzwerk
IPv4202.61.230.59/32
IPv62a03:4000:57:cf5::2/64
Hostnamebbb.hsmr.cc

Der Hackspace betreibt eine BigBlueButton-Instanz zum gemeinsamen Schnaken, Planen oder gar für Vorträge. Diese ist unter https://bbb.hsmr.cc/ zu erreichen.

Einloggen lässt sich dort via LDAP-Account, welcher sonst fürs Zammad genutzt wird. Mittels Account lassen sich neue Sitzungen erstellen. Beitreten ist ohne Account möglich bzw. durch die sitzungsspezifischen Einstellungen geregelt.

Ein gemeinsamer öffentlicher Raum ist unter https://bbb.hsmr.cc/b/general zu erreichen.

Die Installation ist noch recht frisch, also bitte mit Vorsicht testen und Fehler oder Verbesserungsvorschläge zeitig kommunizieren.

Bei der Installation wurde auf Datensparsamkeit geachtet, jedoch besteht ein BBB aus verschiedenen Services. Wir versuchen durch regelmäßiges Prüfen sicherzustellen, dass keine personenbezogenen Daten langfristig gespeichert werden.

Alle Sitzungen, bei denen eine Aufname möglich ist, werden kurzzeitig auf Platte geschrieben. Dies sollte vorm Joinen durch eine extra Abfrage und während der Sitzung durch die "Recording-LED" oben in der Mitte angezeigt werden.

1.  Installation/Konfiguration

1.1  Basis

Die Basisinstallation ist analog zur Dokumentation. Deswegen ist das OS auch ein Ubuntu 18.04, basierend auf dem "Minimal" Image.

Vor der eigentlichen BBB-Installation wurden folgende stichpunktartige Schritte vorgenommen:

  • visudo: NOPASSWD für %sudo gesetzt.
  • Neuen User in der sudo-Gruppe angelegt.
  • /etc/ssh/sshd_config: Pubkey-only-Authentication
  • /etc/netplan/50-cloud-init.yaml anpassen, anschließend netplan apply
  • hostnamectl set-hostname bbb.hsmr.cc
  • Docker installiert, Docker-Dokumentation

Nun wurde die eigentliche BBB-Installation via bbb-install.sh durchgeführt.

$ wget https://ubuntu.bigbluebutton.org/bbb-install.sh
$ chmod +x bbb-install.sh
$ sudo ./bbb-install.sh -v bionic-23 -s bbb.hsmr.cc -e XXX -g -w

User

Zugang haben aktuell: oxzi (alvar), xkey (xkey)

Neue User werden lassen sich folgendermaßen anlegen:

$ sudo useradd -g users -G sudo -m -N $USERNAME

Da ein SSH-Login ausschließlich via Public Key erlaubt ist, muss dieser entsprechend abgelegt werden:

$ echo $KEY > ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys

1.2  Docker

Per default spricht Docker nur legacy IP. Da wir gerne via IPv6 mit dem LDAP-Server kommunizieren würde, müssen wir dies anpassen.

Für die Docker-Container erlauben wir ein Subnetz von unserem /64er, hier das hintere /65er. Anschließend konfigurieren wir den NDP Proxy Daemon.

Unabhängig davon konfigurieren wir, dass Docker generell nach journald loggt. Somit haben wir später nicht die Logs an tausend Orten.

$ cat /etc/docker/daemon.json
{
  "ipv6": true,
  "fixed-cidr-v6": "2a03:4000:57:cf5:8000::/65",
  "log-driver": "journald"
}

$ tail -n 1 /etc/sysctl.conf
net.ipv6.conf.eth0.proxy_ndp=1
$ sudo sysctl -p /etc/sysctl.conf

$ sudo apt-get install ndppd
$ cat /etc/ndppd.conf
proxy eth0 {
  rule 2a03:4000:57:cf5:8000::/65 {
    auto
  }
}
$ sudo systemctl restart ndppd.service

1.3  Greenlight

Greenlight ist ein Web-Frontend sowohl für die User-Authentifizierung wie auch die Erstellung von Räumen.

Im Produktivbetrieb wickeln wir dies über unser LDAP ab. Zum Debuggen gibt es einen lokalen Admin, dessen Zugang entsprechend aktiviert werden muss.

Die Greenlight-Installation liegt in /opt/greenlight.

Docker Compose

Um Docker mit IPv6 zu nutzen, wurde in der docker-compose.yml beiden Services der network_mode: bridge hinzugefügt. Ferner sollen die Container ins Journal loggen. Zusammen sieht die docker-compose.yml wie folgt aus:

version: '3'

services:
  app:
    entrypoint: [bin/start]
    image: bigbluebutton/greenlight:v2
    container_name: greenlight-v2
    env_file: .env
    restart: unless-stopped
    ports:
      - 127.0.0.1:5000:80
# When using external logging
#    logging:
#      driver: $LOG_DRIVER
#      options:
#        syslog-address: $LOG_ADDRESS
#        tag: $LOG_TAG
    volumes:
      - ./log:/usr/src/app/log
      - ./storage:/usr/src/app/storage
# When using sqlite3 as the database
#      - ./db/production:/usr/src/app/db/production
# When using postgresql as the database
    links:
      - db
    network_mode: bridge
    logging:
      driver: "journald"
  db:
    image: postgres:13.2-alpine
    restart: unless-stopped
    ports:
      - 127.0.0.1:5432:5432
    volumes:
      - ./db/production:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=XXX
    network_mode: bridge
    logging:
      driver: "journald"

Greenlight Config

Die Konfiguration erfolgt über die .env-Datei. Angepasst wurde hier lediglich der LDAP-Abschnitt.

LDAP_SERVER=ldap.hsmr.cc
LDAP_PORT=636
LDAP_METHOD=ssl
LDAP_UID=uid
LDAP_BASE=dc=hsmr,dc=cc
LDAP_BIND_DN=cn=bbb,dc=hsmr,dc=cc
LDAP_AUTH=simple
LDAP_PASSWORD=XXX
LDAP_ROLE_FIELD=
LDAP_FILTER=
LDAP_ATTRIBUTE_MAPPING=name=displayName;uid=uid;

Systemd Unit

Gestartet wird Greenlight via systemd, /etc/systemd/system/greenlight.service:

[Unit]
Description=Greenlight
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/opt/greenlight
ExecStart=/usr/local/bin/docker-compose up -d --remove-orphans --build
ExecStop=/usr/local/bin/docker-compose down

[Install]
WantedBy=multi-user.target

Lokaler Admin-Account

Weiter wurde ein lokaler Administrator angelegt; siehe Dokumentation. Dessen Login-Credentials stehen in /root/greenlight-admin.

Standardmäßig ist dieser Account nicht nutzbar, da in der Greenlight-Konfiguration

ALLOW_GREENLIGHT_ACCOUNTS=false

gesetzt ist. Falls also der Admin benötigt wird, müssen kurzzeitig (!) lokale Accounts erlaubt werden. Hierzu ist jeweils ein Neustart von Greenlight notwendig.

Einfacher ist es kurzfristig die Rechte eines LDAP-Users zu erhöhen, siehe unten.

1.4  Greenlight LDAP-Sync

Aktuell synchronisiert Greenlight User-Daten beim ersten Login vom LDAP in die eigene PostgreSQL-Datenbank. Spätere LDAP-Syncs werden nicht durchgeführt, siehe Issue #1918.

Als Behilfslösung schrieb ich dafür greenlight-ldap-sync.

$ cd /opt/greenlight
$ git clone https://github.com/oxzi/greenlight-ldap-sync.git

Die docker-compose.yml wurde um folgenden service erweitert:

  ldap-sync:
    build:
      context: ./greenlight-ldap-sync
    env_file: .env
    environment:
    # - SYNC_DEBUG=on
      - SYNC_INTERVAL=30m
    restart: unless-stopped
    links:
      - db
    network_mode: bridge
    logging:
      driver: "journald"

1.5  BigBlueButton

Zum Verwalten der Dienste nutzt BBB natürlich nicht systemd, sondern eigene Skripte. Besonders relevant sind hier bbb-conf und bbb-record.

Zum Starten/Stoppen/Prüfen gibt es:

sudo bbb-conf --{start,stop,status}

Aufnahmen lassen sich mit bbb-record --list anzeigen und auch löschen, siehe --help.

Allgemein hat BBB nach der Installation relative sinnvolle Defaults, trotzdem ein paar Anpassungen.

Ungewollte Recordings aufräumen

BBB zeichnet alle Sitzungen auf, wo ein Recording möglich ist (!). Es muss also nicht in der Web-UI die Aufnahme gestartet worden sein, sondern es genügt, dass der Aufnahme-Button möglich ist.

Es gibt einen Cronjob in /etc/cron.daily/bigbluebutton, welcher ältere Dateien löscht, siehe Dokumentation.

history=1
unrecorded_days=1
published_days=1
log_history=1

All diese Variablen werden als -mtime +${VAR} an find übergeben. Somit werden Dateien welche nach über 1*24h bearbeitet wurden gelöscht, was zwei Tagen entspricht.

Drei parallele Kurento-Instanzen

Die Dokumentation sagt, dass drei Kurento-Instanzen - je eine für "listen only, webcams, and screen share" - besser performen.

Also wurde zu /etc/bigbluebutton/bbb-conf/apply-config.sh folgender Eintrag hinzugefügt:

enableMultipleKurentos

Echo Test deaktivieren

Standardmäßig wird beim Joinen mit Mikrofon ein Test durchgeführt, welcher jedoch meistens nur nervt.

Deswegen in /usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml den Eintrag public.app.skipCheck auf true setzen.

sudo sed -i.bak 's/\([ ]*skipCheck\): .*$/\1: true/g' /usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml

HSMR-Favicon

Das Favicon lässt sich austauschen, z.B. zum [hsmr]™-Branding.

$ cd /var/www/bigbluebutton-default/
$ rm favicon.ico
$ wget -O favicon.ico https://hsmr.cc/favicon.ico

Damit Greenlight dieses auch ausliefert, müssen wir es in der docker-compose.yml dazu überreden, indem wir den Default übermounten.

services:
  app:
    volumes:
      - /var/www/bigbluebutton-default/favicon.ico:/usr/src/app/app/assets/images/favicon.ico

1.6  TURN-Server

Standardmäßig verwendet BBB die TURN/STUN-Server von Google. Aus Gründen setzen wir auf unserem Server lieber einen eigenen auf.

Das tolle bbb-install.sh-Script kann dies theoretisch, jedoch nur für Ubuntu 20.04 und nicht für 18.04, welches angeblich verpflichtend sei…

Die folgenden Schritte orientieren sich stark an der BBB-Anleitung.

Initial installieren und konfigurieren wir coturn.

$ sudo apt-get install coturn

$ grep "^[^#]" /etc/default/coturn
TURNSERVER_ENABLED=1

$ sudo mkdir /etc/turnserver/
$ sudo chown turnserver:turnserver /etc/turnserver
$ sudo chmod 700 /etc/turnserver

$ cat /etc/letsencrypt/renewal-hooks/deploy/coturn
#!/bin/bash -e
for certfile in fullchain.pem privkey.pem ; do
        cp -L /etc/letsencrypt/live/bbb.hsmr.cc/"${certfile}" /etc/turnserver/"${certfile}".new
        chown turnserver:turnserver /etc/turnserver/"${certfile}".new
        mv /etc/turnserver/"${certfile}".new /etc/turnserver/"${certfile}"
done
systemctl kill -sUSR2 coturn.service

$ sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/coturn
$ sudo /etc/letsencrypt/renewal-hooks/deploy/coturn

$ sudo openssl dhparam -dsaparam  -out /etc/turnserver/dhp.pem 2048
$ sudo chown turnserver:turnserver /etc/turnserver/dhp.pem

$ grep "^[^#]" /etc/turnserver.conf
listening-port=3478
tls-listening-port=5349

listening-ip=202.61.230.59
listening-ip=2a03:4000:57:cf5::2
relay-ip=202.61.230.59
relay-ip=2a03:4000:57:cf5::2
min-port=49152
max-port=65535

fingerprint
lt-cred-mech
use-auth-secret
static-auth-secret=XXX
realm=bbb.hsmr.cc
cert=/etc/turnserver/fullchain.pem
pkey=/etc/turnserver/privkey.pem
dh-file=/etc/turnserver/dhp.pem

no-loopback-peers
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=::1
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=::ffff:0:0-::ffff:ffff:ffff
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=64:ff9b:1::-64:ff9b:1:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
no-cli
no-tlsv1
no-tlsv1_1

In /etc/bigbluebutton/bbb-conf/apply-config.sh fügen wir die zusätzlich benötigten Firewall-Regeln vor enableUFWRules hinzu.

ufw allow 3478/tcp
ufw allow 3478/udp
ufw allow 5349/tcp
ufw allow 5349/udp
ufw allow 49152:65535/udp

Abschließend teilen wir noch BBB mit unseren eigenen Server zu nutzen; /usr/share/bbb-web/WEB-INF/classes/spring/turn-stun-servers.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--

BigBlueButton open source conferencing system - http://www.bigbluebutton.org/

Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).

This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.

BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.

-->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            ">

    <bean id="stun1" class="org.bigbluebutton.web.services.turn.StunServer">
        <constructor-arg index="0" value="stun:bbb.hsmr.cc"/>
    </bean>

    <bean id="turn1" class="org.bigbluebutton.web.services.turn.TurnServer">
        <constructor-arg index="0" value="XXX"/>  <!-- static-auth-secret hier -->
        <constructor-arg index="1" value="turns:bbb.hsmr.cc:5349?transport=tcp"/>
        <constructor-arg index="2" value="86400"/>
    </bean>

    <bean id="turn2" class="org.bigbluebutton.web.services.turn.TurnServer">
        <constructor-arg index="0" value="XXX"/>
        <constructor-arg index="1" value="turn:bbb.hsmr.cc:5349?transport=tcp"/>
        <constructor-arg index="2" value="86400"/>
    </bean>

    <bean id="stunTurnService" class="org.bigbluebutton.web.services.turn.StunTurnService">
        <property name="stunServers">
            <set>
                <ref bean="stun1" />
            </set>
        </property>
        <property name="turnServers">
            <set>
                <ref bean="turn1" />
                <ref bean="turn2" />
            </set>
        </property>
        <property name="remoteIceCandidates">
            <set>
            </set>
        </property>
    </bean>
</beans>

Nach einem Neustart von BBB sollte der TURN-Server genutzt werden. Dies lässt sich testen, wie upstream bei BBB dokumentiert.

1.7  Privacy / Logging

Wunschzustand ist, dass Logs entweder gar nicht, anonymisiert oder nur kurzlebig existieren. Da BBB selbst aus drölfzig Services besteht, muss an einigen Stellen Hand angelgt werden.

Dieser Abschnitt sollte nach gewisser Zeit evaluiert und ggf. angepasst werden.

Systemd Journal

$ grep "^[^#]" /etc/systemd/journald.conf
[Journal]
SystemMaxUse=50M
MaxRetentionSec=1day
MaxFileSec=1day
ForwardToSyslog=no

UFW

Per default ist UFW arg verbose bzgl. geblockten Verbindungen. Da sich dies sowieso niemand ansieht, kann es auch weg.

$ tail -n1 /etc/bigbluebutton/bbb-conf/apply-config.sh
ufw logging off

Docker

Wurde bereits oben durch {"log-driver": "journald"} nach Journald umgelenkt.

Nginx

Die Log-Konfiguration sollte allein in /etc/nginx/nginx.conf geschehen. Hier werden keine Access Logs erstellt und Error Logs ans Journal übergeben, sodass Systemd Journald sie wegrotiert.

http {
  access_log off;
  error_log stderr error;
}

BigBlueButton

In /usr/share/bbb-web/WEB-INF/classes/logback.xml definiert BBB ein eignes logrotate für Logs in /var/log/bigbluebutton/bbb-web.YYYY-MM-DD.log. Hier also den Wert für MaxHistory auf 1 setzen.

sed -i.bak 's/\([ ]*\<MaxHistory>\)[0-9]*\(.*\)$/\11\2/g' /usr/share/bbb-web/WEB-INF/classes/logback.xml

Freeswitch

Freeswitch loggt ebenfalls fleißig, wobei der Backlog abermals via XML reduziert werden kann. In /etc/bbb-fsesl-akka/logback.xml abermals die MaxHistory auf 1 reduzieren.

sed -i.bak 's/\([ ]*\<MaxHistory>\)[0-9]*\(.*\)$/\11\2/g' /etc/bbb-fsesl-akka/logback.xml

logrotate

Zusätzlich nutzt BBB auch das normale logrotate. Hier gibt es /etc/logrotate.d/bbb-{record-core,webrtc-sfu}.logrotate, welche wir aber entfernen. Anschließend legen wir eine gemeinsame Config für BBB, Kurento, Coturn und Greenlight anlegen.

  • Kurento: Logfiles benannt nach Datum und PID (?) in /var/log/kurento-media-server.
  • Greenlight: Meistens via Docker ins Journal; anderes nach /opt/greenlight/log/production.log geschrieben.
$ sudo rm /etc/logrotate.d/bbb-*.logrotate
$ cat /etc/logrotate.d/bbb-meta
/var/log/bigbluebutton/bbb-rap-worker.log
/var/log/bigbluebutton/sanity.log
/var/log/bbb-webrtc-sfu/*.log
/var/log/kurento-media-server/*.log
/var/log/turn_*.log
/opt/greenlight/log/production.log
{
        copytruncate
        missingok
        notifempty
        daily
        rotate 1
        maxage 1
}

1.8  Prometheus Exporter

Es werden zwei Prometheus Exporter installiert. Einmal den BigBlueButton Exporter speziell für BBB. Zusätzlich noch den Node Exporter für generelle Systeminformationen.

$ mkdir /opt/bbb-exporter
$ cd /opt/bbb-exporter
$ cat docker-compose.yaml
version: '3'

services:
  bbb-exporter:
    container_name: bbb-exporter
    image: greenstatic/bigbluebutton-exporter:v0.6.0
    ports:
      - "127.0.0.1:9688:9688"
    volumes:
      - "/var/bigbluebutton:/var/bigbluebutton:ro"
    environment:
      RECORDINGS_METRICS_READ_FROM_DISK: "true"
    env_file:
      - secrets.env
    restart: unless-stopped

  node_exporter:
    container_name: node_exporter
    image: quay.io/prometheus/node-exporter:latest
    command:
      - '--path.rootfs=/host'
      - "--web.listen-address=127.0.0.1:9100"
    network_mode: host
    pid: host
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'
    restart: unless-stopped

$ bbb-conf --secret
# -> API_BASE_URL wird URL mit api/-Endpunkt
# -> API_SECRET wird Secret
$ cat secrets.env
API_BASE_URL=https://bbb.hsmr.cc/bigbluebutton/api/
API_SECRET=XXX

# Öffentlich erreichbar wird der Service geproxied via nginx
$ apt-get install apache2-utils
$ htpasswd -c htpasswd metrics   # nginx mag kein bcrypt, sagt es aber nicht direkt…
$ chgrp www-data htpasswd
$ chmod 440 htpasswd

$ cat /etc/bigbluebutton/nginx/monitoring.nginx
location /metrics_node/ {
    auth_basic "Node Exporter";
    auth_basic_user_file /opt/bbb-exporter/htpasswd;
    proxy_pass http://127.0.0.1:9100/;
    include proxy_params;
}

location /metrics_bbb/ {
    auth_basic "BigBlueButton Exporter";
    auth_basic_user_file /opt/bbb-exporter/htpasswd;
    proxy_pass http://127.0.0.1:9688/;
    include proxy_params;
}

$ systemctl reload nginx

# Und jetzt noch den Container automatisch starten
$ cat /etc/systemd/system/bbb-exporter.service
[Unit]
Description=BBB Exporter
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/opt/bbb-exporter
ExecStart=/usr/local/bin/docker-compose up -d --remove-orphans --build
ExecStop=/usr/local/bin/docker-compose down

[Install]
WantedBy=multi-user.target

$ systemctl daemon-reload
$ systemctl enable bbb-exporter.service
$ systemctl start bbb-exporter.service

Auf ancha läuft ein Prometheus, welcher die Metrics abfragt. Dieses ist in der NixOS-Konfiguraiton folgendermaßen definiert.

{
  services.prometheus = {
    enable = true;

    scrapeConfigs = [
      # . . .
      {
        job_name = "bbb";
        basic_auth = {
          username = "metrics";
          password = "XXX";
        };
        scheme = "https";
        metrics_path = "/metrics_node/metrics";
        static_configs = [{
          targets = [ "bbb.hsmr.cc" ];
        }];
      }
      {
        job_name = "bbb-exporter";
        basic_auth = {
          username = "metrics";
          password = "XXX";
        };
        scheme = "https";
        metrics_path = "/metrics_bbb/metrics";
        static_configs = [{
          targets = [ "bbb.hsmr.cc" ];
        }];
      }
    ];

    # . . .
  };
}

1.9  Restic Backups

Konfigurationseinträge und ähnliches werden mit restic auf den restic Rest Server auf ancha gesichert.

Da es mit den Paketen für ein LTS-Ubuntu traurig aussieht, wird das statische Binary von GitHub installiert.

$ wget https://github.com/restic/restic/releases/download/v0.12.0/restic_0.12.0_linux_amd64.bz2
$ bunzip2 restic_0.12.0_linux_amd64.bz2
$ install -m 755 restic_0.12.0_linux_amd64 /usr/bin/restic

Das Repository muss eingangs initialisiert werden. Die Credentials liegen u.a. für den root-User in dessen Home-Verzeichnis.

$ ls -l /root/restic-*
-r-------- 1 root root 33 Jun  6 12:59 /root/restic-pw
-r-------- 1 root root 33 Jun  6 12:59 /root/restic-rest
$ restic init -r rest:https://bbb:XXX@ancha.lurk.space/restic/bbb/

Angestoßen werden die Backups dann über systemd.

$ cat /root/restic-include
/etc/bigbluebutton/
/etc/cron.*
/etc/default/coturn
/etc/docker/*.json
/etc/logrotate.d/
/etc/nginx/
/etc/systemd/journald.conf
/home/
/opt/bbb-exporter
/opt/greenlight/
/root/
/usr/share/bbb-web/WEB-INF/classes/spring/turn-stun-servers.xml
/usr/share/meteor/bundle/programs/server/assets/app/config/

$ cat /root/restic-exclude
/home/*/.cache
/opt/greenlight/log/

$ systemctl cat restic-backup.service
# /etc/systemd/system/restic-backup.service
[Unit]
Description=restic backup

[Service]
Environment="HOME=/root"
Environment="RESTIC_PASSWORD_FILE=/root/restic-pw"
Environment="RESTIC_REPOSITORY=rest:https://bbb:XXX@ancha.lurk.space/restic/bbb/"

Type=oneshot
ExecStart=/usr/bin/restic backup --files-from /root/restic-include --exclude-file /root/restic-exclude

$ systemctl cat restic-backup.timer
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=scheduled restic backup

[Timer]
OnCalendar=daily
Unit=restic-backup.service

[Install]
WantedBy=timers.target

$ systemctl enable --now restic-backup.timer

2.  Maintenance

2.1  BBB, Updates und After Care

Nach einem Upgrade von BBB-Paketen (prefixed mit bbb-) via apt-get muss Folgendes durchgeführt werden.

  1. BBB neustarten: sudo bbb-conf --restart
  2. Falls hier Probleme gelistet werden, müssen diese händisch behoben werden. Glücklicherweise ist die Problembeschreibung recht gut und BBB's Issue Tracker hilft.
    Weiter gibt es noch bbb-conf --check und --status.
  3. Folgende Abschnitte wiederholen, zusammengefasst in /root/bin/post-bbb-update.sh
    1. BigBlueButton/Echo Test deaktivieren
    2. BigBlueButton/HSMR-Favicon
    3. Logging/BigBlueButton
    4. Logging/Freeswitch
  4. BBB für Änderungen neustarten: sudo bbb-conf --restart
  5. Greenlight neustarten: sudo systemctl restart greenlight.service
  6. Funktionalität im Browser testen!

2.2  Greenlight, Update

Da Greenlight leider via Docker Compose deployed ist, gibt es hier keinen wirklichen Package Manager. Vielmehr wird das aktuelle Image von Docker Hub bezogen, wobei der Tag entsprechend des Releases verwendet wird.

Vor einem Update muss zwingend geprüft werden, ob das Datenbank-Schema geändert wurde. Falls ja, muss die Kompatibilität mit dem LDAP-Sync geprüft und ggf. wiederhergestellt werden.

Der passende Tag lässt sich auf Docker Hub finden. Hierbei sollte die genaue Version entsprechend des Git Tags verwendet werden, z.B. v2.10.0.2.

  1. In /opt/greenlight/docker-compose.yml den Wert services.app.image anpassen, z.B. auf bigbluebutton/greenlight:v2.10.0.2
  2. systemctl restart greenlight.service

2.3  Greenlight, Admin werden

Übers Greenlights PostgreSQL lassen sich User ad hoc zum Admin upgraden.

  • role_id = 1: User
  • role_id = 2: Admin
alvar@bbb:/opt/greenlight$ sudo docker exec -it greenlight_db_1 bash

bash-5.1# psql greenlight_production postgres

greenlight_production=# SELECT * FROM users;
 id | room_id |  provider  |       uid       |     name      | username |         email          | social_uid | image |                       password_digest                        | accepted_terms |         created_at         |         updated_at         | email_verified | language | reset_digest | reset_sent_at | activation_digest | activated_at | deleted | role_id |         last_login
----+---------+------------+-----------------+---------------+----------+------------------------+------------+-------+--------------------------------------------------------------+----------------+----------------------------+----------------------------+----------------+----------+--------------+---------------+-------------------+--------------+---------+---------+----------------------------
  2 |       2 | ldap       | gl-qwertyuiopas | Vor Nachname  | username | example@example.org    | username   |       |                                                              | f              | 2021-05-25 19:53:57.788869 | 2021-05-25 19:54:24.647018 | t              | default  |              |               |                   |              | f       |       1 | 2021-05-25 19:54:24.646052       

greenlight_production=# UPDATE users SET role_id = ROLE_ID WHERE id = ID;

Bitte Rechte nur kurzfristig geben und wieder nehmen.

2.4  Greenlight, Raum-URL anpassen

Die Raum-URL, bzw. speziell der hintere Teil, wird von Greenlight automatisch generiert und kann über die Web-UI nicht angepasst werden. Auch hier hilft wieder SQL direkt, wobei der Raum-Name in der URL in uid steht.

alvar@bbb:/opt/greenlight$ sudo docker exec -it greenlight_db_1 bash

bash-5.1# psql greenlight_production postgres

greenlight_production=# SELECT * FROM rooms;
 id | user_id |            name            |       uid       |                  bbb_id                  | sessions |      last_session       |         created_at         |         updated_at         |             room_settings                                                                                            | moderator_pw | attendee_pw  | access_code | deleted | moderator_access_code
----+---------+----------------------------+-----------------+------------------------------------------+----------+-------------------------+----------------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------+--------------+--------------+-------------+---------+-----------------------
 11 |       2 | Water Cooler Conversations | uwu-123-abc-lel | qwertyuiopasdfghjklzxcvbnm1234567890qwer |        0 |                         | 2021-06-12 08:52:14.786665 | 2021-06-12 08:52:14.786665 | {"muteOnStart":false,"requireModeratorApproval":false,"anyoneCanStart":true,"joinModerator":false,"recording":false} | qwertyuiopas | asdfghjklzxc |             | f       |

greenlight_production=# UPDATE rooms SET uid = 'general' WHERE id = 11;

2.5  Greenlight, Raum mit Access Code sperren

Zugangsberechtigungen lassen sich auch live während Meetings setzen. Die Web-UI erlaubt - wieso auch immer - nur gewürfelte Zahlencodes, was für Verabredungen im Vorfeld schwer fällt. SQL to the rescue.

alvar@bbb:/opt/greenlight$ sudo docker exec -it greenlight_db_1 bash

bash-5.1# psql greenlight_production postgres

greenlight_production=# SELECT * FROM rooms;
. . .
greenlight_production=# UPDATE rooms SET access_code = 'passwort' WHERE id = 23;
greenlight_production=# UPDATE rooms SET access_code = NULL WHERE id = 23;

2.6  BBB/Greenlight, Meeting ID bestimmen

Jedes Meeting besitzt eine BigBlueButton-Meeting ID, welche u.a. für API-Calls benötigt wird. Dass diese ID natürlich eine andere als die Greenlight UID ist, ist ja offensichtlich…

Eine Möglichkeit ist die Abfrage via API direkt. Der checksum-Parameter lässt sich aus Python folgendermaßen berechnen:

>>> import hashlib
>>> hashlib.sha1(b'getMeetingsSUPERSICHER!!11').hexdigest()

Da dies maximal nervig ist, aber wieder SQL. Mittels Greenlights Datenbank lässt sich die Greenlight UID (hinterer Teil der URL) auf die BBB-Meeting ID abbilden.

alvar@bbb:/opt/greenlight$ sudo docker exec -it greenlight_db_1 bash

bash-5.1# psql greenlight_production postgres

greenlight_production=# SELECT bbb_id FROM rooms WHERE uid = 'general';
                  bbb_id
------------------------------------------
 trololololololololololololololololololol
(1 row)

2.7  BBB, Meeting streamen

Ein BBB-Meeting lässt sich via BigBlueButton Broadcaster ad hoc streamen.

Aktuell gibt es dafür die Domain stream.bbb.hsmr.cc, welche ein CNAME auf stream.bbb.lurk.space ist. Zum Livestreamen eines Meetings bzw. eines Vortrags muss dann nur noch über den DevOps-Hai gesprungen werden.

  1. Bei Hetzner Cloud eine ordentliche VM mit eigenen CPU-Kernen klicken.
  2. DNS von stream.bbb.lurk.space auf die Kiste lenken.
  3. Auf der Maschine den BBB Broadcaster und dessen Abhängigkeiten (Docker, Docker Compose) installieren, siehe README.
  4. Die .env-Datei erstellen, für die Meeting ID siehe Abschnitt oben.
  5. docker-compose up --build, vielleicht in einem tmux oder alternativ daemonized.
  6. Wenn vorbei, dann Docker Compose beenden und die Aufzeichnung wegsichern.
  7. Die VM jetzt wieder löschen, sodass wir nur die paar Stunden Laufzeit bezahlen müssen.
  8. DNS wieder anpassen, z.B. auf den localhost zeigen lassen.

Das weggesicherte Video lässt sich mit folgendem ffmpeg-Befehl - bzw. einer Variante davon - zu einem handlichen Video kodieren.

ffmpeg \
        -ss 00:11:00 -i meeting-20210703174740.mkv -ss 00:00:27 \
        -c:v libx264 -preset faster -crf 23 -pix_fmt yuv420p -movflags +faststart \
        -c:a aac -b:a 128k \
        -metadata title="Zivilgesellschaft vs. Geheimdienste" \
        -metadata author="Constanze Kurz" \
        -metadata album="[hsmr] Hackspace Marburg, Sofaecke 01" \
        -metadata year="2021" \
        sofaecke01.mp4
  • Das doppelte -ss liegt darin, dass das Seeken vor dem Input schneller, aber ungenauer ist. Somit wird also grob zu Minute elf gesprungen und danach passend zu 00:11:27.
  • Zur H.264-Zeile sind alle Flags bei ffmpeg dokumentiert.
  • Audio ist AAC ohne Anpassungen.
  • Metadaten basierend auf dieser Tabelle.