diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index d3efc1789cdc..bbe59b002003 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -246,6 +246,9 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - `services.postgresql.extraPlugins` changed its type from just a list of packages to also a function that returns such a list. For example a config line like ``services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ postgis ];`` is recommended to be changed to ``services.postgresql.extraPlugins = ps: with ps; [ postgis ];``; +- The Matrix homeserver [Synapse](https://element-hq.github.io/synapse/) module now supports configuring UNIX domain socket [listeners](#opt-services.matrix-synapse.settings.listeners) through the `path` option. + The default replication worker on the main instance has been migrated away from TCP sockets to UNIX domain sockets. + - Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles. The `nimPackages` and `nim2Packages` sets have been removed. See https://nixos.org/manual/nixpkgs/unstable#nim for more information. diff --git a/nixos/modules/services/matrix/synapse.md b/nixos/modules/services/matrix/synapse.md index f270be8c8d78..9c9c025fc5f5 100644 --- a/nixos/modules/services/matrix/synapse.md +++ b/nixos/modules/services/matrix/synapse.md @@ -126,8 +126,9 @@ then enable `services.matrix-synapse.settings.enable_registration = true;`. Otherwise, or you can generate a registration secret with {command}`pwgen -s 64 1` and set it with [](#opt-services.matrix-synapse.settings.registration_shared_secret). -To create a new user or admin, run the following after you have set the secret -and have rebuilt NixOS: +To create a new user or admin from the terminal your client listener +must be configured to use TCP sockets. Then you can run the following +after you have set the secret and have rebuilt NixOS: ```ShellSession $ nix-shell -p matrix-synapse $ register_new_matrix_user -k your-registration-shared-secret http://localhost:8008 diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix index 4c1c396eac05..e3f9c7742cc7 100644 --- a/nixos/modules/services/matrix/synapse.nix +++ b/nixos/modules/services/matrix/synapse.nix @@ -6,8 +6,16 @@ let cfg = config.services.matrix-synapse; format = pkgs.formats.yaml { }; + filterRecursiveNull = o: + if isAttrs o then + mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o) + else if isList o then + map filterRecursiveNull (filter (v: v != null) o) + else + o; + # remove null values from the final configuration - finalSettings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings; + finalSettings = filterRecursiveNull cfg.settings; configFile = format.generate "homeserver.yaml" finalSettings; usePostgresql = cfg.settings.database.name == "psycopg2"; @@ -105,6 +113,19 @@ let SYSLOG_IDENTIFIER = logName; }; }); + + toIntBase8 = str: + lib.pipe str [ + lib.stringToCharacters + (map lib.toInt) + (lib.foldl (acc: digit: acc * 8 + digit) 0) + ]; + + toDecimalFilePermission = value: + if value == null then + null + else + toIntBase8 value; in { imports = [ @@ -192,10 +213,11 @@ in { ]; options = let - listenerType = workerContext: types.submodule { + listenerType = workerContext: types.submodule ({ config, ... }: { options = { port = mkOption { - type = types.port; + type = types.nullOr types.port; + default = null; example = 8448; description = lib.mdDoc '' The port to listen for HTTP(S) requests on. @@ -203,11 +225,20 @@ in { }; bind_addresses = mkOption { - type = types.listOf types.str; - default = [ + type = types.nullOr (types.listOf types.str); + default = if config.path != null then null else [ "::1" "127.0.0.1" ]; + defaultText = literalExpression '' + if path != null then + null + else + [ + "::1" + "127.0.0.1" + ] + ''; example = literalExpression '' [ "::" @@ -219,6 +250,35 @@ in { ''; }; + path = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Unix domain socket path to bind this listener to. + + ::: {.note} + This option is incompatible with {option}`bind_addresses`, {option}`port`, {option}`tls` + and also does not support the `metrics` and `manhole` listener {option}`type`. + ::: + ''; + }; + + mode = mkOption { + type = types.nullOr (types.strMatching "^[0,2-7]{3,4}$"); + default = if config.path != null then "660" else null; + defaultText = literalExpression '' + if path != null then + "660" + else + null + ''; + example = "660"; + description = '' + File permissions on the UNIX domain socket. + ''; + apply = toDecimalFilePermission; + }; + type = mkOption { type = types.enum [ "http" @@ -234,17 +294,30 @@ in { }; tls = mkOption { - type = types.bool; - default = !workerContext; + type = types.nullOr types.bool; + default = if config.path != null then + null + else + !workerContext; + defaultText = '' + Enabled for the main instance listener, unless it is configured with a UNIX domain socket path. + ''; example = false; description = lib.mdDoc '' Whether to enable TLS on the listener socket. + + ::: {.note} + This option will be ignored for UNIX domain sockets. + ::: ''; }; x_forwarded = mkOption { type = types.bool; - default = false; + default = config.path != null; + defaultText = '' + Enabled if the listener is configured with a UNIX domain socket path + ''; example = true; description = lib.mdDoc '' Use the X-Forwarded-For (XFF) header as the client IP and not the @@ -291,11 +364,28 @@ in { ''; }; }; - }; + }); in { services.matrix-synapse = { enable = mkEnableOption (lib.mdDoc "matrix.org synapse"); + enableRegistrationScript = mkOption { + type = types.bool; + default = clientListener.bind_addresses != []; + example = false; + defaultText = '' + Enabled if the client listener uses TCP sockets + ''; + description = '' + Whether to install the `register_new_matrix_user` script, that + allows account creation on the terminal. + + ::: {.note} + This script does not work when the client listener uses UNIX domain sockets + ::: + ''; + }; + serviceUnit = lib.mkOption { type = lib.types.str; readOnly = true; @@ -616,11 +706,8 @@ in { compress = false; }]; }] ++ lib.optional hasWorkers { - port = 9093; - bind_addresses = [ "127.0.0.1" ]; + path = "/run/matrix-synapse/main_replication.sock"; type = "http"; - tls = false; - x_forwarded = false; resources = [{ names = [ "replication" ]; compress = false; @@ -630,7 +717,7 @@ in { List of ports that Synapse should listen on, their purpose and their configuration. By default, synapse will be configured for client and federation traffic on port 8008, and - for worker replication traffic on port 9093. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers) + use a UNIX domain socket for worker replication. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers) for more details. ''; }; @@ -1006,9 +1093,15 @@ in { listener = lib.findFirst ( listener: - listener.port == main.port + ( + lib.hasAttr "port" main && listener.port or null == main.port + || lib.hasAttr "path" main && listener.path or null == main.path + ) && listenerSupportsResource "replication" listener - && (lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses) + && ( + lib.hasAttr "host" main && lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses + || lib.hasAttr "path" main + ) ) null cfg.settings.listeners; @@ -1022,15 +1115,44 @@ in { This is done by default unless you manually configure either of those settings. ''; } - ]; + { + assertion = cfg.enableRegistrationScript -> clientListener.path == null; + message = '' + The client listener on matrix-synapse is configured to use UNIX domain sockets. + This configuration is incompatible with the `register_new_matrix_user` script. + + Disable `services.mastrix-synapse.enableRegistrationScript` to continue. + ''; + } + ] + ++ (map (listener: { + assertion = (listener.path == null) != (listener.bind_addresses == null); + message = '' + Listeners require either a UNIX domain socket `path` or `bind_addresses` for a TCP socket. + ''; + }) cfg.settings.listeners) + ++ (map (listener: { + assertion = listener.path != null -> (listener.bind_addresses == null && listener.port == null && listener.tls == null); + message = let + formatKeyValue = key: value: lib.optionalString (value != null) " - ${key}=${toString value}\n"; + in '' + Listener configured with UNIX domain socket (${toString listener.path}) ignores the following options: + ${formatKeyValue "bind_addresses" listener.bind_addresses}${formatKeyValue "port" listener.port}${formatKeyValue "tls" listener.tls} + ''; + }) cfg.settings.listeners) + ++ (map (listener: { + assertion = listener.path == null || listener.type == "http"; + message = '' + Listener configured with UNIX domain socket (${toString listener.path}) only supports the "http" listener type. + ''; + }) cfg.settings.listeners); services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally { enabled = true; path = config.services.redis.servers.matrix-synapse.unixSocket; }; services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault { - host = "127.0.0.1"; - port = 9093; + path = "/run/matrix-synapse/main_replication.sock"; }); services.matrix-synapse.serviceUnit = if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service"; @@ -1086,6 +1208,8 @@ in { User = "matrix-synapse"; Group = "matrix-synapse"; WorkingDirectory = cfg.dataDir; + RuntimeDirectory = "matrix-synapse"; + RuntimeDirectoryPreserve = true; ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; Restart = "on-failure"; UMask = "0077"; @@ -1178,7 +1302,9 @@ in { user = "matrix-synapse"; }; - environment.systemPackages = [ registerNewMatrixUser ]; + environment.systemPackages = lib.optionals cfg.enableRegistrationScript [ + registerNewMatrixUser + ]; }; meta = {