Matrix ist ein neues Kommunikationssystem, allerdings nicht mit dem Anspruch, das nächste coole, hippe Teil zu werden, sondern Matrix setzt von Anfang an auf Integration und will Brücken zu anderen Systemen bauen.

Die Kommunikation im Internet hat sich leider zu einer zerklüfteten Inselwelt entwickelt, in der jede Gruppe mit ihrem eigenen System arbeitet und inkompatibel zur Außenwelt ist. Da der Umzug von einem Netzwerk in ein anderes umständlich ist und viele Netzwerke ihre speziellen Vorteile haben, will Matrix die Nutzer eben dort nicht wegholen, sondern dort erreichen. Jeder darf das System nutzen, das ihm gefällt – egal ob XMPP, IRC, Whatsapp, Telegram, Hangouts, Facebook oder SMS – und Matrix kommuniziert mit ihnen. Ein Ansatz, der mir wahnsinnig gut gefällt!

Wer sich Matrix erst einmal nur ansehen will, kann den Webclient Element benutzen und einen öffentlichen Raum wie

Funktionstest

Wenn alles durch ist, den Dienst (neu-)starten und dann sollte er auf dem Port 8008 lauschen.

% sudo systemctl restart matrix-synapse.service
% sudo ss -tlp |grep 8008
LISTEN     0      0        ::1:8008        :::*         users:(("python",pid=24088,fd=10))

Ob der Server für Clients erreichbar ist, kann man per netcat oder telnet prüfen (auch fürs System-Monitoring hilfreich):

% netcat localhost 8008 <<<$'GET /_matrix/client/versions HTTP/1.0\r\n\r'
HTTP/1.0 200 OK
Content-Length: 54
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
Server: Synapse/0.29.0
Cache-Control: no-cache, no-store, must-revalidate
Date: Sun, 20 May 2018 15:16:36 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Content-Type: application/json

{"versions": ["r0.0.1", "r0.1.0", "r0.2.0", "r0.3.0"]}

Im Log /var/log/matrix-synapse/homeserver.log sollte dann etwas in dieser Art stehen:

2018-05-20 17:12:18,831 - synapse.access.http.8008 - 95 - INFO - GET-65532- - - 8008 - Received request: GET /_matrix/client/versions
2018-05-20 17:12:18,832 - synapse.access.http.8008 - 129 - INFO - GET-65532- - - 8008 - {None} Processed request: 1ms (0ms, 0ms) (0ms/0ms/0) 54B 200 "GET /_matrix/client/versions HTTP/1.0" "None"

Ob der Server für andere Server erreichbar ist, also ob die Federation funktioniert, kann man über die Adresse https://matrix.org/federationtester/api/report?server_name=… mit dem entsprechenden Servernamen prüfen. In der Ausgabe sollte irgendwo "AllChecksOK": true erscheinen und am Ende sollte "ConnectionErrors": {} leer sein. Alternativ dazu gibt es noch den Fed-tester.

Worker einrichten

Worker sind noch eine recht junge Entwicklung (Stand: Mai 2019) von Synapse, aber ihr Einsatz lohnt sich. Ziel der Entwicklung ist es, Teile aus dem Hauptprozess herauszulösen und in eigenständige Prozesse zu gliedern. Auf diese Weise kann man …

Element einrichten

Element ist ein Webclient für Matrix, der vom Matrix-Team entwickelt wird. Man kann ihn direkt von app.element.io aus nutzen oder man richtet sich eine eigene Instanz ein. Es gibt zwar ein Debian-Paket, aber dieses hängt vom Paket gconf ab und zieht auf einem Server einen Rattenschwanz an Paketen hinterher. Deshalb verwende ich das allgemeine Paket und entpacke es in /srv/www/m.jo-so.de.

In dem Verzeichnis liegt die Datei config.sample.json, welche nach config.json kopiert werden muss. Darin sollte bei default_hs_url die Adresse des eigenen Matrix-Servers eingetragen werden. Ich habe bei mir die Anmeldung von Gästen deaktiviert "disable_guests": true, bei den Raum-Servern meinen eigenen ergänzt und bei piwik den Schlüssel url durch X-url ersetzt, damit auch sicher kein Piwik angesprochen wird.

Matrix nutzt zwei verschiedene Kommunikationswege: 8008 für Clients und 8448 für andere Server, so wie es auch bei XMPP der Fall ist. Da Matrix eine REST-Schnittstelle verwendet, kann man den Verkehr zum eigentlichen Matrix-Dienst durch einen Nginx leiten, so dass der Matrix-Dienst nicht als root laufen muss und man die Möglichkeiten Nginx' nutzen kann: SSL-Zertifikate, HTTP/2.0, Einschränken der IP-Adressbereiche, HTTP-Authentifizierung und die Vermischung mit anderen Inhalten, denn Matrix nutzt nur das Verzeichnis /_matrix.

