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 settigns
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 description = lib.mdDoc ''
290 The file that holds the logging configuration.
291 '';
292 };
293
294 media_store_path = mkOption {
295 type = types.path;
296 default = if lib.versionAtLeast config.system.stateVersion "22.05"
297 then "${cfg.dataDir}/media_store"
298 else "${cfg.dataDir}/media";
299 defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05";
300 description = lib.mdDoc ''
301 Directory where uploaded images and attachments are stored.
302 '';
303 };
304
305 public_baseurl = mkOption {
306 type = types.nullOr types.str;
307 default = null;
308 example = "https://example.com:8448/";
309 description = lib.mdDoc ''
310 The public-facing base URL for the client API (not including _matrix/...)
311 '';
312 };
313
314 tls_certificate_path = mkOption {
315 type = types.nullOr types.str;
316 default = null;
317 example = "/var/lib/acme/example.com/fullchain.pem";
318 description = lib.mdDoc ''
319 PEM encoded X509 certificate for TLS.
320 You can replace the self-signed certificate that synapse
321 autogenerates on launch with your own SSL certificate + key pair
322 if you like. Any required intermediary certificates can be
323 appended after the primary certificate in hierarchical order.
324 '';
325 };
326
327 tls_private_key_path = mkOption {
328 type = types.nullOr types.str;
329 default = null;
330 example = "/var/lib/acme/example.com/key.pem";
331 description = lib.mdDoc ''
332 PEM encoded private key for TLS. Specify null if synapse is not
333 speaking TLS directly.
334 '';
335 };
336
337 presence.enabled = mkOption {
338 type = types.bool;
339 default = true;
340 example = false;
341 description = lib.mdDoc ''
342 Whether to enable presence tracking.
343
344 Presence tracking allows users to see the state (e.g online/offline)
345 of other local and remote users.
346 '';
347 };
348
349 listeners = mkOption {
350 type = types.listOf (types.submodule {
351 options = {
352 port = mkOption {
353 type = types.port;
354 example = 8448;
355 description = lib.mdDoc ''
356 The port to listen for HTTP(S) requests on.
357 '';
358 };
359
360 bind_addresses = mkOption {
361 type = types.listOf types.str;
362 default = [
363 "::1"
364 "127.0.0.1"
365 ];
366 example = literalExpression ''
367 [
368 "::"
369 "0.0.0.0"
370 ]
371 '';
372 description = lib.mdDoc ''
373 IP addresses to bind the listener to.
374 '';
375 };
376
377 type = mkOption {
378 type = types.enum [
379 "http"
380 "manhole"
381 "metrics"
382 "replication"
383 ];
384 default = "http";
385 example = "metrics";
386 description = lib.mdDoc ''
387 The type of the listener, usually http.
388 '';
389 };
390
391 tls = mkOption {
392 type = types.bool;
393 default = true;
394 example = false;
395 description = lib.mdDoc ''
396 Whether to enable TLS on the listener socket.
397 '';
398 };
399
400 x_forwarded = mkOption {
401 type = types.bool;
402 default = false;
403 example = true;
404 description = lib.mdDoc ''
405 Use the X-Forwarded-For (XFF) header as the client IP and not the
406 actual client IP.
407 '';
408 };
409
410 resources = mkOption {
411 type = types.listOf (types.submodule {
412 options = {
413 names = mkOption {
414 type = types.listOf (types.enum [
415 "client"
416 "consent"
417 "federation"
418 "keys"
419 "media"
420 "metrics"
421 "openid"
422 "replication"
423 "static"
424 ]);
425 description = lib.mdDoc ''
426 List of resources to host on this listener.
427 '';
428 example = [
429 "client"
430 ];
431 };
432 compress = mkOption {
433 type = types.bool;
434 description = lib.mdDoc ''
435 Should synapse compress HTTP responses to clients that support it?
436 This should be disabled if running synapse behind a load balancer
437 that can do automatic compression.
438 '';
439 };
440 };
441 });
442 description = lib.mdDoc ''
443 List of HTTP resources to serve on this listener.
444 '';
445 };
446 };
447 });
448 default = [ {
449 port = 8008;
450 bind_addresses = [ "127.0.0.1" ];
451 type = "http";
452 tls = false;
453 x_forwarded = true;
454 resources = [ {
455 names = [ "client" ];
456 compress = true;
457 } {
458 names = [ "federation" ];
459 compress = false;
460 } ];
461 } ];
462 description = lib.mdDoc ''
463 List of ports that Synapse should listen on, their purpose and their configuration.
464 '';
465 };
466
467 database.name = mkOption {
468 type = types.enum [
469 "sqlite3"
470 "psycopg2"
471 ];
472 default = if versionAtLeast config.system.stateVersion "18.03"
473 then "psycopg2"
474 else "sqlite3";
475 defaultText = literalExpression ''
476 if versionAtLeast config.system.stateVersion "18.03"
477 then "psycopg2"
478 else "sqlite3"
479 '';
480 description = lib.mdDoc ''
481 The database engine name. Can be sqlite3 or psycopg2.
482 '';
483 };
484
485 database.args.database = mkOption {
486 type = types.str;
487 default = {
488 sqlite3 = "${cfg.dataDir}/homeserver.db";
489 psycopg2 = "matrix-synapse";
490 }.${cfg.settings.database.name};
491 defaultText = literalExpression ''
492 {
493 sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db";
494 psycopg2 = "matrix-synapse";
495 }.''${${options.services.matrix-synapse.settings}.database.name};
496 '';
497 description = lib.mdDoc ''
498 Name of the database when using the psycopg2 backend,
499 path to the database location when using sqlite3.
500 '';
501 };
502
503 database.args.user = mkOption {
504 type = types.nullOr types.str;
505 default = {
506 sqlite3 = null;
507 psycopg2 = "matrix-synapse";
508 }.${cfg.settings.database.name};
509 description = lib.mdDoc ''
510 Username to connect with psycopg2, set to null
511 when using sqlite3.
512 '';
513 };
514
515 url_preview_enabled = mkOption {
516 type = types.bool;
517 default = true;
518 example = false;
519 description = lib.mdDoc ''
520 Is the preview URL API enabled? If enabled, you *must* specify an
521 explicit url_preview_ip_range_blacklist of IPs that the spider is
522 denied from accessing.
523 '';
524 };
525
526 url_preview_ip_range_blacklist = mkOption {
527 type = types.listOf types.str;
528 default = [
529 "10.0.0.0/8"
530 "100.64.0.0/10"
531 "127.0.0.0/8"
532 "169.254.0.0/16"
533 "172.16.0.0/12"
534 "192.0.0.0/24"
535 "192.0.2.0/24"
536 "192.168.0.0/16"
537 "192.88.99.0/24"
538 "198.18.0.0/15"
539 "198.51.100.0/24"
540 "2001:db8::/32"
541 "203.0.113.0/24"
542 "224.0.0.0/4"
543 "::1/128"
544 "fc00::/7"
545 "fe80::/10"
546 "fec0::/10"
547 "ff00::/8"
548 ];
549 description = lib.mdDoc ''
550 List of IP address CIDR ranges that the URL preview spider is denied
551 from accessing.
552 '';
553 };
554
555 url_preview_ip_range_whitelist = mkOption {
556 type = types.listOf types.str;
557 default = [];
558 description = lib.mdDoc ''
559 List of IP address CIDR ranges that the URL preview spider is allowed
560 to access even if they are specified in url_preview_ip_range_blacklist.
561 '';
562 };
563
564 url_preview_url_blacklist = mkOption {
565 type = types.listOf types.str;
566 default = [];
567 description = lib.mdDoc ''
568 Optional list of URL matches that the URL preview spider is
569 denied from accessing.
570 '';
571 };
572
573 max_upload_size = mkOption {
574 type = types.str;
575 default = "50M";
576 example = "100M";
577 description = lib.mdDoc ''
578 The largest allowed upload size in bytes
579 '';
580 };
581
582 max_image_pixels = mkOption {
583 type = types.str;
584 default = "32M";
585 example = "64M";
586 description = lib.mdDoc ''
587 Maximum number of pixels that will be thumbnailed
588 '';
589 };
590
591 dynamic_thumbnails = mkOption {
592 type = types.bool;
593 default = false;
594 example = true;
595 description = lib.mdDoc ''
596 Whether to generate new thumbnails on the fly to precisely match
597 the resolution requested by the client. If true then whenever
598 a new resolution is requested by the client the server will
599 generate a new thumbnail. If false the server will pick a thumbnail
600 from a precalculated list.
601 '';
602 };
603
604 turn_uris = mkOption {
605 type = types.listOf types.str;
606 default = [];
607 example = [
608 "turn:turn.example.com:3487?transport=udp"
609 "turn:turn.example.com:3487?transport=tcp"
610 "turns:turn.example.com:5349?transport=udp"
611 "turns:turn.example.com:5349?transport=tcp"
612 ];
613 description = lib.mdDoc ''
614 The public URIs of the TURN server to give to clients
615 '';
616 };
617 turn_shared_secret = mkOption {
618 type = types.str;
619 default = "";
620 example = literalExpression ''
621 config.services.coturn.static-auth-secret
622 '';
623 description = mdDoc ''
624 The shared secret used to compute passwords for the TURN server.
625
626 Secrets should be passed in via `extraConfigFiles`!
627 '';
628 };
629
630 trusted_key_servers = mkOption {
631 type = types.listOf (types.submodule {
632 options = {
633 server_name = mkOption {
634 type = types.str;
635 example = "matrix.org";
636 description = lib.mdDoc ''
637 Hostname of the trusted server.
638 '';
639 };
640
641 verify_keys = mkOption {
642 type = types.nullOr (types.attrsOf types.str);
643 default = null;
644 example = literalExpression ''
645 {
646 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
647 }
648 '';
649 description = lib.mdDoc ''
650 Attribute set from key id to base64 encoded public key.
651
652 If specified synapse will check that the response is signed
653 by at least one of the given keys.
654 '';
655 };
656 };
657 });
658 default = [ {
659 server_name = "matrix.org";
660 verify_keys = {
661 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
662 };
663 } ];
664 description = lib.mdDoc ''
665 The trusted servers to download signing keys from.
666 '';
667 };
668
669 app_service_config_files = mkOption {
670 type = types.listOf types.path;
671 default = [ ];
672 description = lib.mdDoc ''
673 A list of application service config file to use
674 '';
675 };
676
677 };
678 };
679 };
680
681 extraConfigFiles = mkOption {
682 type = types.listOf types.path;
683 default = [];
684 description = lib.mdDoc ''
685 Extra config files to include.
686
687 The configuration files will be included based on the command line
688 argument --config-path. This allows to configure secrets without
689 having to go through the Nix store, e.g. based on deployment keys if
690 NixOps is in use.
691 '';
692 };
693 };
694 };
695
696 config = mkIf cfg.enable {
697 assertions = [
698 { assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
699 message = ''
700 Cannot deploy matrix-synapse with a configuration for a local postgresql database
701 and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
702 database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
703 further reference).
704
705 If you
706 - try to deploy a fresh synapse, you need to configure the database yourself. An example
707 for this can be found in <nixpkgs/nixos/tests/matrix-synapse.nix>
708 - update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
709 to your configuration.
710
711 For further information about this update, please read the release-notes of 20.03 carefully.
712 '';
713 }
714 ];
715
716 services.matrix-synapse.configFile = configFile;
717
718 users.users.matrix-synapse = {
719 group = "matrix-synapse";
720 home = cfg.dataDir;
721 createHome = true;
722 shell = "${pkgs.bash}/bin/bash";
723 uid = config.ids.uids.matrix-synapse;
724 };
725
726 users.groups.matrix-synapse = {
727 gid = config.ids.gids.matrix-synapse;
728 };
729
730 systemd.services.matrix-synapse = {
731 description = "Synapse Matrix homeserver";
732 after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
733 wantedBy = [ "multi-user.target" ];
734 preStart = ''
735 ${cfg.package}/bin/synapse_homeserver \
736 --config-path ${configFile} \
737 --keys-directory ${cfg.dataDir} \
738 --generate-keys
739 '';
740 environment = {
741 PYTHONPATH = makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ];
742 } // optionalAttrs (cfg.withJemalloc) {
743 LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
744 };
745 serviceConfig = {
746 Type = "notify";
747 User = "matrix-synapse";
748 Group = "matrix-synapse";
749 WorkingDirectory = cfg.dataDir;
750 ExecStartPre = [ ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
751 chown matrix-synapse:matrix-synapse ${cfg.dataDir}/homeserver.signing.key
752 chmod 0600 ${cfg.dataDir}/homeserver.signing.key
753 '')) ];
754 ExecStart = ''
755 ${cfg.package}/bin/synapse_homeserver \
756 ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
757 --keys-directory ${cfg.dataDir}
758 '';
759 ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
760 Restart = "on-failure";
761 UMask = "0077";
762
763 # Security Hardening
764 # Refer to systemd.exec(5) for option descriptions.
765 CapabilityBoundingSet = [ "" ];
766 LockPersonality = true;
767 NoNewPrivileges = true;
768 PrivateDevices = true;
769 PrivateTmp = true;
770 PrivateUsers = true;
771 ProcSubset = "pid";
772 ProtectClock = true;
773 ProtectControlGroups = true;
774 ProtectHome = true;
775 ProtectHostname = true;
776 ProtectKernelLogs = true;
777 ProtectKernelModules = true;
778 ProtectKernelTunables = true;
779 ProtectProc = "invisible";
780 ProtectSystem = "strict";
781 ReadWritePaths = [ cfg.dataDir ];
782 RemoveIPC = true;
783 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
784 RestrictNamespaces = true;
785 RestrictRealtime = true;
786 RestrictSUIDSGID = true;
787 SystemCallArchitectures = "native";
788 SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ];
789 };
790 };
791
792 environment.systemPackages = [ registerNewMatrixUser ];
793 };
794
795 meta = {
796 buildDocsInSandbox = false;
797 doc = ./synapse.xml;
798 maintainers = teams.matrix.members;
799 };
800
801}