[~] BACK



~~ HIY V ~~

** MATRIX SYNAPSE **

[PRO] | [20230816] | [20230817] | [0x17]




Als der allmächtige CORTEX IMPLANT Admin Ramses den Matrix Server "cyberwa.re" startete war ich sehr glücklich. Leider ist CYBERWA.RE schneller wieder verschwunden als mir lieb war. Nachdem Gedanken selber einen NixOS Matrix-Server aufzusetzen kann ich auch verstehen warum :D.

0x01 - MATRIX / SYNAPSE


Was ist Matrix überhaupt? Matrix ist ein dezentralisierter Chatservice mit der Möglichkeit Räume zu erstellen, benutzt E2EE und es gibt coole Terminalclients dafür (IAMB zum Beispiel). Es gibt aktuell drei Möglichkeiten einen Matrix-Server zu betreiben. Synapse, Dendrite oder Conduit. [EDIT: Conduit kann Föderation]. Also bleiben noch Synapse und Dendrite. Dendrite ist in GO geschrieben und soll laut verschiedenen Aussagen deutlich besser performen als Synapse - allerdings bin ich mit meinem aktuellen Wissensstand nicht in der Lage Dendrite unter NixOS zum laufen zu bringen. Das folgt dann zu einem späteren Zeitpunkt. Also bleibt im Endeffekt nur Synapse.


0x02 - INFRASTRUKTUR


Natürlich braucht es hinter Matrix auch Blech. Matrix läuft bei mir nicht auf dem Main-Server, auf dem ihr den anderen Kram wie NERDBUDE, CCH! und so weiter finden könnt. Sondern auf einem separaten VPS. Meine Entscheidung kommt schlicht und ergreifend zu stande, weil ich die Arten von Infrastruktur gerne getrennt halte. Aktuell läuft Matrix-Synapse auf der kleinsten Kiste die Hetzner anbietet. Die Matrix Instanz soll nicht öffentlich sein und rein für mich. Von daher sollte die Config ausreichen. Domains braucht es natürlich auch. Also läuft der ganze Kram der nicht auf den Main Server soll, ab jetzt auf dem KYB3R.SPACE. Natürlich hat Hetzner NixOS noch nicht als Default OS im Sortiment, das muss also wieder von Hand drübergebügelt werden. Wie das funktioniert hatte ich bereits hier beschrieben: HIY II - SPEZIFIKATION. Wenn ihr also alles habt: NixOS, Domain und VPS kanns los gehen.


0x03 - INSTALLATION


Natürlich wollen wir nicht nur lokal installieren, sondern nutzen das wunderbare Potential von NixOS und bauen uns eine configurations.nix mit Matrix-Synapse.
Bei mir hat es bis zur funktionierenden Matrix-Synapse Instanz etwas gedauert (und Nerven gekostet - aber so funktioniert Lernen). Deswegen hier vorab kurz zu den Parts die wir benötigen um einen Matrix-Synapse Server aufzusetzen. Was wir brauchen sind die passenden DNS Eintries, SSL Zertifikate, einen funktionierenden NGINX Server, eine passende Subdomain auf er Matrix horcht, die passenden Firewall-Settings, einen Proxy für die Subdomain und das wars eigentlich. Bevor wir mit der eigentlichen Konfiguration anfangen, legen wir uns die passenden DNS entries an.


0x04 - DNS


Damit die Domain die IP findet und entsprechend weiterleitet, müssen passende DNS Entries angelegt werden. Als erstes kommen die A Einträge der Domain mit IP connected:

DNS
	* A 0.0.0.0 3600
	www A 0.0.0.0 3600
	A 0.0.0.0 3600
	m A 0.0.0.0 3600
	

Das setzt die Einträge für Subdomains, die www.kyb3r.space und für die Synapse Sub m.kyb3r.space. Aber Synapse braucht auch noch einen SRV Eintrag:

DNS
	_matrix._tcp.kyb3r.space SRV 10 8448 m.kyb3r.space 5 3600
	

Damit leiten wir die kyb3r.space, sobald Matrix-Traffic reinkommt, an die Subdomain m.kyb3r.space weiter. Ihr müsst natürlich die IP Adressen (0.0.0.0) gegen eure eigenen ausstauschen. Wenn die DNS entries gemacht sind geht es weiter mit der eigentlichen Konfiguration.

0x05 - CONFIGURATION.NIX


