Finally comitting matrix-configuration now that we have secret-management

This commit is contained in:
Daniel Lovbrotte Olsen 2022-12-07 10:14:39 +01:00 committed by System administrator
parent 3ed65c6cfa
commit 4a9efb0d5c
10 changed files with 717 additions and 284 deletions

View File

@ -16,7 +16,6 @@ in
../../services/matrix ../../services/matrix
../../services/nginx ../../services/nginx
../../services/postgres
]; ];
@ -48,6 +47,10 @@ in
address = "129.241.210.169"; address = "129.241.210.169";
prefixLength = 25; prefixLength = 25;
} }
{
address = "129.241.210.213";
prefixLength = 25;
}
]; ];
}; };
networking.interfaces.ens18.ipv6 = { networking.interfaces.ens18.ipv6 = {
@ -56,6 +59,10 @@ in
address = "2001:700:300:1900::169"; address = "2001:700:300:1900::169";
prefixLength = 64; prefixLength = 64;
} }
{
address = "2001:700:300:1900::213";
prefixLength = 64;
}
]; ];
}; };
networking.nameservers = [ "129.241.0.200" "129.241.0.201" ]; networking.nameservers = [ "129.241.0.200" "129.241.0.201" ];

37
secrets/jokum/jokum.yaml Normal file
View File

