Merge pull request #1 from dali99/flake-experiments

This commit is contained in:
Daniel Lovbrotte Olsen 2022-12-22 00:04:31 +01:00 committed by GitHub
commit b6f0a026a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 639 additions and 121 deletions

34
README.MD Normal file
View File

@ -0,0 +1,34 @@
With matrix.YOURDOMAIN pointing at the server:
```
{
imports = [ ./synapse-module ];
services.matrix-synapse-next = {
enable = true;
workers.federationSenders = 1;
workers.federationReceivers = 1;
enableNginx = true;
settings = {
server_name = "YOURDOMAIN";
database = {
name = "psycopg2";
args = {
host = "localhost";
user = "synapse";
password = "synapse";
dbname = "synapse";
};
};
};
};
services.redis.servers."".enable = true;
}
```
is ~enough to get a functional matrix-server running one federation sender and one federation receiver

9
flake.nix Normal file
View File

@ -0,0 +1,9 @@
{
description = "NixOS modules for matrix related services";
outputs = { self }: {
nixosModules = {
synapse = import ./synapse-module;
};
};
}

View File

@ -2,13 +2,21 @@
let let
cfg = config.services.matrix-synapse-next; cfg = config.services.matrix-synapse-next;
wcfg = cfg.workers;
format = pkgs.formats.yaml {}; format = pkgs.formats.yaml {};
matrix-synapse-common-config = format.generate "matrix-synapse-common-config.yaml" cfg.settings; matrix-synapse-common-config = format.generate "matrix-synapse-common-config.yaml" cfg.settings;
pluginsEnv = cfg.package.python.buildEnv.override { pluginsEnv = cfg.package.python.buildEnv.override {
extraLibs = cfg.plugins; extraLibs = cfg.plugins;
}; };
genAttrs' = items: f: g: builtins.listToAttrs (builtins.map (i: lib.attrsets.nameValuePair (f i) (g i)) items);
isListenerType = type: listener: lib.lists.any (r: lib.lists.any (n: n == type) r.names) listener.resources;
in in
{ {
imports = [ ./nginx.nix ];
options.services.matrix-synapse-next = { options.services.matrix-synapse-next = {
enable = lib.mkEnableOption "matrix-synapse"; enable = lib.mkEnableOption "matrix-synapse";
@ -40,13 +48,12 @@ in
''; '';
}; };
enableMainSynapse = lib.mkOption { enableNginx = lib.mkEnableOption "Enable the synapse module managing nginx";
type = lib.types.bool;
default = true; public_baseurl = lib.mkOption {
description = '' type = lib.types.str;
Enable the main synapse process. default = "matrix.${cfg.settings.server_name}";
Useful if running workers on separate computers. description = "The domain where clients and such will connect (May be different from server_name if using delegation)";
'';
}; };
mainLogConfig = lib.mkOption { mainLogConfig = lib.mkOption {
@ -55,96 +62,218 @@ in
default = lib.readFile ./matrix-synapse-log_config.yaml; default = lib.readFile ./matrix-synapse-log_config.yaml;
}; };
workers = lib.mkOption { workers = let
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { isReplication = l: lib.lists.any (r: lib.lists.any (n: n == "replication") r.names) l.resources;
options.settings = lib.mkOption {
type = lib.types.submodule {
freeformType = format.type;
options.worker_app = lib.mkOption {
type = lib.types.enum [
"synapse.app.generic_worker"
"synapse.app.pusher"
"synapse.app.appservice"
"synapse.app.federation_sender"
"synapse.app.media_repository"
"synapse.app.user_dir"
"synapse.app.frontend_proxy"
];
description = "The type of worker application";
};
options.worker_replication_host = lib.mkOption {
type = lib.types.str;
description = "The replication listeners ip on the main synapse process";
default = "127.0.0.1";
};
options.worker_replication_http_port = lib.mkOption {
type = lib.types.port;
description = "The replication listeners port on the main synapse process";
};
options.worker_listeners = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options.type = lib.mkOption {
type = lib.types.enum [ "http" "metrics" ];
description = "The type of the listener";
default = "http";
};
options.port = lib.mkOption {
type = lib.types.port;
description = "the TCP port to bind to";
};
options.bind_addresses = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "A list of local addresses to listen on";
};
options.tls = lib.mkOption {
type = lib.types.bool;
description = "set to true to enable TLS for this listener. Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path.";
default = true;
};
options.x_forwarded = lib.mkOption {
type = lib.types.bool;
description = ''
Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP.
Useful when Synapse is behind a reverse-proxy.
'';
default = false;
};
options.resources = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options.names = lib.mkOption {
type = lib.types.listOf (lib.types.enum [ "client" "consent" "federation" "keys" "media" "metrics" "openid" "replication" "static" "webclient" ]);
description = "A list of resources to host on this port";
};
options.compress = lib.mkOption {
type = lib.types.bool;
description = "enable HTTP compression for this resource";
default = false;
};
});
};
});
description = "Listener configuration for the worker, similar to the main synapse listener";
default = [];
};
};
};
}));
default = {};
description = "Worker configuration";
example = {
"federation_sender1" = {
settings = {
worker_name = "federation_sender1";
worker_app = "synapse.app.federation_sender";
worker_replication_host = "127.0.0.1"; dMRL = lib.lists.findFirst isReplication
worker_replication_http_port = 9093; (throw "No replication listener configured!")
worker_listeners = [ ]; cfg.settings.listeners;
dMRH = lib.findFirst (x: true) (throw "Replication listener had no addresses")
dMRL.bind_addresses;
dMRP = dMRL.port;
in {
mainReplicationHost = lib.mkOption {
type = lib.types.str;
default = if builtins.elem dMRH [ "0.0.0.0" "::" ] then "127.0.0.1" else dMRH;
description = "Host of the main synapse instance's replication listener";
};
mainReplicationPort = lib.mkOption {
type = lib.types.port;
default = dMRP;
description = "Port for the main synapse instance's replication listener";
};
defaultListenerAddress = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "The default listener address for the worker";
};
workerStartingPort = lib.mkOption {
type = lib.types.port;
description = "What port should the automatically configured workers start enumerating from";
default = 8083;
};
enableMetrics = lib.mkOption {
type = lib.types.bool;
default = cfg.settings.enable_metrics;
};
metricsStartingPort = lib.mkOption {
type = lib.types.port;
default = 18083;
};
federationSenders = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured federation senders to set up";
default = 0;
};
federationReceivers = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured federation recievers to set up";
default = 0;
};
initialSyncers = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured intial syncers to set up";
default = 0;
};
normalSyncers = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured sync workers to set up";
default = 0;
};
eventPersisters = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured event-persisters to set up";
default = 0;
};
useUserDirectoryWorker = lib.mkEnableOption "user directory worker";
instances = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
options.isAuto = lib.mkOption {
type = lib.types.bool;
internal = true;
default = false;
}; };
options.index = lib.mkOption {
internal = true;
type = lib.types.ints.positive;
};
# The custom string type here is mainly for the name to use for the metrics of custom worker types
options.type = lib.mkOption {
type = lib.types.either (lib.types.str) (lib.types.enum [ "fed-sender" "fed-receiver" ]);
};
options.settings = let
instanceCfg = config;
inherit (instanceCfg) type isAuto;
in lib.mkOption {
type = lib.types.submodule ({config, ...}: {
freeformType = format.type;
options.worker_app = let
mapTypeApp = t: {
"fed-sender" = "synapse.app.generic_worker";
"fed-receiver" = "synapse.app.generic_worker";
"initial-sync" = "synapse.app.generic_worker";
"normal-sync" = "synapse.app.generic_worker";
"event-persist" = "synapse.app.generic_worker";
"user-dir" = "synapse.app.generic_worker";
}.${t};
defaultApp = if (!isAuto)
then "synapse.app.generic_worker"
else mapTypeApp type;
in lib.mkOption {
type = lib.types.enum [
"synapse.app.generic_worker"
"synapse.app.appservice"
"synapse.app.media_repository"
"synapse.app.user_dir"
];
description = "The type of worker application";
default = defaultApp;
};
options.worker_replication_host = lib.mkOption {
type = lib.types.str;
default = cfg.workers.mainReplicationHost;
description = "The replication listeners ip on the main synapse process";
};
options.worker_replication_http_port = lib.mkOption {
type = lib.types.port;
default = cfg.workers.mainReplicationPort;
description = "The replication listeners port on the main synapse process";
};
options.worker_listeners = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options.type = lib.mkOption {
type = lib.types.enum [ "http" "metrics" ];
description = "The type of the listener";
default = "http";
};
options.port = lib.mkOption {
type = lib.types.port;
description = "the TCP port to bind to";
};
options.bind_addresses = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "A list of local addresses to listen on";
default = [ cfg.workers.defaultListenerAddress ];
};
options.tls = lib.mkOption {
type = lib.types.bool;
description = "set to true to enable TLS for this listener. Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path.";
default = false;
};
options.x_forwarded = lib.mkOption {
type = lib.types.bool;
description = ''
Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP.
Useful when Synapse is behind a reverse-proxy.
'';
default = true;
};
options.resources = let
typeToResources = t: {
"fed-receiver" = [ "federation" ];
"fed-sender" = [ ];
"initial-sync" = [ "client" ];
"normal-sync" = [ "client" ];
"event-persist" = [ "replication" ];
"user-dir" = [ "client" ];
}.${t};
in lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options.names = lib.mkOption {
type = lib.types.listOf (lib.types.enum [ "client" "consent" "federation" "keys" "media" "metrics" "openid" "replication" "static" "webclient" ]);
description = "A list of resources to host on this port";
default = lib.optionals isAuto (typeToResources type);
};
options.compress = lib.mkOption {
type = lib.types.bool;
description = "enable HTTP compression for this resource";
default = false;
};
});
default = [{ }];
};
});
description = "Listener configuration for the worker, similar to the main synapse listener";
default = [ ];
};
});
default = { };
};
}));
default = { };
description = "Worker configuration";
example = {
"federation_sender1" = {
settings = {
worker_name = "federation_sender1";
worker_app = "synapse.app.generic_worker";
worker_replication_host = "127.0.0.1";
worker_replication_http_port = 9093;
worker_listeners = [ ];
};
}; };
}; };
}; };
};
settings = lib.mkOption { settings = lib.mkOption {
type = lib.types.submodule { type = lib.types.submodule {
@ -171,18 +300,11 @@ in
example = "matrix.org"; example = "matrix.org";
}; };
options.pid_file = lib.mkOption {
type = lib.types.path;
description = "When running as a daemon, the file to store the pid in";
default = "/run/matrix-synapse.pid";
};
options.use_presence = lib.mkOption { options.use_presence = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
description = "disable presence tracking on this homeserver, if you're having perfomance issues this can have a big impact"; description = "disable presence tracking, if you're having perfomance issues this can have a big impact";
default = true; default = true;
}; };
options.listeners = lib.mkOption { options.listeners = lib.mkOption {
type = lib.types.listOf (lib.types.submodule { type = lib.types.listOf (lib.types.submodule {
options.port = lib.mkOption { options.port = lib.mkOption {
@ -202,7 +324,7 @@ in
options.tls = lib.mkOption { options.tls = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
description = "set to true to enable TLS for this listener. Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path."; description = "set to true to enable TLS for this listener. Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path.";
default = true; default = false;
}; };
options.x_forwarded = lib.mkOption { options.x_forwarded = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
@ -210,7 +332,7 @@ in
Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP. Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP.
Useful when Synapse is behind a reverse-proxy. Useful when Synapse is behind a reverse-proxy.
''; '';
default = false; default = true;
}; };
options.resources = lib.mkOption { options.resources = lib.mkOption {
type = lib.types.listOf (lib.types.submodule { type = lib.types.listOf (lib.types.submodule {
@ -229,15 +351,30 @@ in
description = "List of ports that Synapse should listen on, their purpose and their configuration"; description = "List of ports that Synapse should listen on, their purpose and their configuration";
default = [ default = [
{ {
port = 8448; port = 8008;
bind_addresses = [ "0.0.0.0" "::" ]; bind_addresses = [ "127.0.0.1" ];
resources = [ resources = [
{ names = [ "client" ]; compress = true; } { names = [ "client" ]; compress = true; }
{ names = [ "federation" ]; compress = false; } { names = [ "federation" ]; compress = false; }
]; ];
} }
(lib.mkIf (wcfg.instances != { }) {
port = 9093;
bind_addresses = [ "127.0.0.1" ];
resources = [
{ names = [ "replication" ]; }
];
})
(lib.mkIf cfg.settings.enable_metrics {
port = 9000;
bind_addresses = [ "127.0.0.1" ];
resources = [
{ names = [ "metrics" ]; }
];
})
]; ];
}; };
options.federation_ip_range_blacklist = lib.mkOption { options.federation_ip_range_blacklist = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
description = '' description = ''
@ -280,7 +417,7 @@ in
options.enable_registration = lib.mkOption { options.enable_registration = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
description = "Enable registration for new users"; description = "Enable registration for new users";
default = true; default = false;
}; };
options.enable_metrics = lib.mkOption { options.enable_metrics = lib.mkOption {
@ -323,18 +460,7 @@ in
} }
]; ];
}; };
options.suppress_key_server_warning = lib.mkOption {
type = lib.types.bool;
description = "using matrix.org as a trusted key server will generate a warning if this is false";
default = false;
};
options.send_federation = lib.mkOption {
type = lib.types.bool;
description = "Disables sending of outbound federation transactions on the main process. Set to false if using federation senders";
default = (lib.lists.count (x: true) cfg.settings.federation_sender_instances) == 0;
};
options.federation_sender_instances = lib.mkOption { options.federation_sender_instances = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
description = '' description = ''
@ -343,7 +469,22 @@ in
started, to ensure that all instances are running with the same config (otherwise started, to ensure that all instances are running with the same config (otherwise
events may be dropped) events may be dropped)
''; '';
default = []; default = [ ];
};
options.redis = lib.mkOption {
type = lib.types.submodule {
freeformType = format.type;
options.enabled = lib.mkOption {
type = lib.types.bool;
description = "Enables using redis, required for worker support";
default = (lib.lists.count (x: true)
(lib.attrsets.attrValues cfg.workers.instances)) > 0;
};
};
default = { };
description = "configuration of redis for synapse and workers";
}; };
}; };
}; };
@ -381,7 +522,7 @@ in
}; };
}) })
(lib.mkIf cfg.enableMainSynapse { ({
systemd.services.matrix-synapse = { systemd.services.matrix-synapse = {
description = "Synapse Matrix homeserver"; description = "Synapse Matrix homeserver";
partOf = [ "matrix-synapse.target" ]; partOf = [ "matrix-synapse.target" ];
@ -409,9 +550,101 @@ in
}; };
}) })
(lib.mkMerge [
({
services.matrix-synapse-next.settings.federation_sender_instances = lib.genList (i: "auto-fed-sender${toString (i + 1)}") cfg.workers.federationSenders;
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.federationSenders)
(i: "auto-fed-sender${toString i}")
(i: {
isAuto = true; type = "fed-sender"; index = i;
settings.worker_listeners = lib.mkIf wcfg.enableMetrics [
{ port = cfg.workers.metricsStartingPort + i - 1;
resources = [ { names = [ "metrics" ]; } ];
}
];
});
})
({
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.federationReceivers)
(i: "auto-fed-receiver${toString i}")
(i: {
isAuto = true; type = "fed-receiver"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + i - 1; }]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + i;
resources = [ { names = [ "metrics" ]; } ];
};
});
})
({
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.initialSyncers)
(i: "auto-initial-sync${toString i}")
(i: {
isAuto = true; type = "initial-sync"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + i - 1; }]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + i;
resources = [ { names = [ "metrics" ]; } ];
};
});
})
({
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.normalSyncers)
(i: "auto-normal-sync${toString i}")
(i: {
isAuto = true; type = "normal-sync"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + cfg.workers.initialSyncers + i - 1; }]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + cfg.workers.initialSyncers + i;
resources = [ { names = [ "metrics" ]; } ];
};
});
})
({
services.matrix-synapse-next.settings.instance_map = genAttrs' (lib.lists.range 1 cfg.workers.eventPersisters)
(i: "auto-event-persist${toString i}")
(i: let
isReplication = l: lib.lists.any (r: lib.lists.any (n: n == "replication") r.names) l.resources;
wRL = lib.lists.findFirst isReplication
(throw "No replication listener configured!")
cfg.workers.instances."auto-event-persist${toString i}".settings.worker_listeners;
wRH = lib.findFirst (x: true) (throw "Replication listener had no addresses")
wRL.bind_addresses;
wRP = wRL.port;
in {
host = wRH;
port = wRP;
}
);
services.matrix-synapse-next.settings.stream_writers.events = lib.mkIf (cfg.workers.eventPersisters > 0) (lib.genList (i: "auto-event-persist${toString (i + 1)}") cfg.workers.eventPersisters);
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.eventPersisters)
(i: "auto-event-persist${toString i}")
(i: {
isAuto = true; type = "event-persist"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + i - 1;}]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + i;
resources = [ { names = [ "metrics" ]; } ];
};
});
})
(lib.mkIf cfg.workers.useUserDirectoryWorker {
services.matrix-synapse-next.workers.instances."auto-user-dir" = {
isAuto = true; type = "user-dir"; index = 1;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + cfg.workers.eventPersisters + 1 - 1;}]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + cfg.workers.eventPersisters + 1;
resources = [ { names = [ "metrics"]; } ];
};
};
services.matrix-synapse-next.settings.update_user_directory_from_worker = "auto-user-dir";
})
])
({ ({
systemd.services = let systemd.services = let
workerList = lib.mapAttrsToList (name: value: lib.nameValuePair name value ) cfg.workers; workerList = lib.mapAttrsToList (name: value: lib.nameValuePair name value ) cfg.workers.instances;
workerName = worker: worker.name; workerName = worker: worker.name;
workerSettings = worker: (worker.value.settings // {worker_name = (workerName worker);}); workerSettings = worker: (worker.value.settings // {worker_name = (workerName worker);});
workerConfig = worker: format.generate "matrix-synapse-worker-${workerName worker}-config.yaml" (workerSettings worker); workerConfig = worker: format.generate "matrix-synapse-worker-${workerName worker}-config.yaml" (workerSettings worker);
@ -423,6 +656,7 @@ in
partOf = [ "matrix-synapse.target" ]; partOf = [ "matrix-synapse.target" ];
wantedBy = [ "matrix-synapse.target" ]; wantedBy = [ "matrix-synapse.target" ];
after = [ "matrix-synapse.service" ]; after = [ "matrix-synapse.service" ];
requires = [ "matrix-synapse.service" ];
environment.PYTHONPATH = lib.makeSearchPathOutput "lib" cfg.package.python.sitePackages [ environment.PYTHONPATH = lib.makeSearchPathOutput "lib" cfg.package.python.sitePackages [
pluginsEnv pluginsEnv
]; ];
@ -431,6 +665,12 @@ in
User = "matrix-synapse"; User = "matrix-synapse";
Group = "matrix-synapse"; Group = "matrix-synapse";
WorkingDirectory = cfg.dataDir; WorkingDirectory = cfg.dataDir;
ExecStartPre = pkgs.writers.writeBash "wait-for-synapse" ''
# From https://md.darmstadt.ccc.de/synapse-at-work
while ! systemctl is-active -q matrix-synapse.service; do
sleep 1
done
'';
ExecStart = '' ExecStart = ''
${cfg.package}/bin/synapse_worker \ ${cfg.package}/bin/synapse_worker \
${ lib.concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ matrix-synapse-common-config (workerConfig worker) ] ++ cfg.extraConfigFiles) } ${ lib.concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ matrix-synapse-common-config (workerConfig worker) ] ++ cfg.extraConfigFiles) }
@ -440,6 +680,6 @@ in
}; };
} }
) workerList); ) workerList);
}) })
]); ]);
} }