Was ich noch nie wirklich gemacht habe, was aber definitiv sinnvoll ist, ist die Konfiguration für NixOS zu modularisieren anstatt alles in einer configuration.nix zu pflegen. So hab ich zumindest mal Synapse aus der eigentlichen Config rausgekratzt und in /modules/synapse.nix gepackt. Aber fangen wir vorne an, bei der configuration.nix.
In die configuration.nix kommen die Konfigurationen für User, SSH, Firewall, NGINX, Zertifikate und natürlich der Link zur synapse.nix.

CONFIGURATION.NIX
{ config, pkgs, ... }: 
let 
   fqdn = "m.kyb3r.space";
   clientConfig."m.homeserver".base_url = "https://${fqdn}";
   serverConfig."m.server" = "${fqdn}:443";

in

{
  imports = [
    ./hardware-configuration.nix
    ./modules/synapse.nix
    
  ];

  boot.tmp.cleanOnBoot = true;
  zramSwap.enable = true;
  networking.hostName = "NB-ES-03";
  networking.domain = "";
  services.openssh.enable = true;
  users.users.root.openssh.authorizedKeys.keys = [
    ''ssh-rsa your-ssh-key'' 
  ];

Im ersten Block werden Variablen für Synapse definiert. Die $fqdn ist die Subdomain, die Synapse später nutzen soll und clientConfig ... und serverConfig ... sind Sachen die synapse später braucht. Danach kommen die Imports, hier zum einen die bekannte hardware.nixfür den Hardwarekram der Kiste auf der der Server läuft und natürlich die synapse.nix die sich dann um den eigentlichen Synapse Server kümmert. Der letzte Block konfiguriert die üblichen Parameter für NixOS wie SSH Key, Host-Name usw.
Der nächste Block ist dann schon etwas individueller. Hier geht es um den NGINX den wir für das Hosting brauchen.

CONFIGURATION.NIX
services.nginx = {
enable = true;

    recommendedTlsSettings = true;
    recommendedOptimisation = true;
    recommendedGzipSettings = true;
    recommendedProxySettings = true;

    virtualHosts."kyb3r.space" = 
     let 
      client = 
        { "m.homeserver"      = { "base_url" = "https://m.kyb3r.space"; };
          "m.identity_server" = { "base_url" = "https://matrix.org"; };
        };
      server = { "m.server" = "m.kyb3r.space:443"; };
    in
   {
      enableACME = true;
      forceSSL = true;
      root = "/var/www/kyb3r";
      locations."/.well-known/matrix/server".extraConfig = ''
          add_header Content-Type application/json;
          return 200 '${builtins.toJSON server}';
      '';
      locations."/.well-known/matrix/client".extraConfig = ''
          add_header Content-Type application/json;
          add_header Access-Controll-Allow-Origin *;
          return 200 '${builtins.toJSON client}';
      '';
    };

    virtualHosts."www.kyb3r.space" = {
      enableACME = true;
      forceSSL = true;
      root = "/var/www/kyb3r/";
    };

    virtualHosts."m.kyb3r.space" = 
      let 
        client = 
          { "m.homeserver"      = { "base_url" = "https://m.kyb3r.space"; };
            "m.identity_server" = { "base_url" = "https://matrix.org"; };
          };
        server = { "m.server" = "m.kyb3r.space:443"; };
      in
      {
        enableACME = true;
        forceSSL = true;
        locations."/_matrix".proxyPass = "http://localhost:8448";
        locations."/_synapse".proxyPass = "https://localhost:8448";
      };		
       };

    security.acme.acceptTerms = true;
    security.acme.certs = {
    "kyb3r.space".email = "post@kyb3r.space";
    "www.kyb3r.space".email = "post@kyb3r.space";
    "m.kyb3r.space".email = "post@kyb3r.space";
    };

Hier starten wir NGINX (wie genau der init abläuft, hatte ich HIER schon mal beschrieben). Wir lassen NGINX drei Instanzen hosten:

- kyb3r.space
- www.kyb3r.space
- m.kyb3r.space

Die ersten beiden sind als reines Webinterface gedacht (irgendwas bau ich da bestimt noch hin) und die dritte ist die Schnittstelle zu Synapse. Die Konfiguration der ersten beiden ist eine Standart-Konfiguration für eine Website. Bei der Synapse-Subdomain sieht die Konfigurationen etwas anders aus. Hier geben wir der Subdomain mittels locations. noch Pfade mit, die von Synapse benötigt werden, um zu funktionieren.
Im letzten Block der noch übrig ist, kommt der normale SSL Zertifikats Foo der notwendig ist um die SSL-Certs für die Domains auszuliefern. Was jetzt noch in der configuration.nix fehlt, sind die Firewall settings:

CONFIGURATION.NIX
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
allowedUDPPortRanges = [
  { from = 4000; to = 4007; }
  { from = 8000; to = 8010; }
];
};  

