at 17.09-beta 22 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.matrix-synapse; 7 logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig; 8 mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${boolToString r.compress}}''; 9 mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${boolToString l.tls}, x_forwarded: ${boolToString l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}''; 10 configFile = pkgs.writeText "homeserver.yaml" '' 11${optionalString (cfg.tls_certificate_path != null) '' 12tls_certificate_path: "${cfg.tls_certificate_path}" 13''} 14${optionalString (cfg.tls_private_key_path != null) '' 15tls_private_key_path: "${cfg.tls_private_key_path}" 16''} 17${optionalString (cfg.tls_dh_params_path != null) '' 18tls_dh_params_path: "${cfg.tls_dh_params_path}" 19''} 20no_tls: ${boolToString cfg.no_tls} 21${optionalString (cfg.bind_port != null) '' 22bind_port: ${toString cfg.bind_port} 23''} 24${optionalString (cfg.unsecure_port != null) '' 25unsecure_port: ${toString cfg.unsecure_port} 26''} 27${optionalString (cfg.bind_host != null) '' 28bind_host: "${cfg.bind_host}" 29''} 30server_name: "${cfg.server_name}" 31pid_file: "/var/run/matrix-synapse.pid" 32web_client: ${boolToString cfg.web_client} 33${optionalString (cfg.public_baseurl != null) '' 34public_baseurl: "${cfg.public_baseurl}" 35''} 36listeners: [${concatStringsSep "," (map mkListener cfg.listeners)}] 37database: { 38 name: "${cfg.database_type}", 39 args: { 40 ${concatStringsSep ",\n " ( 41 mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args 42 )} 43 } 44} 45event_cache_size: "${cfg.event_cache_size}" 46verbose: ${cfg.verbose} 47log_file: "/var/log/matrix-synapse/homeserver.log" 48log_config: "${logConfigFile}" 49rc_messages_per_second: ${cfg.rc_messages_per_second} 50rc_message_burst_count: ${cfg.rc_message_burst_count} 51federation_rc_window_size: ${cfg.federation_rc_window_size} 52federation_rc_sleep_limit: ${cfg.federation_rc_sleep_limit} 53federation_rc_sleep_delay: ${cfg.federation_rc_sleep_delay} 54federation_rc_reject_limit: ${cfg.federation_rc_reject_limit} 55federation_rc_concurrent: ${cfg.federation_rc_concurrent} 56media_store_path: "/var/lib/matrix-synapse/media" 57uploads_path: "/var/lib/matrix-synapse/uploads" 58max_upload_size: "${cfg.max_upload_size}" 59max_image_pixels: "${cfg.max_image_pixels}" 60dynamic_thumbnails: ${boolToString cfg.dynamic_thumbnails} 61url_preview_enabled: ${boolToString cfg.url_preview_enabled} 62${optionalString (cfg.url_preview_enabled == true) '' 63url_preview_ip_range_blacklist: ${builtins.toJSON cfg.url_preview_ip_range_blacklist} 64url_preview_ip_range_whitelist: ${builtins.toJSON cfg.url_preview_ip_range_whitelist} 65url_preview_url_blacklist: ${builtins.toJSON cfg.url_preview_url_blacklist} 66''} 67recaptcha_private_key: "${cfg.recaptcha_private_key}" 68recaptcha_public_key: "${cfg.recaptcha_public_key}" 69enable_registration_captcha: ${boolToString cfg.enable_registration_captcha} 70turn_uris: ${builtins.toJSON cfg.turn_uris} 71turn_shared_secret: "${cfg.turn_shared_secret}" 72enable_registration: ${boolToString cfg.enable_registration} 73${optionalString (cfg.registration_shared_secret != null) '' 74registration_shared_secret: "${cfg.registration_shared_secret}" 75''} 76recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify" 77turn_user_lifetime: "${cfg.turn_user_lifetime}" 78user_creation_max_duration: ${cfg.user_creation_max_duration} 79bcrypt_rounds: ${cfg.bcrypt_rounds} 80allow_guest_access: ${boolToString cfg.allow_guest_access} 81trusted_third_party_id_servers: ${builtins.toJSON cfg.trusted_third_party_id_servers} 82room_invite_state_types: ${builtins.toJSON cfg.room_invite_state_types} 83${optionalString (cfg.macaroon_secret_key != null) '' 84 macaroon_secret_key: "${cfg.macaroon_secret_key}" 85''} 86expire_access_token: ${boolToString cfg.expire_access_token} 87enable_metrics: ${boolToString cfg.enable_metrics} 88report_stats: ${boolToString cfg.report_stats} 89signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key" 90key_refresh_interval: "${cfg.key_refresh_interval}" 91perspectives: 92 servers: { 93 ${concatStringsSep "},\n" (mapAttrsToList (n: v: '' 94 "${n}": { 95 "verify_keys": { 96 ${concatStringsSep "},\n" (mapAttrsToList (n: v: '' 97 "${n}": { 98 "key": "${v}" 99 }'') v)} 100 } 101 '') cfg.servers)} 102 } 103 } 104app_service_config_files: ${builtins.toJSON cfg.app_service_config_files} 105 106${cfg.extraConfig} 107''; 108in { 109 options = { 110 services.matrix-synapse = { 111 enable = mkEnableOption "matrix.org synapse"; 112 package = mkOption { 113 type = types.package; 114 default = pkgs.matrix-synapse; 115 defaultText = "pkgs.matrix-synapse"; 116 description = '' 117 Overridable attribute of the matrix synapse server package to use. 118 ''; 119 }; 120 no_tls = mkOption { 121 type = types.bool; 122 default = false; 123 description = '' 124 Don't bind to the https port 125 ''; 126 }; 127 bind_port = mkOption { 128 type = types.nullOr types.int; 129 default = null; 130 example = 8448; 131 description = '' 132 DEPRECATED: Use listeners instead. 133 The port to listen for HTTPS requests on. 134 For when matrix traffic is sent directly to synapse. 135 ''; 136 }; 137 unsecure_port = mkOption { 138 type = types.nullOr types.int; 139 default = null; 140 example = 8008; 141 description = '' 142 DEPRECATED: Use listeners instead. 143 The port to listen for HTTP requests on. 144 For when matrix traffic passes through loadbalancer that unwraps TLS. 145 ''; 146 }; 147 bind_host = mkOption { 148 type = types.nullOr types.str; 149 default = null; 150 description = '' 151 DEPRECATED: Use listeners instead. 152 Local interface to listen on. 153 The empty string will cause synapse to listen on all interfaces. 154 ''; 155 }; 156 tls_certificate_path = mkOption { 157 type = types.nullOr types.str; 158 default = null; 159 example = "/var/lib/matrix-synapse/homeserver.tls.crt"; 160 description = '' 161 PEM encoded X509 certificate for TLS. 162 You can replace the self-signed certificate that synapse 163 autogenerates on launch with your own SSL certificate + key pair 164 if you like. Any required intermediary certificates can be 165 appended after the primary certificate in hierarchical order. 166 ''; 167 }; 168 tls_private_key_path = mkOption { 169 type = types.nullOr types.str; 170 default = null; 171 example = "/var/lib/matrix-synapse/homeserver.tls.key"; 172 description = '' 173 PEM encoded private key for TLS. Specify null if synapse is not 174 speaking TLS directly. 175 ''; 176 }; 177 tls_dh_params_path = mkOption { 178 type = types.nullOr types.str; 179 default = null; 180 example = "/var/lib/matrix-synapse/homeserver.tls.dh"; 181 description = '' 182 PEM dh parameters for ephemeral keys 183 ''; 184 }; 185 server_name = mkOption { 186 type = types.str; 187 example = "example.com"; 188 description = '' 189 The domain name of the server, with optional explicit port. 190 This is used by remote servers to connect to this server, 191 e.g. matrix.org, localhost:8080, etc. 192 This is also the last part of your UserID. 193 ''; 194 }; 195 web_client = mkOption { 196 type = types.bool; 197 default = false; 198 description = '' 199 Whether to serve a web client from the HTTP/HTTPS root resource. 200 ''; 201 }; 202 public_baseurl = mkOption { 203 type = types.nullOr types.str; 204 default = null; 205 example = "https://example.com:8448/"; 206 description = '' 207 The public-facing base URL for the client API (not including _matrix/...) 208 ''; 209 }; 210 listeners = mkOption { 211 type = types.listOf (types.submodule { 212 options = { 213 port = mkOption { 214 type = types.int; 215 example = 8448; 216 description = '' 217 The port to listen for HTTP(S) requests on. 218 ''; 219 }; 220 bind_address = mkOption { 221 type = types.str; 222 default = ""; 223 example = "203.0.113.42"; 224 description = '' 225 Local interface to listen on. 226 The empty string will cause synapse to listen on all interfaces. 227 ''; 228 }; 229 type = mkOption { 230 type = types.str; 231 default = "http"; 232 description = '' 233 Type of listener. 234 ''; 235 }; 236 tls = mkOption { 237 type = types.bool; 238 default = true; 239 description = '' 240 Whether to listen for HTTPS connections rather than HTTP. 241 ''; 242 }; 243 x_forwarded = mkOption { 244 type = types.bool; 245 default = false; 246 description = '' 247 Use the X-Forwarded-For (XFF) header as the client IP and not the 248 actual client IP. 249 ''; 250 }; 251 resources = mkOption { 252 type = types.listOf (types.submodule { 253 options = { 254 names = mkOption { 255 type = types.listOf types.str; 256 description = '' 257 List of resources to host on this listener. 258 ''; 259 example = ["client" "webclient" "federation"]; 260 }; 261 compress = mkOption { 262 type = types.bool; 263 description = '' 264 Should synapse compress HTTP responses to clients that support it? 265 This should be disabled if running synapse behind a load balancer 266 that can do automatic compression. 267 ''; 268 }; 269 }; 270 }); 271 description = '' 272 List of HTTP resources to serve on this listener. 273 ''; 274 }; 275 }; 276 }); 277 default = [{ 278 port = 8448; 279 bind_address = ""; 280 type = "http"; 281 tls = true; 282 x_forwarded = false; 283 resources = [ 284 { names = ["client" "webclient"]; compress = true; } 285 { names = ["federation"]; compress = false; } 286 ]; 287 }]; 288 description = '' 289 List of ports that Synapse should listen on, their purpose and their configuration. 290 ''; 291 }; 292 verbose = mkOption { 293 type = types.str; 294 default = "0"; 295 description = "Logging verbosity level."; 296 }; 297 rc_messages_per_second = mkOption { 298 type = types.str; 299 default = "0.2"; 300 description = "Number of messages a client can send per second"; 301 }; 302 rc_message_burst_count = mkOption { 303 type = types.str; 304 default = "10.0"; 305 description = "Number of message a client can send before being throttled"; 306 }; 307 federation_rc_window_size = mkOption { 308 type = types.str; 309 default = "1000"; 310 description = "The federation window size in milliseconds"; 311 }; 312 federation_rc_sleep_limit = mkOption { 313 type = types.str; 314 default = "10"; 315 description = '' 316 The number of federation requests from a single server in a window 317 before the server will delay processing the request. 318 ''; 319 }; 320 federation_rc_sleep_delay = mkOption { 321 type = types.str; 322 default = "500"; 323 description = '' 324 The duration in milliseconds to delay processing events from 325 remote servers by if they go over the sleep limit. 326 ''; 327 }; 328 federation_rc_reject_limit = mkOption { 329 type = types.str; 330 default = "50"; 331 description = '' 332 The maximum number of concurrent federation requests allowed 333 from a single server 334 ''; 335 }; 336 federation_rc_concurrent = mkOption { 337 type = types.str; 338 default = "3"; 339 description = "The number of federation requests to concurrently process from a single server"; 340 }; 341 database_type = mkOption { 342 type = types.enum [ "sqlite3" "psycopg2" ]; 343 default = "sqlite3"; 344 description = '' 345 The database engine name. Can be sqlite or psycopg2. 346 ''; 347 }; 348 database_args = mkOption { 349 type = types.attrs; 350 default = { 351 database = "/var/lib/matrix-synapse/homeserver.db"; 352 }; 353 description = '' 354 Arguments to pass to the engine. 355 ''; 356 }; 357 event_cache_size = mkOption { 358 type = types.str; 359 default = "10K"; 360 description = "Number of events to cache in memory."; 361 }; 362 url_preview_enabled = mkOption { 363 type = types.bool; 364 default = false; 365 description = '' 366 Is the preview URL API enabled? If enabled, you *must* specify an 367 explicit url_preview_ip_range_blacklist of IPs that the spider is 368 denied from accessing. 369 ''; 370 }; 371 url_preview_ip_range_blacklist = mkOption { 372 type = types.listOf types.str; 373 default = []; 374 description = '' 375 List of IP address CIDR ranges that the URL preview spider is denied 376 from accessing. 377 ''; 378 }; 379 url_preview_ip_range_whitelist = mkOption { 380 type = types.listOf types.str; 381 default = []; 382 description = '' 383 List of IP address CIDR ranges that the URL preview spider is allowed 384 to access even if they are specified in 385 url_preview_ip_range_blacklist. 386 ''; 387 }; 388 url_preview_url_blacklist = mkOption { 389 type = types.listOf types.str; 390 default = [ 391 "127.0.0.0/8" 392 "10.0.0.0/8" 393 "172.16.0.0/12" 394 "192.168.0.0/16" 395 "100.64.0.0/10" 396 "169.254.0.0/16" 397 ]; 398 description = '' 399 Optional list of URL matches that the URL preview spider is 400 denied from accessing. 401 ''; 402 }; 403 recaptcha_private_key = mkOption { 404 type = types.str; 405 default = ""; 406 description = '' 407 This Home Server's ReCAPTCHA private key. 408 ''; 409 }; 410 recaptcha_public_key = mkOption { 411 type = types.str; 412 default = ""; 413 description = '' 414 This Home Server's ReCAPTCHA public key. 415 ''; 416 }; 417 enable_registration_captcha = mkOption { 418 type = types.bool; 419 default = false; 420 description = '' 421 Enables ReCaptcha checks when registering, preventing signup 422 unless a captcha is answered. Requires a valid ReCaptcha 423 public/private key. 424 ''; 425 }; 426 turn_uris = mkOption { 427 type = types.listOf types.str; 428 default = []; 429 description = '' 430 The public URIs of the TURN server to give to clients 431 ''; 432 }; 433 turn_shared_secret = mkOption { 434 type = types.str; 435 default = ""; 436 description = '' 437 The shared secret used to compute passwords for the TURN server 438 ''; 439 }; 440 turn_user_lifetime = mkOption { 441 type = types.str; 442 default = "1h"; 443 description = "How long generated TURN credentials last"; 444 }; 445 enable_registration = mkOption { 446 type = types.bool; 447 default = false; 448 description = '' 449 Enable registration for new users. 450 ''; 451 }; 452 registration_shared_secret = mkOption { 453 type = types.nullOr types.str; 454 default = null; 455 description = '' 456 If set, allows registration by anyone who also has the shared 457 secret, even if registration is otherwise disabled. 458 ''; 459 }; 460 enable_metrics = mkOption { 461 type = types.bool; 462 default = false; 463 description = '' 464 Enable collection and rendering of performance metrics 465 ''; 466 }; 467 report_stats = mkOption { 468 type = types.bool; 469 default = false; 470 description = '' 471 ''; 472 }; 473 servers = mkOption { 474 type = types.attrsOf (types.attrsOf types.str); 475 default = { 476 "matrix.org" = { 477 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; 478 }; 479 }; 480 description = '' 481 The trusted servers to download signing keys from. 482 ''; 483 }; 484 max_upload_size = mkOption { 485 type = types.str; 486 default = "10M"; 487 description = "The largest allowed upload size in bytes"; 488 }; 489 max_image_pixels = mkOption { 490 type = types.str; 491 default = "32M"; 492 description = "Maximum number of pixels that will be thumbnailed"; 493 }; 494 dynamic_thumbnails = mkOption { 495 type = types.bool; 496 default = false; 497 description = '' 498 Whether to generate new thumbnails on the fly to precisely match 499 the resolution requested by the client. If true then whenever 500 a new resolution is requested by the client the server will 501 generate a new thumbnail. If false the server will pick a thumbnail 502 from a precalculated list. 503 ''; 504 }; 505 user_creation_max_duration = mkOption { 506 type = types.str; 507 default = "1209600000"; 508 description = '' 509 Sets the expiry for the short term user creation in 510 milliseconds. The default value is two weeks. 511 ''; 512 }; 513 bcrypt_rounds = mkOption { 514 type = types.str; 515 default = "12"; 516 description = '' 517 Set the number of bcrypt rounds used to generate password hash. 518 Larger numbers increase the work factor needed to generate the hash. 519 ''; 520 }; 521 allow_guest_access = mkOption { 522 type = types.bool; 523 default = false; 524 description = '' 525 Allows users to register as guests without a password/email/etc, and 526 participate in rooms hosted on this server which have been made 527 accessible to anonymous users. 528 ''; 529 }; 530 trusted_third_party_id_servers = mkOption { 531 type = types.listOf types.str; 532 default = ["matrix.org"]; 533 description = '' 534 The list of identity servers trusted to verify third party identifiers by this server. 535 ''; 536 }; 537 room_invite_state_types = mkOption { 538 type = types.listOf types.str; 539 default = ["m.room.join_rules" "m.room.canonical_alias" "m.room.avatar" "m.room.name"]; 540 description = '' 541 A list of event types that will be included in the room_invite_state 542 ''; 543 }; 544 macaroon_secret_key = mkOption { 545 type = types.nullOr types.str; 546 default = null; 547 description = '' 548 Secret key for authentication tokens 549 ''; 550 }; 551 expire_access_token = mkOption { 552 type = types.bool; 553 default = false; 554 description = '' 555 Whether to enable access token expiration. 556 ''; 557 }; 558 key_refresh_interval = mkOption { 559 type = types.str; 560 default = "1d"; 561 description = '' 562 How long key response published by this server is valid for. 563 Used to set the valid_until_ts in /key/v2 APIs. 564 Determines how quickly servers will query to check which keys 565 are still valid. 566 ''; 567 }; 568 app_service_config_files = mkOption { 569 type = types.listOf types.path; 570 default = [ ]; 571 description = '' 572 A list of application service config file to use 573 ''; 574 }; 575 extraConfig = mkOption { 576 type = types.lines; 577 default = ""; 578 description = '' 579 Extra config options for matrix-synapse. 580 ''; 581 }; 582 logConfig = mkOption { 583 type = types.lines; 584 default = readFile ./matrix-synapse-log_config.yaml; 585 description = '' 586 A yaml python logging config file 587 ''; 588 }; 589 }; 590 }; 591 592 config = mkIf cfg.enable { 593 users.extraUsers = [ 594 { name = "matrix-synapse"; 595 group = "matrix-synapse"; 596 home = "/var/lib/matrix-synapse"; 597 createHome = true; 598 shell = "${pkgs.bash}/bin/bash"; 599 uid = config.ids.uids.matrix-synapse; 600 } ]; 601 602 users.extraGroups = [ 603 { name = "matrix-synapse"; 604 gid = config.ids.gids.matrix-synapse; 605 } ]; 606 607 systemd.services.matrix-synapse = { 608 description = "Synapse Matrix homeserver"; 609 after = [ "network.target" ]; 610 wantedBy = [ "multi-user.target" ]; 611 preStart = '' 612 ${cfg.package}/bin/homeserver \ 613 --config-path ${configFile} \ 614 --keys-directory /var/lib/matrix-synapse \ 615 --generate-keys 616 ''; 617 serviceConfig = { 618 Type = "simple"; 619 User = "matrix-synapse"; 620 Group = "matrix-synapse"; 621 WorkingDirectory = "/var/lib/matrix-synapse"; 622 PermissionsStartOnly = true; 623 ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse"; 624 Restart = "on-failure"; 625 }; 626 }; 627 }; 628}