@ -0,0 +1,37 @@
matrix:
synapse:
dbconfig: ENC[AES256_GCM,data:a0Bq2ilDZM0GddHZS1WcaSY3kdFDbau4BNMu+rumisYZy5/VQOE6LT/gq3vdwH2T7D3r1/cj7YSRcdjq+SRYHiJ9xgb1m3tx+ZlvNrY8PMaYvtmOpMoXyYlJ2iT7/IiMk5UW50cSZEcww7zS8NknZMzjiNEq3+D88J57J6WRmQqj/w==,iv:BsbOLl/hlQIjOLnik8lZWO3+jhMEZ//fisxLon7HdE0=,tag:WqMGflg5+Sh2zx5QFnjy4A==,type:str]
turnconfig: ENC[AES256_GCM,data:lHySrJUpQKAUXsl9LzYlxu4YSCz4qJF6MRLr+LprTEdhGvrnk7U=,iv:Jz7LEOUwTI8LCMOKqB2vN/0Zs+S0IJkHY3wpAC0q5YI=,tag:8KR7duN+Qqpl6B40hSEndw==,type:str]
coturn:
static-auth-secret: ENC[AES256_GCM,data:tPz4GUvJwB2osO2vwyyThms=,iv:MVoFWgqHm88JXaCYa5l57SkX3fSmP97Z7IzvwumHWY8=,tag:af7Qs4qiSYQ/OBLJbZGk2A==,type:str]
registrations:
mx-puppet-discord: ENC[AES256_GCM,data:jrFvjgWG3qdM8ruIehfBP4WfSUFJHA2SabhVFNuUCWk2HR7Q+2PSGrZ0h9ikFnJhZD5JO0hK5AiYJQ34MWaFEKuOqV1DGOf6VCr3QgeIItvctQyS2DD+R1zt5a32KIJOFMFbH66c/X3POY/ZMsT7UA7BcvqX8uB0Esqa8wRqV4wmzGOmUp2v4uBTihr9O7V61m0t+kSYmMgZnJqmGf7b0/FM/dmTxcp+qXrnRPz9JCNCtYKoVjDUZFNlBKrppDC6GjN1y4cBzzVezlHKCk53x71T2yberFRcK02Ni9RLoH0yqU/1jt2gjaQHal52wF3QxSXKT9rtCGZh2o+9Txpp61LNqBiua/Ija+9ToALlP8ihsFDv7UZ04g5y8u2A5mC1HHB5wU1E94ay0NcYasfdBOwQqpwqunQxjvp1cHHnspUasXXwWoRr7faxkKhBGVoCA5yNsSmp8+4zWjkFendRVZan1UsJsJ8me1nOLji4BKAevh3FzagLjjtANIoNqKr9kGBfDzIOCgPGzBLlhJcMZPwZCV/QNbs583g=,iv:3gzyGz7T9PK/J92X46YXYT98bpTnx1uPiiwXuls/kOA=,tag:O+bfssIhPDSKRCpv0YPxTg==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age17tagmpwqjk3mdy45rfesrfey6h863x8wfq38wh33tkrlrywxducs0k6tpq
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzOU56dFpuZ0QySnZFK0k1
MTl1blEzclZBa1dZV1NGYUVBa3hyRmR4WlYwCnRHdXd1TzZQdjAwTGJ5aHYxVS9H
M3Rzd082QVliMzBCdGZ3alZVYlY5cUEKLS0tIER6WWNhWUZuZmI5QXlkazdZTllI
OUN3Z21WQ010ZjlMamVwK0VsYkM4TjgKR+cv3y7rSJ7UwaE3fl42jBV43lG4OU0n
atbZeUj+i2SmaFIE+MoyckbygtFZOvs93xwuMDJjkD7a+EGfCz2ggw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1n4vc3dhv8puqz6ntwrkkpdfj0q002hexqee48wzahll8cmce2ezssrq608
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5cGpSa0NZeGQ4K0w3elhJ
WG5VNkxsT3FERXRZUkt3VEJtRlhSRThSemo4CnlFcjN4UUgrUTN5MHdoZEhyaTdL
OFM4MXFrbnp0MmJCOXRPcFljZVcwdkUKLS0tIFoxelA4Z3lycUY5SzdqTVZ3aW1r
cGFFU3RzU200b0x3M2dkbFJWU0ZVSzAKSg7ZlRvgJshAJxXiXgT+b4nhFe4MjVRY
n7+Ld+SdXJvGtZsH4IObkVYgj16d3SFBs87yWA+NExUoEuQb97fa7Q==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2022-12-07T08:40:22Z"
mac: ENC[AES256_GCM,data:u7XsunuwsjzqkSH/IFP28ijvzGavxZgB8wU5ai8SoBlTyHpUBt/WQ1kcVbqPtsb6xMb9b+7o1MdWOz0yy8P4Jpj0/AalgNI1Rh84I1M/Vurn1fXnUZoM86v3OfLwO1iMExafh7PLiOxH/W1fNjaLJYdVbU6FhDI6Od25yF1W4PI=,iv:Vk2AFkt5p39y20UoWR9HP1iYJCqYVtYHBMnY+lDa9FQ=,tag:6WWSNvt0g7Vtickb7c6dUQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.7.3

133
services/matrix/coturn.nix Normal file
View File

@ -0,0 +1,133 @@
{ config, lib, pkgs, secrets, ... }:
{
imports = [ ./synapse-module ];
sops.secrets."matrix/synapse/turnconfig" = {
owner = config.users.users.matrix-synapse.name;
group = config.users.users.matrix-synapse.group;
};
sops.secrets."matrix/coturn/static-auth-secret" = {
owner = config.users.users.turnserver.name;
group = config.users.users.turnserver.group;
};
services.matrix-synapse-next = {
extraConfigFiles = [
config.sops.secrets."matrix/synapse/turnconfig".path
];
settings = {
turn_uris = [
"turns:turn.pvv.ntnu.no:443?transport=tcp"
"turns:turn.pvv.ntnu.no:443?transport=udp"
"turns:turn.pvv.ntnu.no:5349?transport=tcp"
"turns:turn.pvv.ntnu.no:5349?transport=udp"
"turns:turn.pvv.ntnu.no:3478?transport=udp"
"turns:turn.pvv.ntnu.no:3478?transport=tcp"
"turn:turn.pvv.ntnu.no:3478?transport=udp"
"turn:turn.pvv.ntnu.no:3478?transport=tcp"
"turns:turn.pvv.ntnu.no:3479?transport=tcp"
"turns:turn.pvv.ntnu.no:3479?transport=udp"
"turn:turn.pvv.ntnu.no:3479?transport=tcp"
"turn:turn.pvv.ntnu.no:3479?transport=udp"
];
};
};
security.acme.certs.${config.services.coturn.realm} = {
email = "drift@pvv.ntnu.no";
listenHTTP = "129.241.210.213:80";
reloadServices = [ "coturn.service" ];
};
users.users.turnserver.extraGroups = [ "acme" ];
systemd.services."acme-${config.services.coturn.realm}".serviceConfig = {
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
};
services.coturn = rec {
enable = true;
realm = "turn.pvv.ntnu.no";
cert = "${config.security.acme.certs.${realm}.directory}/full.pem";
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
use-auth-secret = true;
# World readable but I dont think it's that bad
static-auth-secret-file = config.sops.secrets."matrix/coturn/static-auth-secret".path;
secure-stun = true;
listening-ips = [ "129.241.210.213" "2001:700:300:1900::213" ];
tls-listening-port = 443;
alt-tls-listening-port = 5349;
listening-port = 3478;
min-port = 49000;
max-port = 50000;
no-tls = false;
no-dtls = false;
no-tcp-relay = false;
extraConfig = ''
verbose
# ban private IP ranges
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
#user-quota=120
#total-quota=1200
'';
};
networking.firewall = {
interfaces.ens18 = let
range = with config.services.coturn; [ {
from = min-port;
to = max-port;
} ];
in
{
allowedUDPPortRanges = range;
allowedUDPPorts = [ 443 3478 3479 5349 ];
allowedTCPPortRanges = range;
allowedTCPPorts = [ 443 3478 3479 5349 ];
};
};
}

View File

@ -4,7 +4,11 @@
imports = [ imports = [
./synapse.nix ./synapse.nix
./synapse-admin.nix
./element.nix ./element.nix
./coturn.nix
./discord.nix
]; ];

View File

@ -0,0 +1,38 @@
{ config, ... }:
let
cfg = config.services.mx-puppet-discord;
in
{
imports = [ ./synapse-module ];
users.groups.keys-matrix-registrations = { };
sops.secrets."matrix/registrations/mx-puppet-discord" = {
owner = config.users.users.matrix-synapse.name;
group = config.users.groups.keys-matrix-registrations.name;
};
systemd.services.mx-puppet-discord = {
serviceConfig.SupplementaryGroups = [ config.users.groups.keys-matrix-registrations.name ];
};
services.mx-puppet-discord.enable = true;
services.mx-puppet-discord.settings = {
bridge = {
bindAddress = "localhost";
domain = "pvv.ntnu.no";
homeserverUrl = config.services.matrix-synapse-next.settings.public_baseurl;
};
provisioning.whitelist = [ "@dandellion:dodsorf\\.as" "@danio:pvv\\.ntnu\\.no"];
relay.whitelist = [ ".*" ];
selfService.whitelist = [ "@danio:pvv\\.ntnu\\.no" "@dandellion:dodsorf\\.as" ];
};
services.mx-puppet-discord.serviceDependencies = [ "matrix-synapse.target" "nginx.service" ];
services.matrix-synapse-next.settings.app_service_config_files = [ config.sops.secrets."matrix/registrations/mx-puppet-discord".path ];
}

View File

@ -0,0 +1,13 @@
{ config, lib, pkgs, ... }:
# This service requires you to have access to endpoints not available over the internet
# Use an ssh proxy or similar to access this dashboard.
# Then go into your developer console, storage, and change the baseurl to the local ip for synapse
{
services.nginx.virtualHosts."localhost" = {
rejectSSL = true;
root = pkgs.synapse-admin;
};
}

View File

@ -1,13 +1,18 @@
{ lib, pkgs, config, ... }: { lib, pkgs, config, ... }:
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
{ {
options.services.matrix-synapse-next = { options.services.matrix-synapse-next = {
@ -41,13 +46,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 {
@ -56,96 +60,192 @@ 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;
};
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.federation_sender";
"fed-receiver" = "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.pusher"
"synapse.app.appservice"
"synapse.app.federation_sender"
"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" = [ ];
}.${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.federation_sender";
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 {
@ -172,15 +272,9 @@ 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;
}; };
@ -203,7 +297,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;
@ -211,7 +305,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 {
@ -228,63 +322,36 @@ 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 = let
enableReplication = lib.lists.any
(w: !(builtins.elem w.settings.worker_app [ "synapse.app.federation_sender" ]))
(builtins.attrValues cfg.workers.instances);
in [
{ {
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 enableReplication {
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.acme = {
enable = lib.mkOption {
type = lib.types.bool;
description = "ACME support requires tls_certificate_path and tls_private_key_path to be set";
default = false;
};
port = lib.mkOption {
type = lib.types.port;
description = "Port number to listen on for the HTTP-01 challenge. Change this if you are forwarding connections through Apache/Nginx/etc.";
default = 80;
};
bind_addresses = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Local addresses to listen on for incoming connections.
Again, you may want to change this if you are forwarding connections
through Apache/Nginx/etc.";
'';
default = [ "0.0.0.0" "::"];
};
reprovision_threshold = lib.mkOption {
type = lib.types.ints.positive;
description = "How many days remaining on a certificate before it is renewed";
default = 30;
};
domain = lib.mkOption {
type = lib.types.str;
description = ''
The domain that the certificate should be for. Normally this
should be the same as your Matrix domain (i.e., 'server_name'), but,
by putting a file at 'https://<server_name>/.well-known/matrix/server',
you can delegate incoming traffic to another server. If you do that,
you should give the target of the delegation here.
For example: if your 'server_name' is 'example.com', but
'https://example.com/.well-known/matrix/server' delegates to
'matrix.example.com', you should put 'matrix.example.com' here.
'';
default = cfg.settings.server_name;
};
account_key_file = lib.mkOption {
type = lib.types.path;
description = "file to use for the account key, will be generated if it doesn't exist";
default = cfg.dataDir + "/acme_account.key";
};
};
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 = ''
@ -316,7 +383,7 @@ in
options.media_store_path = lib.mkOption { options.media_store_path = lib.mkOption {
type = lib.types.path; type = lib.types.path;
description = "Directory where uploaded images and attachments are stored"; description = "Directory where uploaded images and attachments are stored";
default = cfg.dataDir + "/media_store"; default = "${cfg.dataDir}/media_store";
}; };
options.max_upload_size = lib.mkOption { options.max_upload_size = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -327,7 +394,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 {
@ -350,7 +417,7 @@ in
options.signing_key_path = lib.mkOption { options.signing_key_path = lib.mkOption {
type = lib.types.path; type = lib.types.path;
description = "Path to the signing key to sign messages with"; description = "Path to the signing key to sign messages with";
default = cfg.dataDir + "/" +cfg.settings.server_name + ".signing.key"; default = "${cfg.dataDir}/homeserver.signing.key";
}; };
options.trusted_key_servers = lib.mkOption { options.trusted_key_servers = lib.mkOption {
@ -370,16 +437,11 @@ 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 { options.send_federation = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
description = "Disables sending of outbound federation transactions on the main process. Set to false if using federation senders"; 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; default = cfg.settings.federation_sender_instances == [];
}; };
options.federation_sender_instances = lib.mkOption { options.federation_sender_instances = lib.mkOption {
@ -390,7 +452,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";
}; };
}; };
}; };
@ -428,7 +505,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" ];
@ -456,9 +533,37 @@ 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" ]; } ];
};
});
})
])
({ ({
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);
@ -470,6 +575,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
]; ];
@ -478,6 +584,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) }
@ -488,5 +600,118 @@ in
} }
) workerList); ) workerList);
}) })
(lib.mkIf cfg.enableNginx {
services.nginx.commonHttpConfig = ''
map $request_uri $synapse_backend {
default synapse_master;
# Sync requests
~*^/_matrix/client/(v2_alpha|r0)/sync$ synapse_client;
~*^/_matrix/client/(api/v1|v2_alpha|r0)/events$ synapse_client;
~*^/_matrix/client/(api/v1|r0)/initialSync$ synapse_client;
~*^/_matrix/client/(api/v1|r0)/rooms/[^/]+/initialSync$ synapse_client;
# 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/send_join/ synapse_federation;
~*^/_matrix/federation/v2/send_join/ synapse_federation;
~*^/_matrix/federation/v1/send_leave/ synapse_federation;
~*^/_matrix/federation/v2/send_leave/ synapse_federation;
~*^/_matrix/federation/v1/invite/ synapse_federation;
~*^/_matrix/federation/v2/invite/ synapse_federation;
~*^/_matrix/federation/v1/query_auth/ synapse_federation;
~*^/_matrix/federation/v1/event_auth/ synapse_federation;
~*^/_matrix/federation/v1/exchange_third_party_invite/ synapse_federation;
~*^/_matrix/federation/v1/user/devices/ synapse_federation;
~*^/_matrix/federation/v1/get_groups_publicised$ synapse_federation;
~*^/_matrix/key/v2/query synapse_federation;
# Inbound federation transaction request
~*^/_matrix/federation/v1/send/ synapse_federation;
# Client API requests
~*^/_matrix/client/(api/v1|r0|unstable)/publicRooms$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/context/.*$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/members$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/account/3pid$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/devices$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/keys/query$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/keys/changes$ synapse_client;
~*^/_matrix/client/versions$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/voip/turnServer$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/joined_groups$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/publicised_groups$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/publicised_groups/ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/event/ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/joined_rooms$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/search$ synapse_client;
# Registration/login requests
~*^/_matrix/client/(api/v1|r0|unstable)/login$ synapse_client;
~*^/_matrix/client/(r0|unstable)/register$ synapse_client;
# Event sending requests
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/redact synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state/ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/join/ synapse_client;
~*^/_matrix/client/(api/v1|r0|unstable)/profile/ synapse_client;
}
'';
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_federation.servers = let
fedReceivers = lib.filterAttrs (_: w: w.type == "fed-receiver") cfg.workers.instances;
isListenerType = type: listener: lib.lists.any (r: lib.lists.any (n: n == type) r.names) listener.resources;
isFedListener = l: isListenerType "federation" l;
firstFedListener = w: lib.lists.findFirst isFedListener (throw "No federation endpoint on receiver") w.settings.worker_listeners;
wAddress = w: lib.lists.findFirst (_: true) (throw "No address in receiver") (firstFedListener w).bind_addresses;
wPort = w: (firstFedListener w).port;
socketAddress = w: "${wAddress w}:${builtins.toString (wPort w)}";
socketAddresses = lib.mapAttrsToList (_: value: "${socketAddress value}") fedReceivers;
in if fedReceivers != [ ] then lib.genAttrs socketAddresses (_: { }) else config.services.nginx.upstreams.synapse_master.servers;
services.nginx.upstreams.synapse_client.servers = config.services.nginx.upstreams.synapse_master.servers;
services.nginx.virtualHosts."${cfg.public_baseurl}" = {
enableACME = true;
forceSSL = true;
locations."/_matrix" = {
proxyPass = "http://$synapse_backend";
extraConfig = ''
client_max_body_size ${cfg.settings.max_upload_size};
'';
};
locations."/_synapse/client" = {
proxyPass = "http://$synapse_backend";
};
};
})
]); ]);
} }