Hier machen wir die nötigen TCP und UDP Ports auf. Port 22 für SSH, 80 für den Webkram und 443 für Synapse. Damit steht mal das Grundgerüst - also der Webserver - und wir können uns Synapse zuwenden.

0x06 - SYNAPSE


Jetzt kommt der Punkt der mir diverse schlaflose Nächte und hirnschmelzende Stunden beschert hat. Am Ende war der Fehler aber, wie meistens unter NixOS, unfassbar einfach auszumachen.
Also zu Synapse. Synapse ist in den NixOS Packages mit der aktuellsten Version drin, es braucht also keine Hacks um das ganze zum laufen zu bringen. Den Anfang in der synapse.nix macht die Datenbank, denn ohne die läuft nix. In den meisten Tutorials (und von Matrix selber) wird PostgresQL empfohlen, da es deutlich performanter ist als reines SQL oder andere. Dann hören wir mal auf Matrix und nehmen PostgresQL:

SYNAPSE.NIX
    { pkgs, lib, config, ... }:
    {
      services.postgresql.enable = true;
      services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
        CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'password';
        CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
          TEMPLATE template0
          LC_COLLATE = "C"
          LC_CTYPE = "C";
      '';

Mit services.postgresql.enable = true; aktivieren wir das Packet PostgresQL und geben dem Paket mit services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" direkt die Commands mit, die wir brauchen, um eine Datenbank und einen User anzulegen. CREATE ROLE legt den User an und CREATE DATABASE legt die eigentliche Datenbank an und weißt mit OWNER auch gleich den Besitzer zu. Soweit noch ganz einfach. Aber nur einfach ist langweilig, deswegen kommt jetz tder SYNAPSE Part der Config.

SYNAPSE.NIX
services.matrix-synapse = {
  enable = true;
  settings.server_name = "m.kyb3r.space";
  settings.enable_metrics = true;
  settings.registration_shared_secret_path = "/path/to/secret";
  settings.database.name = "psycopg2";
  settings.database.args = {
      user = "matrix-synapse";
      password = "password";
  };
  settings.listeners = [
    {
      bind_addresses = [ "localhost" ];
port = 8448;
tls = false;
     resources = [
        { compress = true; names = ["client" "federation"]; }
  { compress = false; names = [ "federation" ]; }
      ];
type = "http";
x_forwarded = false;
    }
    {
bind_addresses = [ "127.0.0.1" ];
port = 8008;
resources = [ { compress = true; names = [ "client" "federation" ]; }
];
tls = false;
type = "http";
x_forwarded = true;
    }

  ];
};
}

Diese Stück Konfiguration hat mich Zeit gekostet. Die meisten Tutorials die im Internet zu finden sind, sind mittlerweile out-dated von 2020 oder noch älter. In NixOS hat sich seit dem aber einiges getan und auch die Konventioneni, Synapse Konfigurationen mitzugeben hat sich grundlegend geändert. So zumindest bei Synapse. Der Vorteil liegt deutlich auf der Hand. Vor der Änderung wurde das registration_shared_secret in Klartext (!!!) in der Config definiert. Das will eigentlich niemand wirkllich haben. Für mich konnte ich zwei Varianten (ja - es gibt noch andere Möglichkeiten Keys zu deployen) den Key zur Verfügung zu stellen, ausfindig machen. Zum einen unterstützt services.matrix-synapse den Parameter settings.registration_shared_secret_path = "/path/to/secret";. Dadurch kann der Key in ein separates File geschrieben werden und Synapse wird mit der Nase auf die Key-File gestoßen.
Das File ist recht simple zu erstellen:

TERMINAL
  nix-shell -p pwgen
  pwgen -s 64 1 > /path/to/file/matrix-shared-secret
  

So habt ihr pwgen nicht im System rumhängen. Ok - was macht die Config jetzt genau.
Wir starten den Service services.matrix-synapse mittels enable = true; und definieren danach weitere Parameter. Angefangen bei settings.server_name = "m.kyb3r.space"; welches unseren Servernamen definiert und settings.enable_metrics = true; schreibt Metriken mit, die sehr hilfreich sein können beim Debuggen. Später kann das Feature deaktiviert werden - wir wollen ja datensparsam sein ;)
Wie weiter oben schon beschrieben verweisst settings.registration_shared_secret_path auf den Pfad zum shared_secret.

