Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/matrix-synapse: Add UNIX domain socket listener support #286172

Merged
merged 2 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nixos/doc/manual/release-notes/rl-2405.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions nixos/modules/services/matrix/synapse.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
166 changes: 146 additions & 20 deletions nixos/modules/services/matrix/synapse.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -105,6 +113,19 @@ let
SYSLOG_IDENTIFIER = logName;
};
});

toIntBase8 = str:
lib.pipe str [
mweinelt marked this conversation as resolved.
Show resolved Hide resolved
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 = [
Expand Down Expand Up @@ -192,22 +213,32 @@ 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.
'';
};

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 ''
[
"::"
Expand All @@ -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`.
mweinelt marked this conversation as resolved.
Show resolved Hide resolved
:::
'';
};

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"
Expand All @@ -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.
mweinelt marked this conversation as resolved.
Show resolved Hide resolved
:::
'';
};

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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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.
'';
};
Expand Down Expand Up @@ -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;
Expand All @@ -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";
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -1178,7 +1302,9 @@ in {
user = "matrix-synapse";
};

environment.systemPackages = [ registerNewMatrixUser ];
environment.systemPackages = lib.optionals cfg.enableRegistrationScript [
registerNewMatrixUser
];
};

meta = {
Expand Down
Loading