View File

@ -2,77 +2,84 @@
let let
cfg = config.services.matrix-synapse-next; cfg = config.services.matrix-synapse-next;
in
{
imap0Attrs = with lib; f: set:
listToAttrs (imap0 (i: attr: nameValuePair attr (f i attr set.${attr})) (attrNames set));
in {
imports = [ ./synapse-module ]; imports = [ ./synapse-module ];
sops.secrets."matrix/synapse/dbconfig" = {
owner = config.users.users.matrix-synapse.name;
group = config.users.users.matrix-synapse.group;
};
services.matrix-synapse-next = { services.matrix-synapse-next = {
enable = true; enable = true;
package = pkgs.matrix-synapse;
dataDir = "/data/synapse"; dataDir = "/data/synapse";
enableMainSynapse = true; workers.federationSenders = 1;
workers.federationReceivers = 1;
enableNginx = true;
extraConfigFiles = [
config.sops.secrets."matrix/synapse/dbconfig".path
];
settings = { settings = {
server_name = "pvv.ntnu.no"; server_name = "pvv.ntnu.no";
public_baseurl = "https://matrix.pvv.ntnu.no"; public_baseurl = "https://matrix.pvv.ntnu.no";
# LOL postgres too old media_store_path = "${cfg.dataDir}/media";
# database = {
# name = "psycopg2"; #postgres pvv ntnu no 5432
# args = {
# host = "postgres.pvv.ntnu.no";
# user = "synapse";
# password = "FOLINghtSonj";
# dbname = "synapse";
# };
# };
database = { autocreate_auto_join_rooms = false;
name = "psycopg2"; auto_join_rooms = [
args = { "#pvv:pvv.ntnu.no" # Main space
host = "localhost"; "#announcements:pvv.ntnu.no"
user = "synapse"; "#general:pvv.ntnu.no"
password = "synapse";
dbname = "synapse";
};
};
listeners = [
{
bind_addresses = ["127.0.1.2"]; port = 8008; tls = false; type = "http";
x_forwarded = true;
resources = [
{ names = ["client"]; compress = true;}
{ names = ["federation"]; compress = false;}
];
}
{
bind_addresses = ["127.0.1.2"]; port = 8010; tls = false; type = "http";
resources = [
{ names = ["metrics"]; compress = false; }
];
}
{
bind_addresses = [ "127.0.1.2"]; port = 9008; tls = false; type = "http";
resources = [
{ names = ["replication"]; compress = false; }
];
}
]; ];
max_upload_size = "150M";
enable_metrics = true; enable_metrics = true;
use_presence = true; enable_registration = false;
password_config.enabled = lib.mkForce false; password_config.enabled = lib.mkForce false;
enable_registration = false; trusted_key_servers = [
{ server_name = "matrix.org"; }
{ server_name = "dodsorf.as"; }
];
url_preview_enabled = true;
url_preview_ip_range_blacklist = [
# synapse example config
"127.0.0.0/8"
"10.0.0.0/8"
"172.16.0.0/12"
"192.168.0.0/16"
"100.64.0.0/10"
"192.0.0.0/24"
"169.254.0.0/16"
"192.88.99.0/24"
"198.18.0.0/15"
"192.0.2.0/24"
"198.51.100.0/24"
"203.0.113.0/24"
"224.0.0.0/4"
"::1/128"
"fe80::/10"
"fc00::/7"
"2001:db8::/32"
"ff00::/8"
"fec0::/10"
# NTNU
"129.241.0.0/16"
"2001:700:300::/44"
];
saml2_config = { saml2_config = {
sp_config.metadata.remote = [ sp_config.metadata.remote = [
@ -129,8 +136,7 @@ in
url = "https://www.pvv.ntnu.no"; url = "https://www.pvv.ntnu.no";
}; };
contact_person = [ contact_person = [
{ { given_name = "Drift";
given_name = "Drift";
sur_name = "King"; sur_name = "King";
email_adress = [ "drift@pvv.ntnu.no" ]; email_adress = [ "drift@pvv.ntnu.no" ];
contact_type = "technical"; contact_type = "technical";
@ -147,73 +153,46 @@ in
#attribute_requirements = [ #attribute_requirements = [
# {attribute = "userGroup"; value = "medlem";} # Do we have this? # {attribute = "userGroup"; value = "medlem";} # Do we have this?
#]; #];
}; };
};
};
signing_key_path = "${cfg.dataDir}/homeserver.signing.key"; services.redis.servers."".enable = true;
media_store_path = "${cfg.dataDir}/media";
services.nginx.virtualHosts."matrix.pvv.ntnu.no" = lib.mkMerge [({
locations = let
isListenerType = type: listener: lib.lists.any (r: lib.lists.any (n: n == type) r.names) listener.resources;
isMetricsListener = l: isListenerType "metrics" l;
federation_sender_instances = [ firstMetricsListener = w: lib.lists.findFirst isMetricsListener (throw "No metrics endpoint on worker") w.settings.worker_listeners;
"federation_sender1"
wAddress = w: lib.lists.findFirst (_: true) (throw "No address in receiver") (firstMetricsListener w).bind_addresses;
wPort = w: (firstMetricsListener w).port;
socketAddress = w: "${wAddress w}:${toString (wPort w)}";
metricsPath = w: "/metrics/${w.type}/${toString w.index}";
proxyPath = w: "http://${socketAddress w}/_synapse/metrics";
in lib.mapAttrs' (n: v: lib.nameValuePair (metricsPath v) ({ proxyPass = proxyPath v; }))
cfg.workers.instances;
})
({
locations."/metrics/master/1" = {
proxyPass = "http://127.0.0.1:9000/_synapse/metrics";
};
locations."/metrics/" = let
endpoints = builtins.map (x: "matrix.pvv.ntnu.no/metrics/${x}") [
"master/1"
"fed-sender/1"
"fed-receiver/1"
]; ];
in {
redis = { alias = pkgs.writeTextDir "/config.json"
enabled = true; (builtins.toJSON [
}; { targets = endpoints;
labels = { };
}]) + "/";
}; };
})];
workers = {
"federation_sender1" = {
settings = {
worker_app = "synapse.app.federation_sender";
worker_replication_host = "127.0.1.2";
worker_replication_http_port = 9008;
worker_listeners = [
{
bind_addresses = ["127.0.1.10"]; port = 8010; tls = false; type = "http";
resources = [
{ names = ["metrics"]; compress = false; }
];
}
];
};
};
"federation_reciever1" = {
settings = {
worker_app = "synapse.app.generic_worker";
worker_replication_host = "127.0.1.2";
worker_replication_http_port = 9008;
worker_listeners = [
{
bind_addresses = ["127.0.1.11"]; port = 8010; tls = false; type = "http";
resources = [
{ names = ["metrics"]; compress = false; }
];
}
{
bind_addresses = ["127.0.1.11"]; port = 8011; tls = false; type = "http";
resources = [
{ names = ["federation"]; compress = false; }
];
}
];
};
};
};
};
services.redis.servers.matrix.enable = true;
services.nginx.virtualHosts."matrix.pvv.ntnu.no" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.1.2:8008";
};
locations."/_matrix/federation" = {
proxyPass = "http://127.0.1.11:8011";
};
};
} }

View File

@ -9,6 +9,9 @@
services.nginx = { services.nginx = {
enable = true; enable = true;
defaultListenAddresses = [ "129.241.210.169" "127.0.0.1" "[2001:700:300:1900::169]" ];
recommendedProxySettings = true; recommendedProxySettings = true;
recommendedTlsSettings = true; recommendedTlsSettings = true;
recommendedGzipSettings = true; recommendedGzipSettings = true;

View File

@ -1,6 +0,0 @@
{config, ...}:
{
services.postgresql.enable = true;
services.postgresql.dataDir = "/data/postgres";
}