Als nächstes auen wir die Verbindung zur Datenbank auf. Der Parameter settings.database.name = "psycopg2"; teilt Synapse mit, dass hier eine PostgresQL im Hintergrund läuft. Mit settings.database.args = { user = "matrix-synapse"; password = "password"; }; geben wir natürlich auch noch die Login-Credentials mit. Damit sollte der connect zur Datenbank stehen.
Der Rest der Config erstellt Listener die die Ports 8448 und 8008 and die localhosts binden und sich so um die Verbindung zu Client und die Förderation kümmern.
That's it. Wenn ihr alles so konfiguriert könnt ihr per Rebuild das Ganze zum Laufen bringen:

TERMINAL
	nixos-rebuild switch
	

Wenn alles geklappt hat, solltet ihr jetzt einen funktionalen Synapse Homeserver vor euch haben. Wenn ihr euch jetzt einen Matrix Client eurer Wahl schnappt und euch einen Account auf eurer Instanz erstellen wollt, werdet ihr merken, dass der Server den wir hier konfiguriert haben, nicht dazu gedacht ist, dass sich jede*r einen Account erstellen kann. Es gibt zwar die Möglichkeit öffentliche Registrierung zu aktivieren, ich will das persönlich aber nicht auf meinem Server haben. Ihr könnt aber per Terminal ganz einfach einen User registrieren.

0x07 - USER REGISTRIEREN


Einen laufenden Synapse-Server zu haben, birgt schon ein gewisses Maß an Zufriedenheit, aber natürlich macht das ganze auch nur wirklich Sinn, wenn wir da einen Account haben. Da das öffentliche Registrieren deaktiviert ist, erledigen wir das ganze per Terminal. Dazu bringt Synapse das Tool "register_new_matrix_user" mit. Es gibt zwei Möglichkeiten dieses Tool zu nutzen. Nr. #1 ihr sucht unter /nix/store/ den richtigen Eintrag, oder - und das geht deutlich schneller und unkomplizierter - ihr bedient das ganze per nix-shell -p matrix-synapse:

NIX-SHELL -P MATRIX-SYNAPSE
	register_new_matrix_user -c /PFAD/ZUR/CONFIG/ http://localhost:8008
	

Das registriert einen neuen User (inkl. Passwort und Adminrechten bei Bedarf) auf eurem Synapse-Server. Das wichtige hier ist das der /PFAD/ZUR/CONFIG/ natürlich nicht der Pfad zu eurer NixOS Configuration, sondern den Pfad zur Synapse-Configfile. An den Pfad zur Config kommt ihr ran, wenn ihr euch matrix-synapse.service anschaut. Darin ist der komplette Pfad definiert.

Wenn die Registrierung ohne Fehler durchläuft - sind wir fertig und können den eigenen Matrix-Synapse-Homeserver nutzen.

0x08 - FAZIT


Alles in allem ist die Konfiguration von Matrix-Synapse relativ schnell und ohne viel Aufwand erledigt. Leider sind die Tutorials im Netz alle recht veraltet. Nur der Eintrag im NixOS Manual ist aktuell und hilft weiter. Mit meiner Config habt ihr alles was ihr braucht um euren eigenen Server zu hosten und privat für euch zu betreiben. Sollte ich mich doch mal dafür entscheiden, die Registrierung öffentlich zu machen, werde ich euch natürlich auf dem laufenden halten, ansonsten könnt ihr euch die Config-Paramter aus dem Netz ziehen.

0x09 - DANKESCHÖN


Wie ich hier jetzt schon mehrfach erwähnt habe, war es etwas Arbeit das ganze zum laufen zu bringen. Ganz alleine hab ich das (auch wenn ich das gern gwollt hätte) nicht geschafft. Diverse nette Menschen aus dem Internetz haben mir wertvolle Tips gegeben, die es dann doch noch möglich gemacht haben, meinen eigenen Synapse-Server zu bauen. Deswegen hier ein riesiges Dankeschön an:

- 0x4AxF (Federation fails)
- pinpox (Config Samples und hint zu Dendrite - was ich mir auch noch anschauen werde)
- fleaz (für den Hinweis, dass LetsEncrypt ein Timeout bei der Cert-Vergabe hat (5 Versuche / Stunde))
- spohie (für den Vorschlag "workers" in Verbindung mit Synapse zu verwenden, auch das schau ich mir zeitnah an)

Dankeschön :)

EDIT
[20230817] - Conduit kann Föderation [fleaz]

[~] BACK