nixos/matrix-synapse: add UNIX domain socket listener support

Exposes two options, `path` and `mode`, to configure the location and
permissions on the socket file.

The `mode` needs to be specified as string in octal and will be converted
into a decimal integer, so it correctly passes through the YAML parser
and arrives at the `os.chmod` call in the Twisted codebase. What a fun
detour.

Adds an assertion, that either `path` or `bind_addresses` and `port` are
configured on every listener.

Migrates the default replication listener of the main instance to a UNIX
domain socket, because it is more efficient.

Introduces the `enableRegistrationScript` option, to gracefully disable
the user registration script, when the client listener listens on a UNIX
domain socket, which is something the script does not support.
This commit is contained in:
Martin Weinelt 2024-02-03 23:19:58 +01:00
parent 2565ab7b53
commit 143d266f0d
No known key found for this signature in database
GPG Key ID: 87C1E9888F856759
3 changed files with 143 additions and 21 deletions

View File

@ -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. - `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 ];``; 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. - Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles.
The `nimPackages` and `nim2Packages` sets have been removed. The `nimPackages` and `nim2Packages` sets have been removed.
See https://nixos.org/manual/nixpkgs/unstable#nim for more information. See https://nixos.org/manual/nixpkgs/unstable#nim for more information.

View File

@ -126,8 +126,9 @@ then enable `services.matrix-synapse.settings.enable_registration = true;`.
Otherwise, or you can generate a registration secret with Otherwise, or you can generate a registration secret with
{command}`pwgen -s 64 1` and set it with {command}`pwgen -s 64 1` and set it with
[](#opt-services.matrix-synapse.settings.registration_shared_secret). [](#opt-services.matrix-synapse.settings.registration_shared_secret).
To create a new user or admin, run the following after you have set the secret To create a new user or admin from the terminal your client listener
and have rebuilt NixOS: must be configured to use TCP sockets. Then you can run the following
after you have set the secret and have rebuilt NixOS:
```ShellSession ```ShellSession
$ nix-shell -p matrix-synapse $ nix-shell -p matrix-synapse
$ register_new_matrix_user -k your-registration-shared-secret http://localhost:8008 $ register_new_matrix_user -k your-registration-shared-secret http://localhost:8008

View File

@ -105,6 +105,19 @@ let
SYSLOG_IDENTIFIER = logName; 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 { in {
imports = [ imports = [
@ -192,10 +205,11 @@ in {
]; ];
options = let options = let
listenerType = workerContext: types.submodule { listenerType = workerContext: types.submodule ({ config, ... }: {
options = { options = {
port = mkOption { port = mkOption {
type = types.port; type = types.nullOr types.port;
default = null;
example = 8448; example = 8448;
description = lib.mdDoc '' description = lib.mdDoc ''
The port to listen for HTTP(S) requests on. The port to listen for HTTP(S) requests on.
@ -203,11 +217,20 @@ in {
}; };
bind_addresses = mkOption { bind_addresses = mkOption {
type = types.listOf types.str; type = types.nullOr (types.listOf types.str);
default = [ default = if config.path != null then null else [
"::1" "::1"
"127.0.0.1" "127.0.0.1"
]; ];
defaultText = literalExpression ''
if path != null then
null
else
[
"::1"
"127.0.0.1"
]
'';
example = literalExpression '' example = literalExpression ''
[ [
"::" "::"
@ -219,6 +242,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 = mkOption {
type = types.enum [ type = types.enum [
"http" "http"
@ -234,17 +286,30 @@ in {
}; };
tls = mkOption { tls = mkOption {
type = types.bool; type = types.nullOr types.bool;
default = !workerContext; 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; example = false;
description = lib.mdDoc '' description = lib.mdDoc ''
Whether to enable TLS on the listener socket. Whether to enable TLS on the listener socket.
::: {.note}
This option will be ignored for UNIX domain sockets.
:::
''; '';
}; };
x_forwarded = mkOption { x_forwarded = mkOption {
type = types.bool; type = types.bool;
default = false; default = config.path != null;
defaultText = ''
Enabled if the listener is configured with a UNIX domain socket path
'';
example = true; example = true;
description = lib.mdDoc '' description = lib.mdDoc ''
Use the X-Forwarded-For (XFF) header as the client IP and not the Use the X-Forwarded-For (XFF) header as the client IP and not the
@ -291,11 +356,28 @@ in {
''; '';
}; };
}; };
}; });
in { in {
services.matrix-synapse = { services.matrix-synapse = {
enable = mkEnableOption (lib.mdDoc "matrix.org 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 { serviceUnit = lib.mkOption {
type = lib.types.str; type = lib.types.str;
readOnly = true; readOnly = true;
@ -616,11 +698,8 @@ in {
compress = false; compress = false;
}]; }];
}] ++ lib.optional hasWorkers { }] ++ lib.optional hasWorkers {
port = 9093; path = "/run/matrix-synapse/main_replication.sock";
bind_addresses = [ "127.0.0.1" ];
type = "http"; type = "http";
tls = false;
x_forwarded = false;
resources = [{ resources = [{
names = [ "replication" ]; names = [ "replication" ];
compress = false; compress = false;
@ -630,7 +709,7 @@ in {
List of ports that Synapse should listen on, their purpose and their configuration. 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 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. for more details.
''; '';
}; };
@ -1006,9 +1085,15 @@ in {
listener = lib.findFirst listener = lib.findFirst
( (
listener: 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 && 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 null
cfg.settings.listeners; cfg.settings.listeners;
@ -1022,15 +1107,44 @@ in {
This is done by default unless you manually configure either of those settings. 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 { services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally {
enabled = true; enabled = true;
path = config.services.redis.servers.matrix-synapse.unixSocket; path = config.services.redis.servers.matrix-synapse.unixSocket;
}; };
services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault { services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault {
host = "127.0.0.1"; path = "/run/matrix-synapse/main_replication.sock";
port = 9093;
}); });
services.matrix-synapse.serviceUnit = if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service"; services.matrix-synapse.serviceUnit = if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service";
@ -1086,6 +1200,8 @@ in {
User = "matrix-synapse"; User = "matrix-synapse";
Group = "matrix-synapse"; Group = "matrix-synapse";
WorkingDirectory = cfg.dataDir; WorkingDirectory = cfg.dataDir;
RuntimeDirectory = "matrix-synapse";
RuntimeDirectoryPreserve = true;
ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
Restart = "on-failure"; Restart = "on-failure";
UMask = "0077"; UMask = "0077";
@ -1178,7 +1294,9 @@ in {
user = "matrix-synapse"; user = "matrix-synapse";
}; };
environment.systemPackages = [ registerNewMatrixUser ]; environment.systemPackages = lib.optionals cfg.enableRegistrationScript [
registerNewMatrixUser
];
}; };
meta = { meta = {