server {
    listen 443 http2 ssl;
    listen [::]:443 http2 ssl;

    root /srv/www/default;

    location /_matrix/ {
        access_log off;
        proxy_pass http://[::1]:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    location /.well-known/matrix/server {
            access_log off;
            add_header Access-Control-Allow-Origin *;
            return 200 '{"m.server": "alea.gnuu.de:443"}';
    }

    location /.well-known/matrix/ {
        access_log off;
        proxy_pass http://[::1]:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    location /m/ {
        access_log off;
        alias /srv/www/m.jo-so.de/;
    }
}

% sudo systemctl reload nginx.service

Als einfachere Funktionsprüfung kann man im Browser die Adresse /_matrix/client/versions seines Servers aufrufen und sollte eine Ausgabe ähnlich wie bei https://matrix.org/_matrix/client/versions erhalten.

Ein kleines Skript für die regelmäßige Aktualisierung von Element habe ich in der Anleitung zu jq beschrieben.

Neuen Benutzer anlegen

Man kann es allen Nutzern erlauben, ein neues Konto über die Weboberfläche anzulegen, in dem man in der Serverkonfiguration homeserver.yaml die Option enable_registration: true setzt. Falls man jedoch nur einen geschlossenen Nutzerkreis haben will, kann man in der Serverkonfiguration bei registration_shared_secret: "<SECRET KEY>" eine geheime Zeichenkette (generiert mit pwgen -s 64 1) eintragen und dann mit dem Kommandozeilenprogramm register_new_matrix_user neue Benutzer anlegen:

% register_new_matrix_user -c /etc/matrix-synapse/homeserver.yaml http://localhost:8008
New user localpart [root]: user
Password:
Confirm password:
Make admin [no]:
Sending registration request...
Success.

Das Zurücksetzen des Passworts gibt es die händische Bearbeitung der Datenbank und eine API-Funktion, die man mit curl aufrufen kann.

TURN für Video- und Audioverbindungen einrichten

Für Video- und Audioverbindungen ist neben dem Matrix-Server noch ein TURN-Server notwendig, der zwischen Geräten vermittelt, die sich nicht direkt erreichen können; z. B. durch NAT. Direkt von Debian gibt es das Paket coturn.

Nach der Installation (apt install coturn) muss die Konfigurationsdatei /etc/turnserver.conf wie folgt angepasst werden (Entnommen aus der Doku von synapse):

lt-cred-mech
use-auth-secret
static-auth-secret=Geheimen Schlüssel mit `pwgen -s 64 1` erzeugen
realm=jo-so.de
no-tcp-relay
cert=/etc/letsencrypt/live/host/fullchain.pem
pkey=/etc/letsencrypt/live/host/privkey.pem
# Matrix uses only TURN
no-stun
no-multicast-peers
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
pidfile="/dev/null"
secure-stun
mobility
no-tlsv1

Den geheimen Schlüssel und die TURN-Verbindungen muss man noch in der homeserver.yaml eintragen und den Synapse-Server neustarten.

turn_uris:
  - "turn:alea.gnuu.de:3478?transport=udp"
  - "turn:alea.gnuu.de:3478?transport=tcp"

turn_shared_secret=Geheimer Schlüssel

Die Konfiguration für Systemd wird bei der Installation automatisch generiert, weshalb ich sie durch folgende ersetzt (systemctl edit --full coturn.service) habe. Damit läuft der Prozess auch nicht mehr als Benutzer root.

# https://github.com/coturn/coturn/blob/master/rpm/turnserver.service.fc

[Unit]
Description=coturn
Documentation=man:coturn(1) man:turnadmin(1) man:turnserver(1)
After=network.target

[Service]
User=turnserver
Group=turnserver
SupplementaryGroups=ssl-cert
PIDFile=/run/turnserver.pid
ExecStart=/usr/bin/turnserver -c /etc/turnserver.conf --log-file stdout
Restart=on-abort

LimitNOFILE=999999
LimitNPROC=60000
LimitRTPRIO=infinity
LimitRTTIME=7000000
CPUSchedulingPolicy=other
UMask=0007
NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target

AppArmor /etc/apparmor.d/usr.bin.turnserver

include <tunables/global>

profile /usr/bin/turnserver {
    include <abstractions/base>
    include <abstractions/ssl_certs>
    include <abstractions/ssl_keys>

    /etc/turnserver.conf r,
    owner /var/lib/turn/turndb rwk,
}

Sicherheitshinweis: Trotz der Einstellungen für Verschlüsselung des TURN-Servers konnte ich mit Wireshark noch die Inhalte der Verbindung einsehen. Ich habe nichts gefunden, wie ich die TURN-Verbindung weiter absichern kann.

Source: laptop (Port: 59697)
Destination: alea.gnuu.de (Port: 3478)
Protocol: STUN
Info: Allocate Request UDP lifetime: 3600 user: 1527072691:@test:alea.gnuu.de realm: alea.gnuu.de with nonce

Source: laptop (Port: 59697)
Destination: alea.gnuu.de (Port: 3478)
Protocol: STUN
Info: CreatePermission Request XOR-PEER-ADDRESS: 192.168.178.21:48261 user: 1527072691:@test:alea.gnuu.de realm: alea.gnuu.de with nonce

