diff options
Diffstat (limited to 'modules/services/matrix-homeserver.nix')
-rw-r--r-- | modules/services/matrix-homeserver.nix | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/modules/services/matrix-homeserver.nix b/modules/services/matrix-homeserver.nix new file mode 100644 index 0000000..6af8f7f --- /dev/null +++ b/modules/services/matrix-homeserver.nix @@ -0,0 +1,231 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.modules.services.matrix-homeserver; + + database = { + connection_string = "postgres:///dendrite?host=/run/postgresql"; + max_open_conns = 100; + max_idle_conns = 5; + conn_max_lifetime = -1; + }; +in +{ + imports = [ + ../../overlays/sliding-sync-module.nix + ]; + + options.modules.services.matrix-homeserver = { + enable = mkEnableOption "matrix homeserver instance"; + domain = mkOption { type = types.str; }; + realHost = mkOption { type = types.str; default = "matrix.${cfg.domain}"; }; + slidingSyncHost = mkOption { type = types.str; default = "slidingsync.${cfg.domain}"; }; + turn = { + enable = mkEnableOption "VOIP using TURN"; + domain = mkOption { type = types.str; default = "turn.${cfg.domain}"; }; + shared_secret = mkOption { type = types.str; }; + }; + secrets = { + matrix-server-key = mkOption { type = types.str; description = "path to the server key"; }; + dendrite-envs = mkOption { type = types.nullOr types.str; description = "path for the environment file to source"; }; + sliding-sync-secret = mkOption { type = types.nullOr types.str; description = "path to the sliding sync secret"; }; + }; + }; + + config = mkIf cfg.enable { + # Adapted from Mic92/dotfiles, (C) 2021 Jörg Thalheim (MIT) + services.dendrite = { + enable = true; + settings = { + global = { + server_name = cfg.domain; + # `private_key` has the type `path` + # prefix a `/` to make `path` happy + private_key = "/$CREDENTIALS_DIRECTORY/matrix-server-key"; + jetstream.storage_path = "/var/lib/dendrite/jetstream"; + trusted_third_party_id_servers = [ + "matrix.org" + "vector.im" + ]; + metrics.enabled = true; + }; + logging = [ + { + type = "std"; + level = "info"; # "warn" on public release + } + ]; + app_service_api = { + inherit database; + config_files = [ ]; + }; + client_api = { + registration_disabled = true; + rate_limiting.enabled = false; + rate_limiting.exempt_user_ids = [ + "@abuse:${cfg.domain}" + ]; + # registration_shared_secret = ""; # Initially set this option to configure the admin user. + } // optionalAttrs cfg.turn.enable { + turn = { + turn_user_lifetime = "24h"; + turn_uris = [ + "turns:${cfg.turn.domain}?transport=udp" + "turns:${cfg.turn.domain}?transport=tcp" + "turn:${cfg.turn.domain}?transport=udp" + "turn:${cfg.turn.domain}?transport=tcp" + ]; + turn_shared_secret = cfg.turn.shared_secret; + }; + }; + media_api = { + inherit database; + dynamic_thumbnails = true; + }; + room_server = { + inherit database; + }; + push_server = { + inherit database; + }; + mscs = { + inherit database; + mscs = [ "msc2836" "msc2946" ]; + }; + sync_api = { + inherit database; + real_ip_header = "X-Real-IP"; + # The NixOS option is 'enable', which doesn't exist in Dendrite. + search.enabled = true; + }; + key_server = { + inherit database; + }; + federation_api = { + inherit database; + key_perspectives = [ + { + server_name = "matrix.org"; + keys = [ + { + key_id = "ed25519:auto"; + public_key = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; + } + { + key_id = "ed25519:a_RXGa"; + public_key = "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ"; + } + ]; + } + ]; + prefer_direct_fetch = false; + }; + user_api = { + account_database = database; + device_database = database; + }; + }; + loadCredential = [ "matrix-server-key:${cfg.secrets.matrix-server-key}" ]; + } // optionalAttrs (cfg.secrets.dendrite-envs != null) { + environmentFile = cfg.secrets.dendrite-envs; + }; + + services.prometheus.scrapeConfigs = [ + { + job_name = "dendrite"; + static_configs = [{ + targets = [ "127.0.0.1:${toString config.services.dendrite.httpPort}" ]; + }]; + } + ]; + + systemd.services.dendrite = { + after = [ "postgresql.service" ]; + }; + + environment.persistence."/persist".directories = [ + "/var/lib/private/dendrite" + ]; + + services.sliding-sync = { + enable = true; + server = "https://${cfg.realHost}"; + bindAddr = "[::1]:8009"; + db = "postgres:///syncv3?host=/run/postgresql"; + secret = cfg.secrets.sliding-sync-secret; + after = [ "dendrite.service" ]; + }; + + services.postgresql.enable = true; + services.postgresql.ensureDatabases = [ "dendrite" "syncv3" ]; + services.postgresql.ensureUsers = [ + { + name = "dendrite"; + ensurePermissions."DATABASE dendrite" = "ALL PRIVILEGES"; + } + { + name = "sliding-sync"; + ensurePermissions."DATABASE syncv3" = "ALL PRIVILEGES"; + } + ]; + + services.nginx.virtualHosts.${cfg.realHost} = { + forceSSL = true; + useACMEHost = cfg.domain; + listen = [ + { addr = "0.0.0.0"; port = 443; ssl = true; } + { addr = "[::]"; port = 443; ssl = true; } + { addr = "0.0.0.0"; port = 8448; ssl = true; } + { addr = "[::]"; port = 8448; ssl = true; } + + ]; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 600; + client_max_body_size 50M; + ''; + locations."/_matrix".proxyPass = "http://[::1]:${toString config.services.dendrite.httpPort}"; + locations."/_dendrite".proxyPass = "http://[::1]:${toString config.services.dendrite.httpPort}"; + locations."/_synapse".proxyPass = "http://[::1]:${toString config.services.dendrite.httpPort}"; + }; + + services.nginx.virtualHosts.${cfg.domain} = + let + server-hello = { "m.server" = "${cfg.realHost}:443"; }; + client-hello = { + "m.homeserver"."base_url" = "https://${cfg.realHost}"; + "m.identity_server"."base_url" = "https://vector.im"; + "org.matrix.msc3575.proxy"."url" = "https://${cfg.slidingSyncHost}"; + }; + in + { + forceSSL = true; + useACMEHost = cfg.domain; + locations = { + "/.well-known/matrix/server" = { + extraConfig = '' + add_header Content-Type application/json; + return 200 '${builtins.toJSON server-hello}'; + ''; + }; + "/.well-known/matrix/client" = { + extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON client-hello}'; + ''; + }; + }; + }; + + services.nginx.virtualHosts.${cfg.slidingSyncHost} = { + forceSSL = true; + useACMEHost = cfg.domain; + locations."/".proxyPass = "http://${config.services.sliding-sync.bindAddr}"; + }; + + networking.firewall.allowedTCPPorts = [ 443 8448 ]; + }; +} |