235
synapse-module/nginx.nix Normal file
View File

@ -0,0 +1,235 @@
{ lib, pkgs, config, ...}:
let
cfg = config.services.matrix-synapse-next;
getWorkersOfType = type: lib.filterAttrs (_: w: w.type == type) cfg.workers.instances;
isListenerType = type: listener: lib.lists.any (r: lib.lists.any (n: n == type) r.names) listener.resources;
firstListenerOfType = type: worker: lib.lists.findFirst (isListenerType type) (throw "No federation endpoint on receiver") worker.settings.worker_listeners;
wAddressOfType = type: w: lib.lists.findFirst (_: true) (throw "No address in receiver") (firstListenerOfType type w).bind_addresses;
wPortOfType = type: w: (firstListenerOfType type w).port;
wSocketAddressOfType = type: w: "${wAddressOfType type w}:${builtins.toString (wPortOfType type w)}";
generateSocketAddresses = type: workers: lib.mapAttrsToList (_: w: "${wSocketAddressOfType type w}") workers;
in
{
config = lib.mkIf cfg.enableNginx {
services.nginx.commonHttpConfig = ''
# No since argument means its initialSync
map $arg_since $synapse_unknown_sync {
default synapse_normal_sync;
''' synapse_initial_sync;
}
map $uri $synapse_uri_group {
# Sync requests
~^/_matrix/client/(r0|v3)/sync$ $synapse_unknown_sync;
~^/_matrix/client/(api/v1|r0|v3)/event$ synapse_normal_sync;
~^/_matrix/client/(api/v1|r0|v3)/initialSync$ synapse_initial_sync;
~^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$ synapse_initial_sync;
# Federation requests
~^/_matrix/federation/v1/event/ synapse_federation;
~^/_matrix/federation/v1/state/ synapse_federation;
~^/_matrix/federation/v1/state_ids/ synapse_federation;
~^/_matrix/federation/v1/backfill/ synapse_federation;
~^/_matrix/federation/v1/get_missing_events/ synapse_federation;
~^/_matrix/federation/v1/publicRooms synapse_federation;
~^/_matrix/federation/v1/query/ synapse_federation;
~^/_matrix/federation/v1/make_join/ synapse_federation;
~^/_matrix/federation/v1/make_leave/ synapse_federation;
~^/_matrix/federation/(v1|v2)/send_join/ synapse_federation;
~^/_matrix/federation/(v1|v2)/send_leave/ synapse_federation;
~^/_matrix/federation/(v1|v2)/invite/ synapse_federation;
~^/_matrix/federation/v1/event_auth/ synapse_federation;
~^/_matrix/federation/v1/timestamp_to_event/ synapse_federation;
~^/_matrix/federation/v1/exchange_third_party_invite/ synapse_federation;
~^/_matrix/federation/v1/user/devices/ synapse_federation;
~^/_matrix/key/v2/query synapse_federation;
~^/_matrix/federation/v1/hierarchy/ synapse_federation;
# Inbound federation transaction request
~^/_matrix/federation/v1/send/ synapse_federation_transaction;
# Client API requests
~^/_matrix/client/(api/v1|r0|v3|unstable)/createRoom$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/publicRooms$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/joined_members$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/context/.*$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/members$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/state$ synapse_client_interaction;
~^/_matrix/client/v1/rooms/.*/hierarchy$ synapse_client_interaction;
~^/_matrix/client/(v1|unstable)/rooms/.*/relations/ synapse_client_interaction;
~^/_matrix/client/v1/rooms/.*/threads$ synapse_client_interaction;
~^/_matrix/client/unstable/org.matrix.msc2716/rooms/.*/batch_send$ synapse_client_interaction;
~^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary$ synapse_client_interaction;
~^/_matrix/client/(r0|v3|unstable)/account/3pid$ synapse_client_interaction;
~^/_matrix/client/(r0|v3|unstable)/account/whoami$ synapse_client_interaction;
~^/_matrix/client/(r0|v3|unstable)/devices$ synapse_client_interaction;
~^/_matrix/client/versions$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/voip/turnServer$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/event/ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/joined_rooms$ synapse_client_interaction;
~^/_matrix/client/v1/rooms/.*/timestamp_to_event$ synapse_client_interaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/search$ synapse_client_interaction;
# Encryption requests
~^/_matrix/client/(r0|v3|unstable)/keys/query$ synapse_client_encryption;
~^/_matrix/client/(r0|v3|unstable)/keys/changes$ synapse_client_encryption;
~^/_matrix/client/(r0|v3|unstable)/keys/claim$ synapse_client_encryption;
~^/_matrix/client/(r0|v3|unstable)/room_keys/ synapse_client_encryption;
~^/_matrix/client/(r0|v3|unstable)/keys/upload/ synapse_client_encryption;
# Registration/login requests
~^/_matrix/client/(api/v1|r0|v3|unstable)/login$ synapse_client_login;
~^/_matrix/client/(r0|v3|unstable)/register$ synapse_client_login;
~^/_matrix/client/v1/register/m.login.registration_token/validity$ synapse_client_login;
# Event sending requests
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/redact synapse_client_transaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/send synapse_client_transaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/state/ synapse_client_transaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$ synapse_client_transaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/join/ synapse_client_transaction;
~^/_matrix/client/(api/v1|r0|v3|unstable)/profile/ synapse_client_transaction;
# Account data requests
~^/_matrix/client/(r0|v3|unstable)/.*/tags synapse_client_data;
~^/_matrix/client/(r0|v3|unstable)/.*/account_data synapse_client_data;
# Receipts requests
~^/_matrix/client/(r0|v3|unstable)/rooms/.*/receipt synapse_client_interaction;
~^/_matrix/client/(r0|v3|unstable)/rooms/.*/read_markers synapse_client_interaction;
# Presence requests
~^/_matrix/client/(api/v1|r0|v3|unstable)/presence/ synapse_client_presence;
# User directory search requests;
~^/_matrix/client/(r0|v3|unstable)/user_directory/search$ synapse_client_user-dir;
}
#Plugboard for url -> workers
map $synapse_uri_group $synapse_backend {
default synapse_master;
synapse_initial_sync synapse_worker_initial_sync;
synapse_normal_sync synapse_worker_normal_sync;
synapse_federation synapse_worker_federation;
synapse_federation_transaction synapse_worker_federation;
synapse_client_user-dir synapse_worker_user-dir;
}
# from https://github.com/tswfi/synapse/commit/b3704b936663cc692241e978dce4ac623276b1a6
map $arg_access_token $accesstoken_from_urlparam {
# Defaults to just passing back the whole accesstoken
default $arg_access_token;
# Try to extract username part from accesstoken URL parameter
"~syt_(?<username>.*?)_.*" $username;
}
map $http_authorization $mxid_localpart {
# Defaults to just passing back the whole accesstoken
default $http_authorization;
# Try to extract username part from accesstoken header
"~Bearer syt_(?<username>.*?)_.*" $username;
# if no authorization-header exist, try mapper for URL parameter "access_token"
"" $accesstoken_from_urlparam;
}
'';
services.nginx.upstreams.synapse_master.servers = let
isMainListener = l: isListenerType "client" l && isListenerType "federation" l;
firstMainListener = lib.findFirst isMainListener
(throw "No cartch-all listener configured") cfg.settings.listeners;
address = lib.findFirst (_: true) (throw "No address in main listener") firstMainListener.bind_addresses;
port = firstMainListener.port;
socketAddress = "${address}:${builtins.toString port}";
in {
"${socketAddress}" = { };
};
services.nginx.upstreams.synapse_worker_federation = {
servers = let
fedReceivers = getWorkersOfType "fed-receiver";
socketAddresses = generateSocketAddresses "federation" fedReceivers;
in if fedReceivers != { } then
lib.genAttrs socketAddresses (_: { })
else config.services.nginx.upstreams.synapse_master.servers;
extraConfig = ''
ip_hash;
'';
};
services.nginx.upstreams.synapse_worker_initial_sync = {
servers = let
initialSyncers = getWorkersOfType "initial-sync";
socketAddresses = generateSocketAddresses "client" initialSyncers;
in if initialSyncers != { } then
lib.genAttrs socketAddresses (_: { })
else config.services.nginx.upstreams.synapse_master.servers;
extraConfig = ''
hash $mxid_localpart consistent;
'';
};
services.nginx.upstreams.synapse_worker_normal_sync = {
servers = let
normalSyncers = getWorkersOfType "normal-sync";
socketAddresses = generateSocketAddresses "client" normalSyncers;
in if normalSyncers != { } then
lib.genAttrs socketAddresses (_: { })
else config.services.nginx.upstreams.synapse_master.servers;
extraConfig = ''
hash $mxid_localpart consistent;
'';
};
services.nginx.upstreams.synapse_worker_user-dir = {
servers = let
workers = getWorkersOfType "user-dir";
socketAddresses = generateSocketAddresses "client" workers;
in if workers != { } then
lib.genAttrs socketAddresses (_: { })
else config.services.nginx.upstreams.synapse_master.servers;
};
services.nginx.virtualHosts."${cfg.public_baseurl}" = {
enableACME = true;
forceSSL = true;
locations."/_matrix" = {
proxyPass = "http://$synapse_backend";
extraConfig = ''
add_header X-debug-backend $synapse_backend;
add_header X-debug-group $synapse_uri_group;
client_max_body_size ${cfg.settings.max_upload_size};
proxy_read_timeout 10m;
'';
};
locations."~ ^/_matrix/client/(r0|v3)/sync$" = {
proxyPass = "http://$synapse_backend";
extraConfig = ''
proxy_read_timeout 1h;
'';
};
locations."~ ^/_matrix/client/(api/v1|r0|v3)/initialSync$" = {
proxyPass = "http://synapse_worker_initial_sync";
extraConfig = ''
proxy_read_timeout 1h;
'';
};
locations."~ ^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$" = {
proxyPass = "http://synapse_worker_initial_sync";
extraConfig = ''
proxy_read_timeout 1h;
'';
};
locations."/_synapse/client" = {
proxyPass = "http://$synapse_backend";
};
};
};
}