1{ config, lib, options, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.matrix-synapse;
7 format = pkgs.formats.yaml {};
8
9 # remove null values from the final configuration
10 finalSettings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings;
11 configFile = format.generate "homeserver.yaml" finalSettings;
12 logConfigFile = format.generate "log_config.yaml" cfg.logConfig;
13
14 pluginsEnv = cfg.package.python.buildEnv.override {
15 extraLibs = cfg.plugins;
16 };
17
18 usePostgresql = cfg.settings.database.name == "psycopg2";
19 hasLocalPostgresDB = let args = cfg.settings.database.args; in
20 usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]));
21
22 registerNewMatrixUser =
23 let
24 isIpv6 = x: lib.length (lib.splitString ":" x) > 1;
25 listener =
26 lib.findFirst (
27 listener: lib.any (
28 resource: lib.any (
29 name: name == "client"
30 ) resource.names
31 ) listener.resources
32 ) (lib.last cfg.settings.listeners) cfg.settings.listeners;
33 # FIXME: Handle cases with missing client listener properly,
34 # don't rely on lib.last, this will not work.
35
36 # add a tail, so that without any bind_addresses we still have a useable address
37 bindAddress = head (listener.bind_addresses ++ [ "127.0.0.1" ]);
38 listenerProtocol = if listener.tls
39 then "https"
40 else "http";
41 in
42 pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
43 exec ${cfg.package}/bin/register_new_matrix_user \
44 $@ \
45 ${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \
46 "${listenerProtocol}://${
47 if (isIpv6 bindAddress) then
48 "[${bindAddress}]"
49 else
50 "${bindAddress}"
51 }:${builtins.toString listener.port}/"
52 '';
53in {
54
55 imports = [
56
57 (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
58 The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
59 as the behavior is now obsolete.
60 '')
61 (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
62 Database configuration must be done manually. An exemplary setup is demonstrated in
63 <nixpkgs/nixos/tests/matrix/synapse.nix>
64 '')
65 (mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "")
66 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] ''
67 You may add additional event types via
68 `services.matrix-synapse.room_prejoin_state.additional_event_types` and
69 disable the default events via
70 `services.matrix-synapse.room_prejoin_state.disable_default_event_types`.
71 '')
72
73 # options that don't exist in synapse anymore
74 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_host" ] "Use listener settings instead." )
75 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_port" ] "Use listener settings instead." )
76 (mkRemovedOptionModule [ "services" "matrix-synapse" "expire_access_tokens" ] "" )
77 (mkRemovedOptionModule [ "services" "matrix-synapse" "no_tls" ] "It is no longer supported by synapse." )
78 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_dh_param_path" ] "It was removed from synapse." )
79 (mkRemovedOptionModule [ "services" "matrix-synapse" "unsecure_port" ] "Use settings.listeners instead." )
80 (mkRemovedOptionModule [ "services" "matrix-synapse" "user_creation_max_duration" ] "It is no longer supported by synapse." )
81 (mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead." )
82
83 # options that were moved into rfc42 style settings
84 (mkRemovedOptionModule [ "services" "matrix-synapse" "app_service_config_files" ] "Use settings.app_service_config_files instead" )
85 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_args" ] "Use settings.database.args instead" )
86 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_name" ] "Use settings.database.args.database instead" )
87 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_type" ] "Use settings.database.name instead" )
88 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_user" ] "Use settings.database.args.user instead" )
89 (mkRemovedOptionModule [ "services" "matrix-synapse" "dynamic_thumbnails" ] "Use settings.dynamic_thumbnails instead" )
90 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_metrics" ] "Use settings.enable_metrics instead" )
91 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration" ] "Use settings.enable_registration instead" )
92 (mkRemovedOptionModule [ "services" "matrix-synapse" "extraConfig" ] "Use settings instead." )
93 (mkRemovedOptionModule [ "services" "matrix-synapse" "listeners" ] "Use settings.listeners instead" )
94 (mkRemovedOptionModule [ "services" "matrix-synapse" "logConfig" ] "Use settings.log_config instead" )
95 (mkRemovedOptionModule [ "services" "matrix-synapse" "max_image_pixels" ] "Use settings.max_image_pixels instead" )
96 (mkRemovedOptionModule [ "services" "matrix-synapse" "max_upload_size" ] "Use settings.max_upload_size instead" )
97 (mkRemovedOptionModule [ "services" "matrix-synapse" "presence" "enabled" ] "Use settings.presence.enabled instead" )
98 (mkRemovedOptionModule [ "services" "matrix-synapse" "public_baseurl" ] "Use settings.public_baseurl instead" )
99 (mkRemovedOptionModule [ "services" "matrix-synapse" "report_stats" ] "Use settings.report_stats instead" )
100 (mkRemovedOptionModule [ "services" "matrix-synapse" "server_name" ] "Use settings.server_name instead" )
101 (mkRemovedOptionModule [ "services" "matrix-synapse" "servers" ] "Use settings.trusted_key_servers instead." )
102 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_certificate_path" ] "Use settings.tls_certificate_path instead" )
103 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_private_key_path" ] "Use settings.tls_private_key_path instead" )
104 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_shared_secret" ] "Use settings.turn_shared_secret instead" )
105 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_uris" ] "Use settings.turn_uris instead" )
106 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_user_lifetime" ] "Use settings.turn_user_lifetime instead" )
107 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_enabled" ] "Use settings.url_preview_enabled instead" )
108 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_blacklist" ] "Use settings.url_preview_ip_range_blacklist instead" )
109 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_whitelist" ] "Use settings.url_preview_ip_range_whitelist instead" )
110 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_url_blacklist" ] "Use settings.url_preview_url_blacklist instead" )
111
112 # options that are too specific to mention them explicitly in settings
113 (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "email" ] "Use settings.account_threepid_delegates.email instead" )
114 (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "msisdn" ] "Use settings.account_threepid_delegates.msisdn instead" )
115 (mkRemovedOptionModule [ "services" "matrix-synapse" "allow_guest_access" ] "Use settings.allow_guest_access instead" )
116 (mkRemovedOptionModule [ "services" "matrix-synapse" "bcrypt_rounds" ] "Use settings.bcrypt_rounds instead" )
117 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration_captcha" ] "Use settings.enable_registration_captcha instead" )
118 (mkRemovedOptionModule [ "services" "matrix-synapse" "event_cache_size" ] "Use settings.event_cache_size instead" )
119 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_concurrent" ] "Use settings.rc_federation.concurrent instead" )
120 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_reject_limit" ] "Use settings.rc_federation.reject_limit instead" )
121 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_delay" ] "Use settings.rc_federation.sleep_delay instead" )
122 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_limit" ] "Use settings.rc_federation.sleep_limit instead" )
123 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_window_size" ] "Use settings.rc_federation.window_size instead" )
124 (mkRemovedOptionModule [ "services" "matrix-synapse" "key_refresh_interval" ] "Use settings.key_refresh_interval instead" )
125 (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_burst_count" ] "Use settings.rc_messages.burst_count instead" )
126 (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_per_second" ] "Use settings.rc_messages.per_second instead" )
127 (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_private_key" ] "Use settings.recaptcha_private_key instead" )
128 (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_public_key" ] "Use settings.recaptcha_public_key instead" )
129 (mkRemovedOptionModule [ "services" "matrix-synapse" "redaction_retention_period" ] "Use settings.redaction_retention_period instead" )
130 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "additional_event_types" ] "Use settings.room_prejoin_state.additional_event_types instead" )
131 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "disable_default_event_types" ] "Use settings.room_prejoin-state.disable_default_event_types instead" )
132
133 # Options that should be passed via extraConfigFiles, so they are not persisted into the nix store
134 (mkRemovedOptionModule [ "services" "matrix-synapse" "macaroon_secret_key" ] "Pass this value via extraConfigFiles instead" )
135 (mkRemovedOptionModule [ "services" "matrix-synapse" "registration_shared_secret" ] "Pass this value via extraConfigFiles instead" )
136
137 ];
138
139 options = {
140 services.matrix-synapse = {
141 enable = mkEnableOption (lib.mdDoc "matrix.org synapse");
142
143 configFile = mkOption {
144 type = types.path;
145 readOnly = true;
146 description = lib.mdDoc ''
147 Path to the configuration file on the target system. Useful to configure e.g. workers
148 that also need this.
149 '';
150 };
151
152 package = mkOption {
153 type = types.package;
154 default = pkgs.matrix-synapse;
155 defaultText = literalExpression "pkgs.matrix-synapse";
156 description = lib.mdDoc ''
157 Overridable attribute of the matrix synapse server package to use.
158 '';
159 };
160
161 plugins = mkOption {
162 type = types.listOf types.package;
163 default = [ ];
164 example = literalExpression ''
165 with config.services.matrix-synapse.package.plugins; [
166 matrix-synapse-ldap3
167 matrix-synapse-pam
168 ];
169 '';
170 description = lib.mdDoc ''
171 List of additional Matrix plugins to make available.
172 '';
173 };
174
175 withJemalloc = mkOption {
176 type = types.bool;
177 default = false;
178 description = lib.mdDoc ''
179 Whether to preload jemalloc to reduce memory fragmentation and overall usage.
180 '';
181 };
182
183 dataDir = mkOption {
184 type = types.str;
185 default = "/var/lib/matrix-synapse";
186 description = lib.mdDoc ''
187 The directory where matrix-synapse stores its stateful data such as
188 certificates, media and uploads.
189 '';
190 };
191
192 settings = mkOption {
193 default = {};
194 description = mdDoc ''
195 The primary synapse configuration. See the
196 [sample configuration](https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml)
197 for possible values.
198
199 Secrets should be passed in by using the `extraConfigFiles` option.
200 '';
201 type = with types; submodule {
202 freeformType = format.type;
203 options = {
204 # This is a reduced set of popular options and defaults
205 # Do not add every available option here, they can be specified
206 # by the user at their own discretion. This is a freeform type!
207
208 server_name = mkOption {
209 type = types.str;
210 example = "example.com";
211 default = config.networking.hostName;
212 defaultText = literalExpression "config.networking.hostName";
213 description = lib.mdDoc ''
214 The domain name of the server, with optional explicit port.
215 This is used by remote servers to look up the server address.
216 This is also the last part of your UserID.
217
218 The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
219 '';
220 };
221
222 enable_registration = mkOption {
223 type = types.bool;
224 default = false;
225 description = lib.mdDoc ''
226 Enable registration for new users.
227 '';
228 };
229
230 registration_shared_secret = mkOption {
231 type = types.nullOr types.str;
232 default = null;
233 description = mdDoc ''
234 If set, allows registration by anyone who also has the shared
235 secret, even if registration is otherwise disabled.
236
237 Secrets should be passed in via `extraConfigFiles`!
238 '';
239 };
240
241 macaroon_secret_key = mkOption {
242 type = types.nullOr types.str;
243 default = null;
244 description = mdDoc ''
245 Secret key for authentication tokens. If none is specified,
246 the registration_shared_secret is used, if one is given; otherwise,
247 a secret key is derived from the signing key.
248
249 Secrets should be passed in via `extraConfigFiles`!
250 '';
251 };
252
253 enable_metrics = mkOption {
254 type = types.bool;
255 default = false;
256 description = lib.mdDoc ''
257 Enable collection and rendering of performance metrics
258 '';
259 };
260
261 report_stats = mkOption {
262 type = types.bool;
263 default = false;
264 description = lib.mdDoc ''
265 Whether or not to report anonymized homeserver usage statistics.
266 '';
267 };
268
269 signing_key_path = mkOption {
270 type = types.path;
271 default = "${cfg.dataDir}/homeserver.signing.key";
272 description = lib.mdDoc ''
273 Path to the signing key to sign messages with.
274 '';
275 };
276
277 pid_file = mkOption {
278 type = types.path;
279 default = "/run/matrix-synapse.pid";
280 readOnly = true;
281 description = lib.mdDoc ''
282 The file to store the PID in.
283 '';
284 };
285
286 log_config = mkOption {
287 type = types.path;
288 default = ./synapse-log_config.yaml;
289 defaultText = lib.literalExpression "nixos/modules/services/matrix/synapse-log_config.yaml";
290 description = lib.mdDoc ''
291 The file that holds the logging configuration.
292 '';
293 };
294
295 media_store_path = mkOption {
296 type = types.path;
297 default = if lib.versionAtLeast config.system.stateVersion "22.05"
298 then "${cfg.dataDir}/media_store"
299 else "${cfg.dataDir}/media";
300 defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05";
301 description = lib.mdDoc ''
302 Directory where uploaded images and attachments are stored.
303 '';
304 };
305
306 public_baseurl = mkOption {
307 type = types.nullOr types.str;
308 default = null;
309 example = "https://example.com:8448/";
310 description = lib.mdDoc ''
311 The public-facing base URL for the client API (not including _matrix/...)
312 '';
313 };
314
315 tls_certificate_path = mkOption {
316 type = types.nullOr types.str;
317 default = null;
318 example = "/var/lib/acme/example.com/fullchain.pem";
319 description = lib.mdDoc ''
320 PEM encoded X509 certificate for TLS.
321 You can replace the self-signed certificate that synapse
322 autogenerates on launch with your own SSL certificate + key pair
323 if you like. Any required intermediary certificates can be
324 appended after the primary certificate in hierarchical order.
325 '';
326 };
327
328 tls_private_key_path = mkOption {
329 type = types.nullOr types.str;
330 default = null;
331 example = "/var/lib/acme/example.com/key.pem";
332 description = lib.mdDoc ''
333 PEM encoded private key for TLS. Specify null if synapse is not
334 speaking TLS directly.
335 '';
336 };
337
338 presence.enabled = mkOption {
339 type = types.bool;
340 default = true;
341 example = false;
342 description = lib.mdDoc ''
343 Whether to enable presence tracking.
344
345 Presence tracking allows users to see the state (e.g online/offline)
346 of other local and remote users.
347 '';
348 };
349
350 listeners = mkOption {
351 type = types.listOf (types.submodule {
352 options = {
353 port = mkOption {
354 type = types.port;
355 example = 8448;
356 description = lib.mdDoc ''
357 The port to listen for HTTP(S) requests on.
358 '';
359 };
360
361 bind_addresses = mkOption {
362 type = types.listOf types.str;
363 default = [
364 "::1"
365 "127.0.0.1"
366 ];
367 example = literalExpression ''
368 [
369 "::"
370 "0.0.0.0"
371 ]
372 '';
373 description = lib.mdDoc ''
374 IP addresses to bind the listener to.
375 '';
376 };
377
378 type = mkOption {
379 type = types.enum [
380 "http"
381 "manhole"
382 "metrics"
383 "replication"
384 ];
385 default = "http";
386 example = "metrics";
387 description = lib.mdDoc ''
388 The type of the listener, usually http.
389 '';
390 };
391
392 tls = mkOption {
393 type = types.bool;
394 default = true;
395 example = false;
396 description = lib.mdDoc ''
397 Whether to enable TLS on the listener socket.
398 '';
399 };
400
401 x_forwarded = mkOption {
402 type = types.bool;
403 default = false;
404 example = true;
405 description = lib.mdDoc ''
406 Use the X-Forwarded-For (XFF) header as the client IP and not the
407 actual client IP.
408 '';
409 };
410
411 resources = mkOption {
412 type = types.listOf (types.submodule {
413 options = {
414 names = mkOption {
415 type = types.listOf (types.enum [
416 "client"
417 "consent"
418 "federation"
419 "keys"
420 "media"
421 "metrics"
422 "openid"
423 "replication"
424 "static"
425 ]);
426 description = lib.mdDoc ''
427 List of resources to host on this listener.
428 '';
429 example = [
430 "client"
431 ];
432 };
433 compress = mkOption {
434 type = types.bool;
435 description = lib.mdDoc ''
436 Should synapse compress HTTP responses to clients that support it?
437 This should be disabled if running synapse behind a load balancer
438 that can do automatic compression.
439 '';
440 };
441 };
442 });
443 description = lib.mdDoc ''
444 List of HTTP resources to serve on this listener.
445 '';
446 };
447 };
448 });
449 default = [ {
450 port = 8008;
451 bind_addresses = [ "127.0.0.1" ];
452 type = "http";
453 tls = false;
454 x_forwarded = true;
455 resources = [ {
456 names = [ "client" ];
457 compress = true;
458 } {
459 names = [ "federation" ];
460 compress = false;
461 } ];
462 } ];
463 description = lib.mdDoc ''
464 List of ports that Synapse should listen on, their purpose and their configuration.
465 '';
466 };
467
468 database.name = mkOption {
469 type = types.enum [
470 "sqlite3"
471 "psycopg2"
472 ];
473 default = if versionAtLeast config.system.stateVersion "18.03"
474 then "psycopg2"
475 else "sqlite3";
476 defaultText = literalExpression ''
477 if versionAtLeast config.system.stateVersion "18.03"
478 then "psycopg2"
479 else "sqlite3"
480 '';
481 description = lib.mdDoc ''
482 The database engine name. Can be sqlite3 or psycopg2.
483 '';
484 };
485
486 database.args.database = mkOption {
487 type = types.str;
488 default = {
489 sqlite3 = "${cfg.dataDir}/homeserver.db";
490 psycopg2 = "matrix-synapse";
491 }.${cfg.settings.database.name};
492 defaultText = literalExpression ''
493 {
494 sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db";
495 psycopg2 = "matrix-synapse";
496 }.''${${options.services.matrix-synapse.settings}.database.name};
497 '';
498 description = lib.mdDoc ''
499 Name of the database when using the psycopg2 backend,
500 path to the database location when using sqlite3.
501 '';
502 };
503
504 database.args.user = mkOption {
505 type = types.nullOr types.str;
506 default = {
507 sqlite3 = null;
508 psycopg2 = "matrix-synapse";
509 }.${cfg.settings.database.name};
510 defaultText = lib.literalExpression ''
511 {
512 sqlite3 = null;
513 psycopg2 = "matrix-synapse";
514 }.''${cfg.settings.database.name};
515 '';
516 description = lib.mdDoc ''
517 Username to connect with psycopg2, set to null
518 when using sqlite3.
519 '';
520 };
521
522 url_preview_enabled = mkOption {
523 type = types.bool;
524 default = true;
525 example = false;
526 description = lib.mdDoc ''
527 Is the preview URL API enabled? If enabled, you *must* specify an
528 explicit url_preview_ip_range_blacklist of IPs that the spider is
529 denied from accessing.
530 '';
531 };
532
533 url_preview_ip_range_blacklist = mkOption {
534 type = types.listOf types.str;
535 default = [
536 "10.0.0.0/8"
537 "100.64.0.0/10"
538 "127.0.0.0/8"
539 "169.254.0.0/16"
540 "172.16.0.0/12"
541 "192.0.0.0/24"
542 "192.0.2.0/24"
543 "192.168.0.0/16"
544 "192.88.99.0/24"
545 "198.18.0.0/15"
546 "198.51.100.0/24"
547 "2001:db8::/32"
548 "203.0.113.0/24"
549 "224.0.0.0/4"
550 "::1/128"
551 "fc00::/7"
552 "fe80::/10"
553 "fec0::/10"
554 "ff00::/8"
555 ];
556 description = lib.mdDoc ''
557 List of IP address CIDR ranges that the URL preview spider is denied
558 from accessing.
559 '';
560 };
561
562 url_preview_ip_range_whitelist = mkOption {
563 type = types.listOf types.str;
564 default = [];
565 description = lib.mdDoc ''
566 List of IP address CIDR ranges that the URL preview spider is allowed
567 to access even if they are specified in url_preview_ip_range_blacklist.
568 '';
569 };
570
571 url_preview_url_blacklist = mkOption {
572 type = types.listOf types.str;
573 default = [];
574 description = lib.mdDoc ''
575 Optional list of URL matches that the URL preview spider is
576 denied from accessing.
577 '';
578 };
579
580 max_upload_size = mkOption {
581 type = types.str;
582 default = "50M";
583 example = "100M";
584 description = lib.mdDoc ''
585 The largest allowed upload size in bytes
586 '';
587 };
588
589 max_image_pixels = mkOption {
590 type = types.str;
591 default = "32M";
592 example = "64M";
593 description = lib.mdDoc ''
594 Maximum number of pixels that will be thumbnailed
595 '';
596 };
597
598 dynamic_thumbnails = mkOption {
599 type = types.bool;
600 default = false;
601 example = true;
602 description = lib.mdDoc ''
603 Whether to generate new thumbnails on the fly to precisely match
604 the resolution requested by the client. If true then whenever
605 a new resolution is requested by the client the server will
606 generate a new thumbnail. If false the server will pick a thumbnail
607 from a precalculated list.
608 '';
609 };
610
611 turn_uris = mkOption {
612 type = types.listOf types.str;
613 default = [];
614 example = [
615 "turn:turn.example.com:3487?transport=udp"
616 "turn:turn.example.com:3487?transport=tcp"
617 "turns:turn.example.com:5349?transport=udp"
618 "turns:turn.example.com:5349?transport=tcp"
619 ];
620 description = lib.mdDoc ''
621 The public URIs of the TURN server to give to clients
622 '';
623 };
624 turn_shared_secret = mkOption {
625 type = types.str;
626 default = "";
627 example = literalExpression ''
628 config.services.coturn.static-auth-secret
629 '';
630 description = mdDoc ''
631 The shared secret used to compute passwords for the TURN server.
632
633 Secrets should be passed in via `extraConfigFiles`!
634 '';
635 };
636
637 trusted_key_servers = mkOption {
638 type = types.listOf (types.submodule {
639 options = {
640 server_name = mkOption {
641 type = types.str;
642 example = "matrix.org";
643 description = lib.mdDoc ''
644 Hostname of the trusted server.
645 '';
646 };
647
648 verify_keys = mkOption {
649 type = types.nullOr (types.attrsOf types.str);
650 default = null;
651 example = literalExpression ''
652 {
653 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
654 }
655 '';
656 description = lib.mdDoc ''
657 Attribute set from key id to base64 encoded public key.
658
659 If specified synapse will check that the response is signed
660 by at least one of the given keys.
661 '';
662 };
663 };
664 });
665 default = [ {
666 server_name = "matrix.org";
667 verify_keys = {
668 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
669 };
670 } ];
671 description = lib.mdDoc ''
672 The trusted servers to download signing keys from.
673 '';
674 };
675
676 app_service_config_files = mkOption {
677 type = types.listOf types.path;
678 default = [ ];
679 description = lib.mdDoc ''
680 A list of application service config file to use
681 '';
682 };
683
684 };
685 };
686 };
687
688 extraConfigFiles = mkOption {
689 type = types.listOf types.path;
690 default = [];
691 description = lib.mdDoc ''
692 Extra config files to include.
693
694 The configuration files will be included based on the command line
695 argument --config-path. This allows to configure secrets without
696 having to go through the Nix store, e.g. based on deployment keys if
697 NixOps is in use.
698 '';
699 };
700 };
701 };
702
703 config = mkIf cfg.enable {
704 assertions = [
705 { assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
706 message = ''
707 Cannot deploy matrix-synapse with a configuration for a local postgresql database
708 and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
709 database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
710 further reference).
711
712 If you
713 - try to deploy a fresh synapse, you need to configure the database yourself. An example
714 for this can be found in <nixpkgs/nixos/tests/matrix/synapse.nix>
715 - update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
716 to your configuration.
717
718 For further information about this update, please read the release-notes of 20.03 carefully.
719 '';
720 }
721 ];
722
723 services.matrix-synapse.configFile = configFile;
724
725 users.users.matrix-synapse = {
726 group = "matrix-synapse";
727 home = cfg.dataDir;
728 createHome = true;
729 shell = "${pkgs.bash}/bin/bash";
730 uid = config.ids.uids.matrix-synapse;
731 };
732
733 users.groups.matrix-synapse = {
734 gid = config.ids.gids.matrix-synapse;
735 };
736
737 systemd.services.matrix-synapse = {
738 description = "Synapse Matrix homeserver";
739 after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
740 wantedBy = [ "multi-user.target" ];
741 preStart = ''
742 ${cfg.package}/bin/synapse_homeserver \
743 --config-path ${configFile} \
744 --keys-directory ${cfg.dataDir} \
745 --generate-keys
746 '';
747 environment = {
748 PYTHONPATH = makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ];
749 } // optionalAttrs (cfg.withJemalloc) {
750 LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
751 };
752 serviceConfig = {
753 Type = "notify";
754 User = "matrix-synapse";
755 Group = "matrix-synapse";
756 WorkingDirectory = cfg.dataDir;
757 ExecStartPre = [ ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
758 chown matrix-synapse:matrix-synapse ${cfg.settings.signing_key_path}
759 chmod 0600 ${cfg.settings.signing_key_path}
760 '')) ];
761 ExecStart = ''
762 ${cfg.package}/bin/synapse_homeserver \
763 ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
764 --keys-directory ${cfg.dataDir}
765 '';
766 ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
767 Restart = "on-failure";
768 UMask = "0077";
769
770 # Security Hardening
771 # Refer to systemd.exec(5) for option descriptions.
772 CapabilityBoundingSet = [ "" ];
773 LockPersonality = true;
774 NoNewPrivileges = true;
775 PrivateDevices = true;
776 PrivateTmp = true;
777 PrivateUsers = true;
778 ProcSubset = "pid";
779 ProtectClock = true;
780 ProtectControlGroups = true;
781 ProtectHome = true;
782 ProtectHostname = true;
783 ProtectKernelLogs = true;
784 ProtectKernelModules = true;
785 ProtectKernelTunables = true;
786 ProtectProc = "invisible";
787 ProtectSystem = "strict";
788 ReadWritePaths = [ cfg.dataDir ];
789 RemoveIPC = true;
790 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
791 RestrictNamespaces = true;
792 RestrictRealtime = true;
793 RestrictSUIDSGID = true;
794 SystemCallArchitectures = "native";
795 SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ];
796 };
797 };
798
799 environment.systemPackages = [ registerNewMatrixUser ];
800 };
801
802 meta = {
803 buildDocsInSandbox = false;
804 doc = ./synapse.md;
805 maintainers = teams.matrix.members;
806 };
807
808}