at 23.11-beta 49 kB view raw
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 13 usePostgresql = cfg.settings.database.name == "psycopg2"; 14 hasLocalPostgresDB = let args = cfg.settings.database.args; in 15 usePostgresql 16 && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ])) 17 && config.services.postgresql.enable; 18 hasWorkers = cfg.workers != { }; 19 20 listenerSupportsResource = resource: listener: 21 lib.any ({ names, ... }: builtins.elem resource names) listener.resources; 22 23 clientListener = findFirst 24 (listenerSupportsResource "client") 25 null 26 (cfg.settings.listeners 27 ++ concatMap ({ worker_listeners, ... }: worker_listeners) (attrValues cfg.workers)); 28 29 registerNewMatrixUser = 30 let 31 isIpv6 = hasInfix ":"; 32 33 # add a tail, so that without any bind_addresses we still have a useable address 34 bindAddress = head (clientListener.bind_addresses ++ [ "127.0.0.1" ]); 35 listenerProtocol = if clientListener.tls 36 then "https" 37 else "http"; 38 in 39 assert assertMsg (clientListener != null) "No client listener found in synapse or one of its workers"; 40 pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" '' 41 exec ${cfg.package}/bin/register_new_matrix_user \ 42 $@ \ 43 ${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \ 44 "${listenerProtocol}://${ 45 if (isIpv6 bindAddress) then 46 "[${bindAddress}]" 47 else 48 "${bindAddress}" 49 }:${builtins.toString clientListener.port}/" 50 ''; 51 52 defaultExtras = [ 53 "systemd" 54 "postgres" 55 "url-preview" 56 "user-search" 57 ]; 58 59 wantedExtras = cfg.extras 60 ++ lib.optional (cfg.settings ? oidc_providers) "oidc" 61 ++ lib.optional (cfg.settings ? jwt_config) "jwt" 62 ++ lib.optional (cfg.settings ? saml2_config) "saml2" 63 ++ lib.optional (cfg.settings ? redis) "redis" 64 ++ lib.optional (cfg.settings ? sentry) "sentry" 65 ++ lib.optional (cfg.settings ? user_directory) "user-search" 66 ++ lib.optional (cfg.settings.url_preview_enabled) "url-preview" 67 ++ lib.optional (cfg.settings.database.name == "psycopg2") "postgres"; 68 69 wrapped = pkgs.matrix-synapse.override { 70 extras = wantedExtras; 71 inherit (cfg) plugins; 72 }; 73 74 defaultCommonLogConfig = { 75 version = 1; 76 formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; 77 handlers.journal = { 78 class = "systemd.journal.JournalHandler"; 79 formatter = "journal_fmt"; 80 }; 81 root = { 82 level = "INFO"; 83 handlers = [ "journal" ]; 84 }; 85 disable_existing_loggers = false; 86 }; 87 88 defaultCommonLogConfigText = generators.toPretty { } defaultCommonLogConfig; 89 90 logConfigText = logName: 91 lib.literalMD '' 92 Path to a yaml file generated from this Nix expression: 93 94 ``` 95 ${generators.toPretty { } ( 96 recursiveUpdate defaultCommonLogConfig { handlers.journal.SYSLOG_IDENTIFIER = logName; } 97 )} 98 ``` 99 ''; 100 101 genLogConfigFile = logName: format.generate 102 "synapse-log-${logName}.yaml" 103 (cfg.log // optionalAttrs (cfg.log?handlers.journal) { 104 handlers.journal = cfg.log.handlers.journal // { 105 SYSLOG_IDENTIFIER = logName; 106 }; 107 }); 108in { 109 110 imports = [ 111 112 (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] '' 113 The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0 114 as the behavior is now obsolete. 115 '') 116 (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] '' 117 Database configuration must be done manually. An exemplary setup is demonstrated in 118 <nixpkgs/nixos/tests/matrix/synapse.nix> 119 '') 120 (mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "") 121 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] '' 122 You may add additional event types via 123 `services.matrix-synapse.room_prejoin_state.additional_event_types` and 124 disable the default events via 125 `services.matrix-synapse.room_prejoin_state.disable_default_event_types`. 126 '') 127 128 # options that don't exist in synapse anymore 129 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_host" ] "Use listener settings instead." ) 130 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_port" ] "Use listener settings instead." ) 131 (mkRemovedOptionModule [ "services" "matrix-synapse" "expire_access_tokens" ] "" ) 132 (mkRemovedOptionModule [ "services" "matrix-synapse" "no_tls" ] "It is no longer supported by synapse." ) 133 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_dh_param_path" ] "It was removed from synapse." ) 134 (mkRemovedOptionModule [ "services" "matrix-synapse" "unsecure_port" ] "Use settings.listeners instead." ) 135 (mkRemovedOptionModule [ "services" "matrix-synapse" "user_creation_max_duration" ] "It is no longer supported by synapse." ) 136 (mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead." ) 137 138 # options that were moved into rfc42 style settings 139 (mkRemovedOptionModule [ "services" "matrix-synapse" "app_service_config_files" ] "Use settings.app_service_config_files instead" ) 140 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_args" ] "Use settings.database.args instead" ) 141 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_name" ] "Use settings.database.args.database instead" ) 142 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_type" ] "Use settings.database.name instead" ) 143 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_user" ] "Use settings.database.args.user instead" ) 144 (mkRemovedOptionModule [ "services" "matrix-synapse" "dynamic_thumbnails" ] "Use settings.dynamic_thumbnails instead" ) 145 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_metrics" ] "Use settings.enable_metrics instead" ) 146 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration" ] "Use settings.enable_registration instead" ) 147 (mkRemovedOptionModule [ "services" "matrix-synapse" "extraConfig" ] "Use settings instead." ) 148 (mkRemovedOptionModule [ "services" "matrix-synapse" "listeners" ] "Use settings.listeners instead" ) 149 (mkRemovedOptionModule [ "services" "matrix-synapse" "logConfig" ] "Use settings.log_config instead" ) 150 (mkRemovedOptionModule [ "services" "matrix-synapse" "max_image_pixels" ] "Use settings.max_image_pixels instead" ) 151 (mkRemovedOptionModule [ "services" "matrix-synapse" "max_upload_size" ] "Use settings.max_upload_size instead" ) 152 (mkRemovedOptionModule [ "services" "matrix-synapse" "presence" "enabled" ] "Use settings.presence.enabled instead" ) 153 (mkRemovedOptionModule [ "services" "matrix-synapse" "public_baseurl" ] "Use settings.public_baseurl instead" ) 154 (mkRemovedOptionModule [ "services" "matrix-synapse" "report_stats" ] "Use settings.report_stats instead" ) 155 (mkRemovedOptionModule [ "services" "matrix-synapse" "server_name" ] "Use settings.server_name instead" ) 156 (mkRemovedOptionModule [ "services" "matrix-synapse" "servers" ] "Use settings.trusted_key_servers instead." ) 157 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_certificate_path" ] "Use settings.tls_certificate_path instead" ) 158 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_private_key_path" ] "Use settings.tls_private_key_path instead" ) 159 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_shared_secret" ] "Use settings.turn_shared_secret instead" ) 160 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_uris" ] "Use settings.turn_uris instead" ) 161 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_user_lifetime" ] "Use settings.turn_user_lifetime instead" ) 162 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_enabled" ] "Use settings.url_preview_enabled instead" ) 163 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_blacklist" ] "Use settings.url_preview_ip_range_blacklist instead" ) 164 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_whitelist" ] "Use settings.url_preview_ip_range_whitelist instead" ) 165 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_url_blacklist" ] "Use settings.url_preview_url_blacklist instead" ) 166 167 # options that are too specific to mention them explicitly in settings 168 (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "email" ] "Use settings.account_threepid_delegates.email instead" ) 169 (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "msisdn" ] "Use settings.account_threepid_delegates.msisdn instead" ) 170 (mkRemovedOptionModule [ "services" "matrix-synapse" "allow_guest_access" ] "Use settings.allow_guest_access instead" ) 171 (mkRemovedOptionModule [ "services" "matrix-synapse" "bcrypt_rounds" ] "Use settings.bcrypt_rounds instead" ) 172 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration_captcha" ] "Use settings.enable_registration_captcha instead" ) 173 (mkRemovedOptionModule [ "services" "matrix-synapse" "event_cache_size" ] "Use settings.event_cache_size instead" ) 174 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_concurrent" ] "Use settings.rc_federation.concurrent instead" ) 175 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_reject_limit" ] "Use settings.rc_federation.reject_limit instead" ) 176 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_delay" ] "Use settings.rc_federation.sleep_delay instead" ) 177 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_limit" ] "Use settings.rc_federation.sleep_limit instead" ) 178 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_window_size" ] "Use settings.rc_federation.window_size instead" ) 179 (mkRemovedOptionModule [ "services" "matrix-synapse" "key_refresh_interval" ] "Use settings.key_refresh_interval instead" ) 180 (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_burst_count" ] "Use settings.rc_messages.burst_count instead" ) 181 (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_per_second" ] "Use settings.rc_messages.per_second instead" ) 182 (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_private_key" ] "Use settings.recaptcha_private_key instead" ) 183 (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_public_key" ] "Use settings.recaptcha_public_key instead" ) 184 (mkRemovedOptionModule [ "services" "matrix-synapse" "redaction_retention_period" ] "Use settings.redaction_retention_period instead" ) 185 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "additional_event_types" ] "Use settings.room_prejoin_state.additional_event_types instead" ) 186 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "disable_default_event_types" ] "Use settings.room_prejoin-state.disable_default_event_types instead" ) 187 188 # Options that should be passed via extraConfigFiles, so they are not persisted into the nix store 189 (mkRemovedOptionModule [ "services" "matrix-synapse" "macaroon_secret_key" ] "Pass this value via extraConfigFiles instead" ) 190 (mkRemovedOptionModule [ "services" "matrix-synapse" "registration_shared_secret" ] "Pass this value via extraConfigFiles instead" ) 191 192 ]; 193 194 options = let 195 listenerType = workerContext: types.submodule { 196 options = { 197 port = mkOption { 198 type = types.port; 199 example = 8448; 200 description = lib.mdDoc '' 201 The port to listen for HTTP(S) requests on. 202 ''; 203 }; 204 205 bind_addresses = mkOption { 206 type = types.listOf types.str; 207 default = [ 208 "::1" 209 "127.0.0.1" 210 ]; 211 example = literalExpression '' 212 [ 213 "::" 214 "0.0.0.0" 215 ] 216 ''; 217 description = lib.mdDoc '' 218 IP addresses to bind the listener to. 219 ''; 220 }; 221 222 type = mkOption { 223 type = types.enum [ 224 "http" 225 "manhole" 226 "metrics" 227 "replication" 228 ]; 229 default = "http"; 230 example = "metrics"; 231 description = lib.mdDoc '' 232 The type of the listener, usually http. 233 ''; 234 }; 235 236 tls = mkOption { 237 type = types.bool; 238 default = !workerContext; 239 example = false; 240 description = lib.mdDoc '' 241 Whether to enable TLS on the listener socket. 242 ''; 243 }; 244 245 x_forwarded = mkOption { 246 type = types.bool; 247 default = false; 248 example = true; 249 description = lib.mdDoc '' 250 Use the X-Forwarded-For (XFF) header as the client IP and not the 251 actual client IP. 252 ''; 253 }; 254 255 resources = mkOption { 256 type = types.listOf (types.submodule { 257 options = { 258 names = mkOption { 259 type = types.listOf (types.enum [ 260 "client" 261 "consent" 262 "federation" 263 "health" 264 "keys" 265 "media" 266 "metrics" 267 "openid" 268 "replication" 269 "static" 270 ]); 271 description = lib.mdDoc '' 272 List of resources to host on this listener. 273 ''; 274 example = [ 275 "client" 276 ]; 277 }; 278 compress = mkOption { 279 default = false; 280 type = types.bool; 281 description = lib.mdDoc '' 282 Whether synapse should compress HTTP responses to clients that support it. 283 This should be disabled if running synapse behind a load balancer 284 that can do automatic compression. 285 ''; 286 }; 287 }; 288 }); 289 description = lib.mdDoc '' 290 List of HTTP resources to serve on this listener. 291 ''; 292 }; 293 }; 294 }; 295 in { 296 services.matrix-synapse = { 297 enable = mkEnableOption (lib.mdDoc "matrix.org synapse"); 298 299 serviceUnit = lib.mkOption { 300 type = lib.types.str; 301 readOnly = true; 302 description = lib.mdDoc '' 303 The systemd unit (a service or a target) for other services to depend on if they 304 need to be started after matrix-synapse. 305 306 This option is useful as the actual parent unit for all matrix-synapse processes 307 changes when configuring workers. 308 ''; 309 }; 310 311 configFile = mkOption { 312 type = types.path; 313 readOnly = true; 314 description = lib.mdDoc '' 315 Path to the configuration file on the target system. Useful to configure e.g. workers 316 that also need this. 317 ''; 318 }; 319 320 package = mkOption { 321 type = types.package; 322 readOnly = true; 323 description = lib.mdDoc '' 324 Reference to the `matrix-synapse` wrapper with all extras 325 (e.g. for `oidc` or `saml2`) added to the `PYTHONPATH` of all executables. 326 327 This option is useful to reference the "final" `matrix-synapse` package that's 328 actually used by `matrix-synapse.service`. For instance, when using 329 workers, it's possible to run 330 `''${config.services.matrix-synapse.package}/bin/synapse_worker` and 331 no additional PYTHONPATH needs to be specified for extras or plugins configured 332 via `services.matrix-synapse`. 333 334 However, this means that this option is supposed to be only declared 335 by the `services.matrix-synapse` module itself and is thus read-only. 336 In order to modify `matrix-synapse` itself, use an overlay to override 337 `pkgs.matrix-synapse-unwrapped`. 338 ''; 339 }; 340 341 extras = mkOption { 342 type = types.listOf (types.enum (lib.attrNames pkgs.matrix-synapse-unwrapped.optional-dependencies)); 343 default = defaultExtras; 344 example = literalExpression '' 345 [ 346 "cache-memory" # Provide statistics about caching memory consumption 347 "jwt" # JSON Web Token authentication 348 "oidc" # OpenID Connect authentication 349 "postgres" # PostgreSQL database backend 350 "redis" # Redis support for the replication stream between worker processes 351 "saml2" # SAML2 authentication 352 "sentry" # Error tracking and performance metrics 353 "systemd" # Provide the JournalHandler used in the default log_config 354 "url-preview" # Support for oEmbed URL previews 355 "user-search" # Support internationalized domain names in user-search 356 ] 357 ''; 358 description = lib.mdDoc '' 359 Explicitly install extras provided by matrix-synapse. Most 360 will require some additional configuration. 361 362 Extras will automatically be enabled, when the relevant 363 configuration sections are present. 364 365 Please note that this option is additive: i.e. when adding a new item 366 to this list, the defaults are still kept. To override the defaults as well, 367 use `lib.mkForce`. 368 ''; 369 }; 370 371 plugins = mkOption { 372 type = types.listOf types.package; 373 default = [ ]; 374 example = literalExpression '' 375 with config.services.matrix-synapse.package.plugins; [ 376 matrix-synapse-ldap3 377 matrix-synapse-pam 378 ]; 379 ''; 380 description = lib.mdDoc '' 381 List of additional Matrix plugins to make available. 382 ''; 383 }; 384 385 withJemalloc = mkOption { 386 type = types.bool; 387 default = false; 388 description = lib.mdDoc '' 389 Whether to preload jemalloc to reduce memory fragmentation and overall usage. 390 ''; 391 }; 392 393 dataDir = mkOption { 394 type = types.str; 395 default = "/var/lib/matrix-synapse"; 396 description = lib.mdDoc '' 397 The directory where matrix-synapse stores its stateful data such as 398 certificates, media and uploads. 399 ''; 400 }; 401 402 log = mkOption { 403 type = types.attrsOf format.type; 404 defaultText = literalExpression defaultCommonLogConfigText; 405 description = mdDoc '' 406 Default configuration for the loggers used by `matrix-synapse` and its workers. 407 The defaults are added with the default priority which means that 408 these will be merged with additional declarations. These additional 409 declarations also take precedence over the defaults when declared 410 with at least normal priority. For instance 411 the log-level for synapse and its workers can be changed like this: 412 413 ```nix 414 { lib, ... }: { 415 services.matrix-synapse.log.root.level = "WARNING"; 416 } 417 ``` 418 419 And another field can be added like this: 420 421 ```nix 422 { 423 services.matrix-synapse.log = { 424 loggers."synapse.http.matrixfederationclient".level = "DEBUG"; 425 }; 426 } 427 ``` 428 429 Additionally, the field `handlers.journal.SYSLOG_IDENTIFIER` will be added to 430 each log config, i.e. 431 * `synapse` for `matrix-synapse.service` 432 * `synapse-<worker name>` for `matrix-synapse-worker-<worker name>.service` 433 434 This is only done if this option has a `handlers.journal` field declared. 435 436 To discard all settings declared by this option for each worker and synapse, 437 `lib.mkForce` can be used. 438 439 To discard all settings declared by this option for a single worker or synapse only, 440 [](#opt-services.matrix-synapse.workers._name_.worker_log_config) or 441 [](#opt-services.matrix-synapse.settings.log_config) can be used. 442 ''; 443 }; 444 445 settings = mkOption { 446 default = { }; 447 description = mdDoc '' 448 The primary synapse configuration. See the 449 [sample configuration](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_config.yaml) 450 for possible values. 451 452 Secrets should be passed in by using the `extraConfigFiles` option. 453 ''; 454 type = with types; submodule { 455 freeformType = format.type; 456 options = { 457 # This is a reduced set of popular options and defaults 458 # Do not add every available option here, they can be specified 459 # by the user at their own discretion. This is a freeform type! 460 461 server_name = mkOption { 462 type = types.str; 463 example = "example.com"; 464 default = config.networking.hostName; 465 defaultText = literalExpression "config.networking.hostName"; 466 description = lib.mdDoc '' 467 The domain name of the server, with optional explicit port. 468 This is used by remote servers to look up the server address. 469 This is also the last part of your UserID. 470 471 The server_name cannot be changed later so it is important to configure this correctly before you start Synapse. 472 ''; 473 }; 474 475 enable_registration = mkOption { 476 type = types.bool; 477 default = false; 478 description = lib.mdDoc '' 479 Enable registration for new users. 480 ''; 481 }; 482 483 registration_shared_secret = mkOption { 484 type = types.nullOr types.str; 485 default = null; 486 description = mdDoc '' 487 If set, allows registration by anyone who also has the shared 488 secret, even if registration is otherwise disabled. 489 490 Secrets should be passed in via `extraConfigFiles`! 491 ''; 492 }; 493 494 macaroon_secret_key = mkOption { 495 type = types.nullOr types.str; 496 default = null; 497 description = mdDoc '' 498 Secret key for authentication tokens. If none is specified, 499 the registration_shared_secret is used, if one is given; otherwise, 500 a secret key is derived from the signing key. 501 502 Secrets should be passed in via `extraConfigFiles`! 503 ''; 504 }; 505 506 enable_metrics = mkOption { 507 type = types.bool; 508 default = false; 509 description = lib.mdDoc '' 510 Enable collection and rendering of performance metrics 511 ''; 512 }; 513 514 report_stats = mkOption { 515 type = types.bool; 516 default = false; 517 description = lib.mdDoc '' 518 Whether or not to report anonymized homeserver usage statistics. 519 ''; 520 }; 521 522 signing_key_path = mkOption { 523 type = types.path; 524 default = "${cfg.dataDir}/homeserver.signing.key"; 525 description = lib.mdDoc '' 526 Path to the signing key to sign messages with. 527 ''; 528 }; 529 530 pid_file = mkOption { 531 type = types.path; 532 default = "/run/matrix-synapse.pid"; 533 readOnly = true; 534 description = lib.mdDoc '' 535 The file to store the PID in. 536 ''; 537 }; 538 539 log_config = mkOption { 540 type = types.path; 541 default = genLogConfigFile "synapse"; 542 defaultText = logConfigText "synapse"; 543 description = lib.mdDoc '' 544 The file that holds the logging configuration. 545 ''; 546 }; 547 548 media_store_path = mkOption { 549 type = types.path; 550 default = if lib.versionAtLeast config.system.stateVersion "22.05" 551 then "${cfg.dataDir}/media_store" 552 else "${cfg.dataDir}/media"; 553 defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05"; 554 description = lib.mdDoc '' 555 Directory where uploaded images and attachments are stored. 556 ''; 557 }; 558 559 public_baseurl = mkOption { 560 type = types.nullOr types.str; 561 default = null; 562 example = "https://example.com:8448/"; 563 description = lib.mdDoc '' 564 The public-facing base URL for the client API (not including _matrix/...) 565 ''; 566 }; 567 568 tls_certificate_path = mkOption { 569 type = types.nullOr types.str; 570 default = null; 571 example = "/var/lib/acme/example.com/fullchain.pem"; 572 description = lib.mdDoc '' 573 PEM encoded X509 certificate for TLS. 574 You can replace the self-signed certificate that synapse 575 autogenerates on launch with your own SSL certificate + key pair 576 if you like. Any required intermediary certificates can be 577 appended after the primary certificate in hierarchical order. 578 ''; 579 }; 580 581 tls_private_key_path = mkOption { 582 type = types.nullOr types.str; 583 default = null; 584 example = "/var/lib/acme/example.com/key.pem"; 585 description = lib.mdDoc '' 586 PEM encoded private key for TLS. Specify null if synapse is not 587 speaking TLS directly. 588 ''; 589 }; 590 591 presence.enabled = mkOption { 592 type = types.bool; 593 default = true; 594 example = false; 595 description = lib.mdDoc '' 596 Whether to enable presence tracking. 597 598 Presence tracking allows users to see the state (e.g online/offline) 599 of other local and remote users. 600 ''; 601 }; 602 603 listeners = mkOption { 604 type = types.listOf (listenerType false); 605 default = [{ 606 port = 8008; 607 bind_addresses = [ "127.0.0.1" ]; 608 type = "http"; 609 tls = false; 610 x_forwarded = true; 611 resources = [{ 612 names = [ "client" ]; 613 compress = true; 614 } { 615 names = [ "federation" ]; 616 compress = false; 617 }]; 618 }] ++ lib.optional hasWorkers { 619 port = 9093; 620 bind_addresses = [ "127.0.0.1" ]; 621 type = "http"; 622 tls = false; 623 x_forwarded = false; 624 resources = [{ 625 names = [ "replication" ]; 626 compress = false; 627 }]; 628 }; 629 description = lib.mdDoc '' 630 List of ports that Synapse should listen on, their purpose and their configuration. 631 632 By default, synapse will be configured for client and federation traffic on port 8008, and 633 for worker replication traffic on port 9093. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers) 634 for more details. 635 ''; 636 }; 637 638 database.name = mkOption { 639 type = types.enum [ 640 "sqlite3" 641 "psycopg2" 642 ]; 643 default = if versionAtLeast config.system.stateVersion "18.03" 644 then "psycopg2" 645 else "sqlite3"; 646 defaultText = literalExpression '' 647 if versionAtLeast config.system.stateVersion "18.03" 648 then "psycopg2" 649 else "sqlite3" 650 ''; 651 description = lib.mdDoc '' 652 The database engine name. Can be sqlite3 or psycopg2. 653 ''; 654 }; 655 656 database.args.database = mkOption { 657 type = types.str; 658 default = { 659 sqlite3 = "${cfg.dataDir}/homeserver.db"; 660 psycopg2 = "matrix-synapse"; 661 }.${cfg.settings.database.name}; 662 defaultText = literalExpression '' 663 { 664 sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db"; 665 psycopg2 = "matrix-synapse"; 666 }.''${${options.services.matrix-synapse.settings}.database.name}; 667 ''; 668 description = lib.mdDoc '' 669 Name of the database when using the psycopg2 backend, 670 path to the database location when using sqlite3. 671 ''; 672 }; 673 674 database.args.user = mkOption { 675 type = types.nullOr types.str; 676 default = { 677 sqlite3 = null; 678 psycopg2 = "matrix-synapse"; 679 }.${cfg.settings.database.name}; 680 defaultText = lib.literalExpression '' 681 { 682 sqlite3 = null; 683 psycopg2 = "matrix-synapse"; 684 }.''${cfg.settings.database.name}; 685 ''; 686 description = lib.mdDoc '' 687 Username to connect with psycopg2, set to null 688 when using sqlite3. 689 ''; 690 }; 691 692 url_preview_enabled = mkOption { 693 type = types.bool; 694 default = true; 695 example = false; 696 description = lib.mdDoc '' 697 Is the preview URL API enabled? If enabled, you *must* specify an 698 explicit url_preview_ip_range_blacklist of IPs that the spider is 699 denied from accessing. 700 ''; 701 }; 702 703 url_preview_ip_range_blacklist = mkOption { 704 type = types.listOf types.str; 705 default = [ 706 "10.0.0.0/8" 707 "100.64.0.0/10" 708 "127.0.0.0/8" 709 "169.254.0.0/16" 710 "172.16.0.0/12" 711 "192.0.0.0/24" 712 "192.0.2.0/24" 713 "192.168.0.0/16" 714 "192.88.99.0/24" 715 "198.18.0.0/15" 716 "198.51.100.0/24" 717 "2001:db8::/32" 718 "203.0.113.0/24" 719 "224.0.0.0/4" 720 "::1/128" 721 "fc00::/7" 722 "fe80::/10" 723 "fec0::/10" 724 "ff00::/8" 725 ]; 726 description = lib.mdDoc '' 727 List of IP address CIDR ranges that the URL preview spider is denied 728 from accessing. 729 ''; 730 }; 731 732 url_preview_ip_range_whitelist = mkOption { 733 type = types.listOf types.str; 734 default = [ ]; 735 description = lib.mdDoc '' 736 List of IP address CIDR ranges that the URL preview spider is allowed 737 to access even if they are specified in url_preview_ip_range_blacklist. 738 ''; 739 }; 740 741 url_preview_url_blacklist = mkOption { 742 # FIXME revert to just `listOf (attrsOf str)` after some time(tm). 743 type = types.listOf ( 744 types.coercedTo 745 types.str 746 (const (throw '' 747 Setting `config.services.matrix-synapse.settings.url_preview_url_blacklist` 748 to a list of strings has never worked. Due to a bug, this was the type accepted 749 by the module, but in practice it broke on runtime and as a result, no URL 750 preview worked anywhere if this was set. 751 752 See https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#url_preview_url_blacklist 753 on how to configure it properly. 754 '')) 755 (types.attrsOf types.str)); 756 default = [ ]; 757 example = literalExpression '' 758 [ 759 { scheme = "http"; } # no http previews 760 { netloc = "www.acme.com"; path = "/foo"; } # block http(s)://www.acme.com/foo 761 ] 762 ''; 763 description = lib.mdDoc '' 764 Optional list of URL matches that the URL preview spider is 765 denied from accessing. 766 ''; 767 }; 768 769 max_upload_size = mkOption { 770 type = types.str; 771 default = "50M"; 772 example = "100M"; 773 description = lib.mdDoc '' 774 The largest allowed upload size in bytes 775 ''; 776 }; 777 778 max_image_pixels = mkOption { 779 type = types.str; 780 default = "32M"; 781 example = "64M"; 782 description = lib.mdDoc '' 783 Maximum number of pixels that will be thumbnailed 784 ''; 785 }; 786 787 dynamic_thumbnails = mkOption { 788 type = types.bool; 789 default = false; 790 example = true; 791 description = lib.mdDoc '' 792 Whether to generate new thumbnails on the fly to precisely match 793 the resolution requested by the client. If true then whenever 794 a new resolution is requested by the client the server will 795 generate a new thumbnail. If false the server will pick a thumbnail 796 from a precalculated list. 797 ''; 798 }; 799 800 turn_uris = mkOption { 801 type = types.listOf types.str; 802 default = [ ]; 803 example = [ 804 "turn:turn.example.com:3487?transport=udp" 805 "turn:turn.example.com:3487?transport=tcp" 806 "turns:turn.example.com:5349?transport=udp" 807 "turns:turn.example.com:5349?transport=tcp" 808 ]; 809 description = lib.mdDoc '' 810 The public URIs of the TURN server to give to clients 811 ''; 812 }; 813 turn_shared_secret = mkOption { 814 type = types.str; 815 default = ""; 816 example = literalExpression '' 817 config.services.coturn.static-auth-secret 818 ''; 819 description = mdDoc '' 820 The shared secret used to compute passwords for the TURN server. 821 822 Secrets should be passed in via `extraConfigFiles`! 823 ''; 824 }; 825 826 trusted_key_servers = mkOption { 827 type = types.listOf (types.submodule { 828 freeformType = format.type; 829 options = { 830 server_name = mkOption { 831 type = types.str; 832 example = "matrix.org"; 833 description = lib.mdDoc '' 834 Hostname of the trusted server. 835 ''; 836 }; 837 }; 838 }); 839 default = [{ 840 server_name = "matrix.org"; 841 verify_keys = { 842 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; 843 }; 844 }]; 845 description = lib.mdDoc '' 846 The trusted servers to download signing keys from. 847 ''; 848 }; 849 850 app_service_config_files = mkOption { 851 type = types.listOf types.path; 852 default = [ ]; 853 description = lib.mdDoc '' 854 A list of application service config file to use 855 ''; 856 }; 857 858 redis = lib.mkOption { 859 type = types.submodule { 860 freeformType = format.type; 861 options = { 862 enabled = lib.mkOption { 863 type = types.bool; 864 default = false; 865 description = lib.mdDoc '' 866 Whether to use redis support 867 ''; 868 }; 869 }; 870 }; 871 default = { }; 872 description = lib.mdDoc '' 873 Redis configuration for synapse. 874 875 See the 876 [upstream documentation](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/usage/configuration/config_documentation.md#redis) 877 for available options. 878 ''; 879 }; 880 }; 881 }; 882 }; 883 884 workers = lib.mkOption { 885 default = { }; 886 description = lib.mdDoc '' 887 Options for configuring workers. Worker support will be enabled if at least one worker is configured here. 888 889 See the [worker documention](https://matrix-org.github.io/synapse/latest/workers.html#worker-configuration) 890 for possible options for each worker. Worker-specific options overriding the shared homeserver configuration can be 891 specified here for each worker. 892 893 ::: {.note} 894 Worker support will add a replication listener on port 9093 to the main synapse process using the default 895 value of [`services.matrix-synapse.settings.listeners`](#opt-services.matrix-synapse.settings.listeners) and configure that 896 listener as `services.matrix-synapse.settings.instance_map.main`. 897 If you set either of those options, make sure to configure a replication listener yourself. 898 899 A redis server is required for running workers. A local one can be enabled 900 using [`services.matrix-synapse.configureRedisLocally`](#opt-services.matrix-synapse.configureRedisLocally). 901 902 Workers also require a proper reverse proxy setup to direct incoming requests to the appropriate process. See 903 the [reverse proxy documentation](https://matrix-org.github.io/synapse/latest/reverse_proxy.html) for a 904 general reverse proxying setup and 905 the [worker documentation](https://matrix-org.github.io/synapse/latest/workers.html#available-worker-applications) 906 for the available endpoints per worker application. 907 ::: 908 ''; 909 type = types.attrsOf (types.submodule ({name, ...}: { 910 freeformType = format.type; 911 options = { 912 worker_app = lib.mkOption { 913 type = types.enum [ 914 "synapse.app.generic_worker" 915 "synapse.app.media_repository" 916 ]; 917 description = "Type of this worker"; 918 default = "synapse.app.generic_worker"; 919 }; 920 worker_listeners = lib.mkOption { 921 default = [ ]; 922 type = types.listOf (listenerType true); 923 description = lib.mdDoc '' 924 List of ports that this worker should listen on, their purpose and their configuration. 925 ''; 926 }; 927 worker_log_config = lib.mkOption { 928 type = types.path; 929 default = genLogConfigFile "synapse-${name}"; 930 defaultText = logConfigText "synapse-${name}"; 931 description = lib.mdDoc '' 932 The file for log configuration. 933 934 See the [python documentation](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema) 935 for the schema and the [upstream repository](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_log_config.yaml) 936 for an example. 937 ''; 938 }; 939 }; 940 })); 941 default = { }; 942 example = lib.literalExpression '' 943 { 944 "federation_sender" = { }; 945 "federation_receiver" = { 946 worker_listeners = [ 947 { 948 type = "http"; 949 port = 8009; 950 bind_addresses = [ "127.0.0.1" ]; 951 tls = false; 952 x_forwarded = true; 953 resources = [{ 954 names = [ "federation" ]; 955 }]; 956 } 957 ]; 958 }; 959 } 960 ''; 961 }; 962 963 extraConfigFiles = mkOption { 964 type = types.listOf types.path; 965 default = [ ]; 966 description = lib.mdDoc '' 967 Extra config files to include. 968 969 The configuration files will be included based on the command line 970 argument --config-path. This allows to configure secrets without 971 having to go through the Nix store, e.g. based on deployment keys if 972 NixOps is in use. 973 ''; 974 }; 975 976 configureRedisLocally = lib.mkOption { 977 type = types.bool; 978 default = false; 979 description = lib.mdDoc '' 980 Whether to automatically configure a local redis server for matrix-synapse. 981 ''; 982 }; 983 }; 984 }; 985 986 config = mkIf cfg.enable { 987 assertions = [ 988 { 989 assertion = clientListener != null; 990 message = '' 991 At least one listener which serves the `client` resource via HTTP is required 992 by synapse in `services.matrix-synapse.settings.listeners` or in one of the workers! 993 ''; 994 } 995 { 996 assertion = hasWorkers -> cfg.settings.redis.enabled; 997 message = '' 998 Workers for matrix-synapse require configuring a redis instance. This can be done 999 automatically by setting `services.matrix-synapse.configureRedisLocally = true`. 1000 ''; 1001 } 1002 { 1003 assertion = 1004 let 1005 main = cfg.settings.instance_map.main; 1006 listener = lib.findFirst 1007 ( 1008 listener: 1009 listener.port == main.port 1010 && listenerSupportsResource "replication" listener 1011 && (lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses) 1012 ) 1013 null 1014 cfg.settings.listeners; 1015 in 1016 hasWorkers -> (cfg.settings.instance_map ? main && listener != null); 1017 message = '' 1018 Workers for matrix-synapse require setting `services.matrix-synapse.settings.instance_map.main` 1019 to any listener configured in `services.matrix-synapse.settings.listeners` with a `"replication"` 1020 resource. 1021 1022 This is done by default unless you manually configure either of those settings. 1023 ''; 1024 } 1025 ]; 1026 1027 services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally { 1028 enabled = true; 1029 path = config.services.redis.servers.matrix-synapse.unixSocket; 1030 }; 1031 services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault { 1032 host = "127.0.0.1"; 1033 port = 9093; 1034 }); 1035 1036 services.matrix-synapse.serviceUnit = if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service"; 1037 services.matrix-synapse.configFile = configFile; 1038 services.matrix-synapse.package = wrapped; 1039 1040 # default them, so they are additive 1041 services.matrix-synapse.extras = defaultExtras; 1042 1043 services.matrix-synapse.log = mapAttrsRecursive (const mkDefault) defaultCommonLogConfig; 1044 1045 users.users.matrix-synapse = { 1046 group = "matrix-synapse"; 1047 home = cfg.dataDir; 1048 createHome = true; 1049 shell = "${pkgs.bash}/bin/bash"; 1050 uid = config.ids.uids.matrix-synapse; 1051 }; 1052 1053 users.groups.matrix-synapse = { 1054 gid = config.ids.gids.matrix-synapse; 1055 }; 1056 1057 systemd.targets.matrix-synapse = lib.mkIf hasWorkers { 1058 description = "Synapse Matrix parent target"; 1059 after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service"; 1060 wantedBy = [ "multi-user.target" ]; 1061 }; 1062 1063 systemd.services = 1064 let 1065 targetConfig = 1066 if hasWorkers 1067 then { 1068 partOf = [ "matrix-synapse.target" ]; 1069 wantedBy = [ "matrix-synapse.target" ]; 1070 unitConfig.ReloadPropagatedFrom = "matrix-synapse.target"; 1071 requires = optional hasLocalPostgresDB "postgresql.service"; 1072 } 1073 else { 1074 after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service"; 1075 requires = optional hasLocalPostgresDB "postgresql.service"; 1076 wantedBy = [ "multi-user.target" ]; 1077 }; 1078 baseServiceConfig = { 1079 environment = optionalAttrs (cfg.withJemalloc) { 1080 LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so"; 1081 }; 1082 serviceConfig = { 1083 Type = "notify"; 1084 User = "matrix-synapse"; 1085 Group = "matrix-synapse"; 1086 WorkingDirectory = cfg.dataDir; 1087 ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; 1088 Restart = "on-failure"; 1089 UMask = "0077"; 1090 1091 # Security Hardening 1092 # Refer to systemd.exec(5) for option descriptions. 1093 CapabilityBoundingSet = [ "" ]; 1094 LockPersonality = true; 1095 NoNewPrivileges = true; 1096 PrivateDevices = true; 1097 PrivateTmp = true; 1098 PrivateUsers = true; 1099 ProcSubset = "pid"; 1100 ProtectClock = true; 1101 ProtectControlGroups = true; 1102 ProtectHome = true; 1103 ProtectHostname = true; 1104 ProtectKernelLogs = true; 1105 ProtectKernelModules = true; 1106 ProtectKernelTunables = true; 1107 ProtectProc = "invisible"; 1108 ProtectSystem = "strict"; 1109 ReadWritePaths = [ cfg.dataDir cfg.settings.media_store_path ]; 1110 RemoveIPC = true; 1111 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; 1112 RestrictNamespaces = true; 1113 RestrictRealtime = true; 1114 RestrictSUIDSGID = true; 1115 SystemCallArchitectures = "native"; 1116 SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; 1117 }; 1118 } 1119 // targetConfig; 1120 genWorkerService = name: workerCfg: 1121 let 1122 finalWorkerCfg = workerCfg // { worker_name = name; }; 1123 workerConfigFile = format.generate "worker-${name}.yaml" finalWorkerCfg; 1124 in 1125 { 1126 name = "matrix-synapse-worker-${name}"; 1127 value = lib.mkMerge [ 1128 baseServiceConfig 1129 { 1130 description = "Synapse Matrix worker ${name}"; 1131 # make sure the main process starts first for potential database migrations 1132 after = [ "matrix-synapse.service" ]; 1133 requires = [ "matrix-synapse.service" ]; 1134 serviceConfig = { 1135 ExecStart = '' 1136 ${cfg.package}/bin/synapse_worker \ 1137 ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile workerConfigFile ] ++ cfg.extraConfigFiles) } 1138 --keys-directory ${cfg.dataDir} 1139 ''; 1140 }; 1141 } 1142 ]; 1143 }; 1144 in 1145 { 1146 matrix-synapse = lib.mkMerge [ 1147 baseServiceConfig 1148 { 1149 description = "Synapse Matrix homeserver"; 1150 preStart = '' 1151 ${cfg.package}/bin/synapse_homeserver \ 1152 --config-path ${configFile} \ 1153 --keys-directory ${cfg.dataDir} \ 1154 --generate-keys 1155 ''; 1156 serviceConfig = { 1157 ExecStartPre = [ 1158 ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" '' 1159 chown matrix-synapse:matrix-synapse ${cfg.settings.signing_key_path} 1160 chmod 0600 ${cfg.settings.signing_key_path} 1161 '')) 1162 ]; 1163 ExecStart = '' 1164 ${cfg.package}/bin/synapse_homeserver \ 1165 ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) } 1166 --keys-directory ${cfg.dataDir} 1167 ''; 1168 }; 1169 } 1170 ]; 1171 } 1172 // (lib.mapAttrs' genWorkerService cfg.workers); 1173 1174 services.redis.servers.matrix-synapse = lib.mkIf cfg.configureRedisLocally { 1175 enable = true; 1176 user = "matrix-synapse"; 1177 }; 1178 1179 environment.systemPackages = [ registerNewMatrixUser ]; 1180 }; 1181 1182 meta = { 1183 buildDocsInSandbox = false; 1184 doc = ./synapse.md; 1185 maintainers = teams.matrix.members; 1186 }; 1187 1188}