Im Wireshark konnte ich aber auch beobachten, dass meine beiden Testgeräte direkt per IPv6 kommuniziert haben, ohne den Weg über den TURN-Server.

Wechsel der Datenbank zu Postgres

Ich habe ursprünglich mit sqlite begonnen und irgendwann die Datenbank auf Postgres umgestellt. Hier die Befehle zum Einrichten des Benutzers und der Datenbank. Dabei ist darauf zu achten, dass der Benutzername identisch mit dem Systembenutzer seien muss, unter dem auch der Dienst läuft, sonst funktioniert die passwortlose Anmeldung per Socket nicht; systemctl show -pUser matrix-synapse.service.

% sudo apt install python-psycopg2
% sudo -u postgres sh -c 'createuser matrix-synapse
  && createdb -O matrix-synapse -l C.UTF-8 -T template0 matrix'

In der Konfiguration homeserver.yaml müssen noch die Einstellungen wie folgt geändert werden:

# Database configuration
database:
  # The database engine name
  name: psycopg2
  # Arguments to pass to the engine
  args:
    database: matrix
    user: matrix-synapse
    host: /run/postgresql
    application_name: matrix-synapse

Zusätzlich zu den Parametern für connect kann man noch Parameter für das Connection-Pooling angeben. Leider ist zu dem von Synapse genutzten Connection-Pool zu sagen, dass es ungenutzte (idle) Verbindungen nicht automatisch schließt. Eine Lösung hierfür habe ich noch nicht gefunden.

Bei Systemd sollte man dann noch angeben, dass der Server erst nach Postgres gestartet wird:

% sudo systemctl edit matrix-synapse.service
[Unit]
After=network-online.target postgresql.service

Für die Datenmigration von sqlite zu Postgres gibt es das Programm synapse_port_db. Dieses kann auch mehrfach ausgeführt werden, falls man den Dienst weiterlaufen lässt und eine Kopie der sqlite-Datenbank verwendet hat oder es zu Fehlern kam. Ich habe aber einfach für die gesamte Zeit den Dienst bei mir abgeschaltet.

% cd /tmp && sudo -u matrix-synapse synapse_port_db -v --curses \
  --sqlite-database /var/lib/matrix-synapse/homeserver.db \
  --postgres-config /etc/matrix-synapse/homeserver.yaml

Wenn es zu einem Fehler kommt und in der Datei port-synapse.log etwas von Failed to insert: room_depth steht, dann zuerst einmal nachsehen, ob der Fehler 3214 bereits gelöst ist oder ggf. den ALTER TABLE-Befehl von dort ausführen und die Migration noch einmal starten.

Wartungsarbeiten

Auf der Webseite von Matrix werden diverse Anleitungen zur Wartung von Synapse und Moderation von Matrix angeboten.

Datensicherung

Wichtig für eine Wiederherstellung einer Matrix-Installation sind

