about summary refs log tree commit diff
path: root/modules/matrix
diff options
context:
space:
mode:
authorAzat Bahawi <azat@bahawi.net>2024-04-21 02:15:42 +0300
committerAzat Bahawi <azat@bahawi.net>2024-04-21 02:15:42 +0300
commite6ed60548397627bf10f561f9438201dbba0a36e (patch)
treef9a84c5957d2cc4fcd148065ee9365a0c851ae1c /modules/matrix
parent2024-04-18 (diff)
2024-04-21
Diffstat (limited to 'modules/matrix')
-rw-r--r--modules/matrix/default.nix1
-rw-r--r--modules/matrix/dendrite.nix239
-rw-r--r--modules/matrix/element.nix62
3 files changed, 302 insertions, 0 deletions
diff --git a/modules/matrix/default.nix b/modules/matrix/default.nix
new file mode 100644
index 0000000..ef9fb18
--- /dev/null
+++ b/modules/matrix/default.nix
@@ -0,0 +1 @@
+{ lib, ... }: with lib; { imports = attrValues (modulesIn ./.); }
diff --git a/modules/matrix/dendrite.nix b/modules/matrix/dendrite.nix
new file mode 100644
index 0000000..dafef00
--- /dev/null
+++ b/modules/matrix/dendrite.nix
@@ -0,0 +1,239 @@
+{
+  config,
+  lib,
+  inputs,
+  pkgs,
+  ...
+}:
+with lib;
+let
+  cfg = config.nixfiles.modules.matrix.dendrite;
+in
+{
+  options.nixfiles.modules.matrix.dendrite = {
+    enable = mkEnableOption "Dendrite Matrix server";
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 8008;
+    };
+
+    domain = mkOption {
+      type = types.str;
+      default = config.networking.domain;
+      description = "Domain name sans protocol scheme.";
+    };
+  };
+
+  config =
+    let
+      db = "dendrite";
+    in
+    mkIf cfg.enable {
+      ark.directories = [
+        "/var/lib/dendrite"
+        "/var/lib/private/dendrite"
+      ];
+
+      # FIXME Use systemd secrets/environment for this.
+      secrets.dendrite-private-key = {
+        file = "${inputs.self}/secrets/dendrite-private-key";
+        mode = "0444";
+      };
+      secrets.dendrite-environment-file = {
+        file = "${inputs.self}/secrets/dendrite-environment-file";
+        mode = "0444";
+      };
+
+      nixfiles.modules = {
+        nginx = {
+          enable = true;
+          upstreams.dendrite.servers."127.0.0.1:${toString config.services.dendrite.httpPort}" = { };
+          virtualHosts.${cfg.domain}.locations = {
+            "/_matrix".proxyPass = "http://dendrite";
+            "= /.well-known/matrix/server" = {
+              extraConfig = ''
+                add_header Content-Type application/json;
+                add_header Access-Control-Allow-Origin *;
+              '';
+              return = "200 '${generators.toJSON { } { "m.server" = "${cfg.domain}:443"; }}'";
+            };
+            "= /.well-known/matrix/client" = {
+              extraConfig = ''
+                add_header Content-Type application/json;
+                add_header Access-Control-Allow-Origin *;
+              '';
+              return = "200 '${generators.toJSON { } { "m.homeserver".base_url = "https://${cfg.domain}"; }}'";
+            };
+          };
+        };
+
+        postgresql = {
+          enable = true;
+          extraPostStart = [
+            ''
+              $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"'
+            ''
+          ];
+        };
+
+        promtail.filters = [
+          {
+            match = {
+              selector = ''{syslog_identifier="dendrite"} |~ ".*Failed to fetch key for server.*"'';
+              action = "drop";
+            };
+          }
+          {
+            match = {
+              selector = ''{syslog_identifier="dendrite"} |~ ".*could not download key for.*"'';
+              action = "drop";
+            };
+          }
+        ];
+      };
+
+      services.postgresql = {
+        ensureDatabases = [ db ];
+        ensureUsers = [
+          {
+            name = db;
+            ensureDBOwnership = true;
+          }
+        ];
+      };
+
+      systemd.services.dendrite = {
+        description = "Dendrite Matrix homeserver";
+        wantedBy = [ "multi-user.target" ];
+        requires = [
+          "network.target"
+          "postgresql.service"
+        ];
+        after = [
+          "network.target"
+          "postgresql.service"
+        ];
+        serviceConfig =
+          let
+            needsPrivileges = cfg.port < 1024;
+            capabilities = [ "" ] ++ optionals needsPrivileges [ "CAP_NET_BIND_SERVICE" ];
+          in
+          {
+            Restart = "on-failure";
+            ExecStartPre =
+              let
+                settings = {
+                  version = 2;
+                  global = {
+                    server_name = cfg.domain;
+                    private_key = config.secrets.dendrite-private-key.path;
+                    database = {
+                      connection_string = "postgresql://${db}@/${db}?host=/run/postgresql";
+                      max_open_conns = 64;
+                      max_idle_connections = 8;
+                    };
+                    cache = {
+                      max_size_estimated = "1gb";
+                      max_age = "1h";
+                    };
+                    trusted_third_party_id_servers = [
+                      "matrix.org"
+                      "nixos.org"
+                      "vector.im"
+                    ];
+                    presence = {
+                      enable_inbound = false;
+                      enable_outbound = false;
+                    };
+                  };
+                  client_api = {
+                    registration_disabled = true;
+                    guests_disabled = true;
+                    registration_shared_secret = "$REGISTRATION_SHARED_SECRET";
+                  };
+                  media_api = {
+                    base_path = "/var/lib/dendrite/media_store";
+                    max_file_size_bytes = 0;
+                    dynamic_thumbnails = true;
+                    max_thumbnail_generators = 8;
+                    thumbnail_sizes = [
+                      {
+                        width = 32;
+                        height = 32;
+                        method = "crop";
+                      }
+                      {
+                        width = 96;
+                        height = 96;
+                        method = "crop";
+                      }
+                      {
+                        width = 640;
+                        height = 480;
+                        method = "scale";
+                      }
+                    ];
+                  };
+                  logging = [
+                    {
+                      type = "std";
+                      level = "info";
+                    }
+                  ];
+                };
+              in
+              concatStringsSep " " [
+                (getExe pkgs.envsubst)
+                "-i ${(pkgs.formats.yaml { }).generate "dendrite.yaml" settings}"
+                "-o /run/dendrite/dendrite.yaml"
+              ];
+            ExecStart = concatStringsSep " " [
+              (getExe' pkgs.dendrite "dendrite")
+              "--config /run/dendrite/dendrite.yaml"
+              "--http-bind-address 127.0.0.1:${toString cfg.port}"
+            ];
+            ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+            EnvironmentFile = config.secrets.dendrite-environment-file.path;
+            DynamicUser = true;
+            StateDirectory = "dendrite";
+            RuntimeDirectory = "dendrite";
+            RuntimeDirectoryMode = "0700";
+            AmbientCapabilities = capabilities;
+            CapabilityBoundingSet = capabilities;
+            UMask = "0077";
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            NoNewPrivileges = true;
+            PrivateDevices = true;
+            PrivateTmp = true;
+            PrivateUsers = !needsPrivileges;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHome = true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            ProtectSystem = "strict";
+            ProtectProc = "noaccess";
+            ProcSubset = "pid";
+            RemoveIPC = true;
+            RestrictAddressFamilies = [
+              "AF_UNIX"
+              "AF_INET"
+              "AF_INET6"
+            ];
+            RestrictNamespaces = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            SystemCallArchitectures = "native";
+            SystemCallFilter = [
+              "@system-service"
+              "~@privileged"
+            ];
+          };
+      };
+    };
+}
diff --git a/modules/matrix/element.nix b/modules/matrix/element.nix
new file mode 100644
index 0000000..01b991e
--- /dev/null
+++ b/modules/matrix/element.nix
@@ -0,0 +1,62 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib;
+let
+  cfg = config.nixfiles.modules.matrix.element;
+in
+{
+  options.nixfiles.modules.matrix.element = {
+    enable = mkEnableOption "Element, a Matrix web interface";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; nullOr str;
+      default = "element.${config.networking.domain}";
+    };
+
+    homeserver = mkOption {
+      description = "Default Matrix homeserver.";
+      type = with types; str;
+      default = my.domain.azahi;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion =
+          with config.nixfiles.modules.matrix;
+          (synapse.enable || dendrite.enable) && !(!synapse.enable && !dendrite.enable);
+        message = "Synapse or Dendrite must be enabled";
+      }
+    ];
+
+    nixfiles.modules.nginx = with cfg; {
+      enable = true;
+      virtualHosts.${domain}.locations."/".root = pkgs.element-web.override {
+        conf = {
+          default_server_config."m.homeserver" = {
+            base_url = "https://${homeserver}";
+            server_name = homeserver;
+          };
+          disable_custom_urls = true;
+          disable_guests = true;
+          disable_login_language_selector = true;
+          disable_3pid_login = true;
+          brand = homeserver;
+          branding.authFooterLinks = [
+            {
+              text = "NixOS";
+              url = "https://nixos.org";
+            }
+          ];
+          default_theme = "dark";
+        };
+      };
+    };
+  };
+}

Consider giving Nix/NixOS a try! <3