Um das Backup zu verkleinern kann man die Verzeischnisse /var/lib/matrix-synapse/media/(remote_|url_cache)*/* auslassen. Deren Inhalt lässt sich im Nachhinein über andere Server wiederherstellen.

Bei der Wiederherstellung des Systems muss man mit dem Zugangsschlüssel (Access-Token) eines Admin-Benutzers den Cache zurücksetzen, damit Synapse die Dateien, die vom Backup ausgeschlossen waren, neu beschafft. Den Zugangsschlüssel findet man in Element unter Einstellungen, Hilfe, Fortgeschritten und nutzt diesen für purge_remote_media mit aktuellem Zeitstempel:

curl -H "Authorization: Bearer <access_token>" -d '' "http://localhost:8086/_synapse/admin/v1/purge_media_cache?before_ts=$(date +%s000)"

Ungenutzte Räume entfernen

Wenn Nutzer einen Raum verlassen, der über mehrere Server verteilt liegt, bleiben auf dem eigenen Server die alten Nachrichten zurück. Da diese nicht mehr genutzt werden, ist es sinnvoll, diese zu entfernen:

mx_token=<Token eines Admins>
for room in $(curl -s --header "Authorization: Bearer $mx_token" \
  'http://localhost:8008/_synapse/admin/v1/rooms?limit=800' \
  |jq -r '[ .rooms[] | select(.joined_local_members == 0) ] |sort_by(.state_events) | .[].room_id')
do
    echo -n "Purging $room: "
    time curl -s -o /dev/null --header "Authorization: Bearer $mx_token" \
      -X POST -H "Content-Type: application/json" -d "{ \"room_id\": \"$room\" }" \
      'http://localhost:8008/_synapse/admin/v1/purge_room'
done

state_groups_state aufräumen

In der Tabelle state_groups_state legt Synapse irgendwelche Daten ab (weiß jemand welche? Stift oben rechts nutzen), die zum Teil redundant sind. Daher kann man diese Tabelle mit dem Programm rust-synapse-compress-state aufräumen und somit den Speicherverbrauch eindämmen.

Wer Rust installiert hat, kompiliert das Programm besser selbst aus den Quellen, denn Releases passieren eher selten:

cargo install --git https://github.com/matrix-org/rust-synapse-compress-state

Danach liegt das Programm im Verzeichnis ~/.cargo/bin/synapse-compress-state. Beim Aufruf erstellt es aber nur eine SQL-Dateien, die die eigentlichen Änderungen beschreiben. Man muss also nach jedem Durchlauf des Programms noch das entstandene SQL-Skript ausführen. Daher habe ich mir das Hilfsprogramm synapse-clear-state erstellt, um auch alle anderen Schritte dafür durchzuführen. Dieses Hilfsprogramm rufe ich mithilfe von Systemd auf, damit es im Hintergrund läuft (bei mir dauert es über 2 Stunden) und ich die Meldungen im Nachhinein prüfen kann

% sudo systemd-run --nice=4 -pCPUSchedulingPolicy=batch \
  -pIOSchedulingClass=idle --uid=matrix-synapse --collect \
  --unit=synapse-clear-state ~/bin/synapse-clear-state
% journalctl -Stoday -u synapse-clear-state

Das Programm hat nämlich die Macke und erzeugt ein größeres Ergebnis als der Ausgangszustand. Da auch die Prüfung auf die Korrektheit des neuen Zustands teilweise sehr lange dauert, kann man mit der Option -m bei synapse-compress-state die Mindestanzahl an Tabellenzeilen angeben, für die der neue Zustand genommen wird. Abgesehen von dieser Macke liefert das Programm aber teilweise eine Reduktion auf 10 % (sic!) bei einigen Räumen.

Synapse muss während der Ausführung nicht angehalten werden, sodass es egal ist, wie lange das Programm läuft oder ob man es zwischendurch abbricht. Ich rufe es bei mir unregelmäßig alle paar Wochen auf.

device_inbox aufräumen

Die Schema-Definition der Datenbank von Synapse ist piep. Daher werden Einträge in der Tabelle device_inbox nicht gelöscht, wenn die dazugehörigen Geräte gelöscht werden. Mit folgendem SQL-Befehl kann man dies händisch erledigen:

DELETE
-- SELECT COUNT(*)
FROM device_inbox
WHERE device_id IN (SELECT device_id FROM devices WHERE hidden = true)
  OR device_id NOT IN (SELECT device_id FROM devices);

event_forward_extremities aufräumen

Wenn ein Server offline ist, kommen die Daten im Nachhinein nicht immer in der korrekten Reihenfolge an. Dadurch kann es passieren, dass Synapse in der Tabelle event_forward_extremities ungünstige Einträge erzeugt, die dazu führen, dass Synapse sehr viel Arbeitsspeicher verbraucht und der Dienst teilweise unbenutzbar wird. Daher sollte man regelmäßig prüfen, ob diese ungünstigen Einträge entstanden sind und bei mehr als fünf diese entfernen. Ein paar wenige stören nicht und man kann sie belassen, weil sonst Synapse abgeschaltet werden muss (was potentiell zu neuen fehlerhaften Einträgen führt).

Ich beobachte diese fehlerhaften Einträge auch noch mit Synapse 1.34, aber ich prüfe auch nur etwa einmal im Monat darauf. Zum Entfernen verwende ich ein angepasstes Skript , das ich anhand des Beitrags im Bugtracker erstellt habe:

sudo -u matrix-synapse LC_ALL=C.UTF-8 psql -f matrix-clean-extremities.sql matrix

Alte Anhänge löschen

Die beiden Verzeichnisse mit remote in /var/lib/matrix-synapse/media können mit der Zeit verdammt groß werden. Sie enthalten Bilder, Video und Dateien, die Nutzer auf anderen Servern in Räume geschickt haben. Nach einem gewissen Zeitraum sind die Dateien aber nicht mehr von Bedeutung und können gelöscht werden. Sollte doch einmal ein Nutzer auf dem lokalen Server so weit zurück scrollen oder springen, werden die Datei von anderen Servern wieder geholt, da mindestens der Ursprungsserver diese als lokale Daten betrachten und sie nicht löschen sollte.

Aber sicher ist dies nicht und bisher habe ich auch noch keine Möglichkeit gefunden, Chats zu exportieren. Wer also den nötigen Festplattenplatz hat, sollte die Dateien lieber liegenlassen, denn das Löschen betrifft alle Räume, inklusive privater, verschlüsselter Chats mit Urlaubsbildern und Dokumenten.

Purge Remote Media API

curl -H "Authorization: Bearer <access_token>" -d '' \
  "http://localhost:8008/_synapse/admin/v1/purge_media_cache?before_ts=$(date -d-6months +%s)000"

Alte Nachrichten löschen

Räume, in denen viel geschrieben wird, wachsen dementsprechend schnell an. Wer auf seinem Server Platz schaffen will oder wer alte Nachrichten aufgrund der Datensparsamkeit beseitigen möchte, kann dies für einzelne Räume durchführen.

Jedoch gilt wie oben bei den alten Anhängen: Sollte ein Nutzer die alten Nachrichten wieder anfragen, werden diese von den Nachbarservern beschafft, sofern sie dort noch verfügbar sind. Für private Unterhaltungen könnten das einige Nutzer nicht so lustig auffassen, wenn sie auf die Konzertpläne vom letzten Jahr oder die Glückwünsche vom letzten runden Geburtstag nicht mehr zugreifen können. Daher: Wer den Platz hat, sollte die Nachrichten besser aufheben.

curl -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{ "delete_local_events": false, "purge_up_to_ts": '$(date -d-6months +%s)000' }' \
  "http://localhost:8008/_synapse/admin/v1/purge_history/$room_id"

Purge History

Räume aus der Datenbank kratzen

Ich habe bei mir einen kaputten Zustand des Synapse-Servers erreicht, sodass ich dem Raum #matrix:matrix.org nicht mehr beitreten konnte, ihn aber auch nicht aus Element entfernen konnte. Es gibt im git das Programm nuke-room-from-db, mit dem man die Datenbank bereinigen kann.

Unerwünschte Räume entfernen

Matrix ist so entworfen, dass der Administrator eines Servers nicht in den Inhalt eines Raumes eingreifen kann. Wenn in einem Raum Nachrichten und Anhänge ausgetauscht werden, die vom Administrator nicht erwünscht sind, kann der Administrator nur den kompletten Raum vom Server löschen. Konkret: Wenn in einem Raum niemand Spamer und störende Nutzer hinauswirft, kann der Serveradministrator nichts tun – er hat technisch keine Möglichkeiten, die Benutzer zu entfernen, weil der Raum über alle beteiligten Server verteilt liegt und er die Struktur mit händischen Eingriffen zerstören würde.

Der folgende Aufruf löscht den Raum mit der MX-ID $room_id und blockiert den erneuten Beitritt zu dem Raum. Die Teilnehmer des lokalen Servers werden alle vom Raum abgemeldet und in einen neuen Raum eingetragen, in dem der Nutzer @abuse:example.com Administrator ist und die Nutzer keine Schreibrechte haben. Als Information erscheint im neuen Raum die Nachricht message. Den alten Raum würde ich erst in einem zweiten Schritt durch die normale Funktion zum Aufräumen aus der Datenbank entfernen, damit der Aufruf nicht zu lange dauert.

curl -H "Authorization: Bearer <access_token>" \
  -X DELETE -d '{
    "new_room_user_id": "@abuse:example.com",
    "room_name": "Löschung des Raums …",
    "message": "Der Raum … wurde gelöscht, weil …",
    "block": true,
    "purge": false
  }' "http://localhost:8008/_synapse/admin/v1/rooms/$room_id"

Versiegelung von Räumen

Matrix bietet die Möglichkeit, in einem Raum einen Grabstein (engl. tombstone) zu platzieren und somit die Kommunikation über diesen Raum zu beenden. Diese Funktionalität wird bei der Aktualisierung der Raumversionen genutzt, indem in den alten Raum ein Grabstein gesetzt und ein neuer Raum (mit aktueller Version) eröffnet wird. Auf diesem Grabstein steht dann auch der Verweis auf den neuen Raum, wo das Leben weiter geht. 😉

Diese Versiegelungen für einen Raum kann man in Element mit /devtools und Send Custom Event erstellen. Der Event Type ist dann m.room.tombstone und als Content gibt man folgendes an:

{
        "body": "This room has been replaced, blblabla",
        "replacement_room": "!newroom:example.org"
}

Oder man erstellt die entsprechende Nachricht über die Kommandozeile mit folgendem Aufruf. Dabei muss $mx_token von einem Admin des Raumes sein und $old_roomid bzw. $new_roomid die ID (mit !…:example.org) sein:

curl -s -X PUT -H "Authorization: Bearer $mx_token" \
  -H "Content-Type: application/json" \
  --data-binary "{\"replacement_room\":\"$new_roomid\"}" \
  "https://…/_matrix/client/r0/rooms/$old_roomid/state/m.room.tombstone"

Clients für Matrix

Element

Weitere Apps

Desktop-Programme

Kommunikation mit anderen Netzwerken

IRC

Von matrix.org werden offiziell Verbindungen in verschiedene IRC-Netze angeboten. Um zum Beispiel in den IRC-Channel #firefox bei moznet einzutreten, betritt man den Matrix-Raum #mozilla_#firefox:matrix.org. Genaueres gibt's in der Anleitung zum IRC-Appservice.

Nachdem man das erste Mal einen Chatraum für ein anderes Netzwerk betreten hat, wird man von dem Dienst für das Netzwerk in einen zusätzlichen Raum eingeladen; alternativ kann dem auch selbst einen Direktchat mit dem Appservice user starten. In diesem Raum kann man über spezielle Nachrichten den Dienst steuern: * mit !nick seinen Namen in dem fremden Netzwerk ändern, * mit !join weitere Räume betreten – ich betrete neue Räume fast nur mit diesem Befehl und lasse mich in den eigentlichen Matrix-Raum einladen * mit !help die allgemeine Hilfe abfragen

Wenn man Schwierigkeiten mit den Räumen hat, weil zum Beispiel der Nickname nicht stimmt oder Nachrichten nicht ankommen, kann man mit dem Befehl !quit auch komplett die Brücke verlassen und von vorn beginnen.

Nick-Name registrieren bei libera.chat

Einige Channels in IRC sind nur für registrierte Benutzer (+r) zugänglich. Daher ist es sinnvoll, sich in diesen Netzwerken einen Namen zu registrieren. Exemplarisch beschreibe ich hier den Weg für Libera Chat, aber mit anderen Netzwerken sollte es genauso ablaufen:

  1. mithilfe eines Webclients zum Netzwerk verbinden
  2. seinen gewünschten Namen auswählen: /nick jo-so
  3. ein Passwort mit dem Lieblingspasswortmanager generieren und die Registrierung starten: /msg NickServ REGISTER YourPassword youremail@example.com
  4. die E-Mail abrufen und den Befehl daraus im Webchat ausführen: /msg NickServ VERIFY REGISTER jo-so …; dies sollte mit einer Erfolgsmeldung bestätigt werden
  5. den Webchat schließen
  6. im Matrix-Raum mit dem Appservice user für liberachat IRC Bridge status den Nickname und das Passwort hinterlegen: !username jo-so und !storepass YourPassword
  7. danach !reconnect ausführen
  8. wenn die Verbindung wieder besteht, sollte nach !nick die Meldung »Currently connected to IRC networks: irc.libera.chat as jo-so« erfolgen

Theoretisch müsste auch alles von Matrix aus funktionieren, aber ich habe dies nicht getestet.

  1. mit dem Appservice user einen Direktchat starten
  2. !nick jo-so
  3. !cmd MSG NickServ REGISTER YourPassword youremail@example.com
  4. !cmd MSG NickServ VERIFY REGISTER jo-so …
  5. !username jo-so
  6. !storepass YourPassword

Weitere Dokumentationen:

Whatsapp

Whatsapp ist ein geschlossenes System und bietet keine saubere Schnittstelle, über die man an dem Netzwerk teilnehmen könnte. Für Matrix wurde jedoch über den Whatsapp-Webchat ein Weg in das Netzwerk gefunden. Über den Dienst mautrix-whatsapp werden die Kontakte von Whatsapp importiert und jeweils ein Raum erstellt. Alle Nachrichten in dem Matrix-Raum werden dann zu Whatsapp übertragen und umgekehrt.

Wer bereits den Whatsapp-Webchat nutzt, kennt dessen Schwäche und diese bleibt auch mit der Matrix-Kopplung: Das Handy mit der Whatsapp-App muss immer online sein. Allerdings kann man sich auch eine virtuelle Maschine mit Anbox einrichten und dort Whatsapp installieren.

In der Liste der unterstützen Funktionen kann man sehen, was alles bereits umgesetzt wurde und was noch geplant ist. Im Matrix-Raum des Projekts kann man sich auch immer Hilfe holen und Fragen stellen.

Installation von mautrix-whatsapp

Zum Erstellen des Programms benötigt man go; Installation mit apt install golang. Gemäß der offiziellen Anleitung holt und übersetzt man das Programm mit folgendem Befehl:

go get -u maunium.net/go/mautrix-whatsapp

Der Quelltext befindet sich danach in dem Verzeichnis ~/go/src/maunium.net/go/mautrix-whatsapp und das Ergebnis ist unter ~/go/bin/mautrix-whatsapp. Später kann man mit dem gleichen Befehl den Quelltext aktualisieren und neu übersetzen.

Ich lasse den Dienst auf dem selben Server wie Synapse laufen und habe mir dafür ein Verzeichnis /srv/matrix/whatsapp-bridge angelegt und das Programm dort abgelegt. In dem Verzeichnis habe ich die Unterverzeichnisse db und logs erstellt und entsprechende Eigentümer (Gruppe adm) und Rechte (g=rxs,o=) dafür gesetzt. Für das Hauptverzeichnis und die Dateien habe ich auch entsprechende Rechte gesetzt, um die Fehlergefahr etwas einzuschränken.

Im Quelltextverzeichnis liegt eine Datei example-config.yaml. In dieser muss man die Einstellungen anpassen:

Danach sollte man Datei example-config.yaml in das srv-Verzeichnis als config.yaml kopieren und dort ./mautrix-whatsapp -g aufrufen. Leider werden dabei alle Kommentare in der Datei zerstört, weshalb es besser ist, die abgeänderten Werte aus der config.yaml von as_token und hs_token in die example-config.yaml zu übertragen und am Ende diese Datei noch einmal in das srv-Verzeichnis kopieren.

In diesem Schritt wurde auch die Datei registration.yaml erstellt. Diese ist für den Home-Server wichtig, damit er den App-Service kennt. In der Serverkonfiguration /etc/matrix-synapse/homeserver.yaml muss diese Datei unter app_service_config_files eingetragen werden und danach Synapse neugestartet werden (systemctl restart matrix-synapse):

app_service_config_files:
    - /srv/matrix/whatsapp-bridge/registration.yaml

Für den Dienst habe ich noch eine Konfiguration für systemd erstellt (systemctl edit --force --full matrix-whatsapp.service) und dabei den vorhandenen Benutzer des Matrix-Servers wiederverwendet.

[Unit]
Description=Bridging service between Matrix and Whatsapp
Documentation=https://maunium.net/go/mautrix-whatsapp
After=network.target

[Service]
ExecStart=/srv/matrix/whatsapp-bridge/mautrix-whatsapp
WorkingDirectory=/srv/matrix/whatsapp-bridge
User=matrix-synapse
Group=daemon
Restart=on-failure

NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target

Den Dienst muss man dann noch aktivieren und starten: systemctl enable --now matrix-whatsapp. Alle Logmeldungen sind damit auch im Journal enthalten.

Nutzung von mautrix-whatsapp

Grundsätzlich kann der Matrix-Whatsapp-Dienst mit mehreren Benutzern umgehen, jedoch sollte man sich bewusst sein, dass der Administrator des Dienstes auf die Zugangsschlüssel für Whatsapp und damit indirekt auf die Handy-Kontakte Zugriff hat. Als Administrator wiederum kann man in der Konfiguration im Abschnitt bridge.permissions einstellen, wer den Dienst nutzen darf.

Für die Nutzung muss man als erstes einen Chat mit dem entsprechenden Dienst beginnen; bei mir @whatsappbot:alea.gnuu.de. Über diesen Raum kann man den Whatsapp-Dienst steuern: Mit der Nachricht help bekommt man die Hilfe angezeigt.

Mit der Nachricht login wird ein QR-Code angezeigt, den man mit dem Handy über die App (Menü: Whatsapp Web) einscannen muss. Nach erfolgreicher Kopplung wird in der App der Eintrag Mautrix-WhatsApp bridge aufgeführt und der Dienst lädt einen in die Räume mit den Whatsapp-Kontakten ein – das ist etwas nervig, weil man gegebenenfalls über hundert Einladungen einzeln bestätigen muss.

Danach merkt man nicht mehr den Unterschied, ob man mit einem Matrix-Partner oder einem Whatsapp-Partner schreibt.

mxmpp

% git clone 'https://github.com/anewusername/mxpp.git'
% cd mxpp
% git clone 'https://github.com/matrix-org/matrix-python-sdk.git'
% ln -s matrix-python-sdk/matrix_client .
% git clone 'https://github.com/yaml/pyyaml'
% ln -s pyyaml/lib3/yaml .
% sudo agi python3-sleekxmpp

https://gitlab.com/ecartman/sleekxmpp-omemo-plugin

Skype-Bridge

https://github.com/matrix-hacks/matrix-puppet-skype

https://github.com/matrix-hacks/matrix-puppet-facebook

matterbridge

https://github.com/42wim/matterbridge

XMPP-Bridge mit Prosody

https://gitlab.com/uhoreg/matrix-appservice-prosody

Dienste innerhalb von Matrix

t2bot.io

t2bot bietet mehrere öffentliche Bots und Bridges an, so dass man die Dienste nicht selbst einrichten und pflegen muss.

Ein Bot für Umfragen

Manchmal möchte man in Gruppen eine Umfrage durchführen. Dies lässt sich leicht mit dem poll-bot erreichen.

% git clone https://github.com/shawnanastasio/matrix-pollbot
% pip install matrix-bot-api

% register_new_matrix_user -c /etc/matrix-synapse/homeserver.yaml http://localhost:8008
New user localpart [root]: pollbot
Password:
Confirm password:
Make admin [no]:
Sending registration request...
Success.
% cd matrix-pollbot
% cp config.ini.example config.ini && emacs config.ini

% python3 pollbot.py

Den Bot kann man dann in den Raum einladen und mit !newpoll eine neue Umfrage starten (kleiner Tipp: Keine URLs verwenden, sonst wird immer wieder URL-Preview aktiv). Danach sendet man als Nachricht in den Raum die Frage und danach als einzelne Nachrichten die möglichen Antworten. Mit !startpoll schließt man dann die Erstellung ab. Jeder im Raum kann dann mit !info sich die Umfrage ansehen, mit !vote <number> darüber abstimmen und mit !endpoll wird die Umfrage beendet. Alle Befehle werden bei !pollhelp aufgelistet.

Gedanken zu Matrix

Wahl der Kennungen

Die Benutzernamen bei Matrix folgen dem Schema @user:domain, Raumnamen haben das Schema #raum:domain, für Gemeinschaften ist es +name:domain und interne IDs lauten !id:domain. Im Vergleich mit XMPP ist dies sehr gut, denn wenn irgendwo im WWW diese IDs auftauchen, gibt es genug Spammer die sie einfach als E-Mailadresse verwenden. Ich bekomme auch massenweise Spam für Message-IDs von Mailinglisten oder Usenet-Beiträgen, weil diese eben auch wie eine E-Mailadresse aufgebaut sind.

Anfangs fand ich die Form der Kennungen merkwürdig, aber mittlerweile begrüße ich die Abweichung vom bekannten Muster der E-Mails.

Referenzierung von Nachrichten

Da Nachrichten eine global eindeutige Kennung haben, lässt sich auch darauf verweisen; Beispiel. Da zum Beispiel der Webclient Element auch ohne Anmeldung auf öffentliche Räume zugreifen kann, lässt sich damit angenehm auf Diskussion im Matrix-Netzwerk verweisen; Beispiel. Zu finden sind die Links unter anderem bei Element, wenn man auf die drei Punkte rechts neben einer Nachricht klickt.

Technisches Design

Was mir bei Matrix von Anfang an gefallen hat, waren die Konzepte des Systems. Im Grunde ist Matrix sehr einfach: es gibt nur Benutzer, Räume und Nachrichten. Das hat zur Folge, dass ein direkter Chat mit einer Person für Matrix auch über einen Raum läuft. Genauso kann man Selbstgespräche in einem Raum mit nur einem Teilnehmer führen – ich nutze das, um Nachrichten zwischen Handy und Computer auszutauschen. Es wird also nicht zwischen Direktnachrichten und Gruppengesprächen unterschieden. Der Kern der Technologie ist schlicht und einfach gehalten, was immer ein großer Vorteil ist.

Matrix verwendet für Räume intern langen Kennungen und bietet für diese einen lesbaren Alias an. Somit kann ein Raum einerseits über mehrere Namen (z. B. mit und ohne Bindestrich) erreichbar sein und andererseits auch von einem zu einem anderen Server umziehen, weil sich einfach der Alias ändert.

Das Netzwerk ist auch ähnlich dem Usenet aufgebaut. Es gibt für einen Raum, eine Nachricht oder einen Benutzer eine Quelle (den Home-Server), die für die eindeutige Kennung sorgt, aber nicht als zentrale Instanz agiert. Jeder Server, der über einen seiner Benutzer an einem Raum beteiligt ist, pflegt die Nachrichten des Raums. Somit kann ein Knoten des Netzwerks ausfallen und alle anderen drumherum können weiterarbeiten. Im Gegensatz zum Usenet kooperieren die Server automatisch untereinander und es bedarf keiner händischen Pflege der Feeds.

Die Verknüpfung von Benutzerkennungen mit anderen Merkmalen wie Telefonnummer oder E-Mailadresse hat man in einen eigenen Dienst ausgelagert: identity servers. Dieses Verzeichnis kann also völlig getrennt von dem Kommunikationsnetzwerk aufgebaut und gepflegt werden, sprich man muss sich nicht dort registrieren, um Matrix nutzen zu können.

Bilder und Dateien sind mit Bestandteil des Protokolls, so dass es nicht wie bei XMPP die Probleme bei der Übertragung über einen zusätzlichen Server gibt und Bilder integrieren sich auch gut in Gruppengespräche. In der Nachricht steht zwar auch nur ein Verweis auf die Daten, aber der Abruf dieser läuft über die gleiche Schnittstelle.

"content": {
  "body": "IMG_20180330_120003.jpg",
  "info": {
    "mimetype": "image/jpeg",
    "thumbnail_info": {
      "mimetype": "image/jpeg",
      "h": 320,
      "w": 240,
      "size": 82143
    },
    "h": 1632,
    "thumbnail_url": "mxc://alea.gnuu.de/uCtPLuiKLlQPFwVmxfuCEovu",
    "w": 1224,
    "size": 908347,
    "orientation": 0
  },
  "msgtype": "m.image",
  "url": "mxc://alea.gnuu.de/IkyOoQUDtHlIhHBRBNGqDqJI"
},

Was ich auch wirklich gut finde, ist die Schnittstelle per Rest/JSON. JSON finde ich wesentlich handlicher als XML und Rest-Aufrufe kann man auch einfach von der Kommandozeile aus mit curl ausführen; eine kleine Einführung in die Matrix-Schnittstelle.

Ein kleines Beispiel für die Vorzüge von REST

Mit Synapse 0.29 gab es immer wieder Performance-Probleme mit großen Räumen wie #matrix:matrix.org. Da der Zugriff auf den Server über normale HTTP-Aufrufe erfolgt, kann man diese auch mit Nginx filtern und so Funktionen ausgleichen, die in der eigentlich Software nicht eingebaut sind; wie hier vorgeschlagen:

krombeI: I don't suppose it's possible to disable pagination for only certain rooms?

aidalgol: It is not possible via synapse. But I implemented it in nginx as follows (i know it't hacky but currently my only solution for accidential OOMing...)

location ~ /_matrix/client/r0/rooms/.*cURbafjkfsMDVwdRDQ.*matrix.org/messages {
    # set as text/plain to let this custom message be displayed (at least in js-sdk)
    default_type text/plain;
    return 429 "fetching history for #matrix:matrix.org leads to server OOM => it is currently disabled";
}

Programmierung von Matrix

Matrix-Implementation in Rust

https://www.ruma.io/ Matrix-Raum

dsn-Traveller-Bot

Go

Sonstiges

Datenbank von Synapse