at master 25 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 12 cfg = config.services.jitsi-meet; 13 14 # The configuration files are JS of format "var <<string>> = <<JSON>>;". In order to 15 # override only some settings, we need to extract the JSON, use jq to merge it with 16 # the config provided by user, and then reconstruct the file. 17 overrideJs = 18 source: varName: userCfg: appendExtra: 19 let 20 extractor = pkgs.writeText "extractor.js" '' 21 var fs = require("fs"); 22 eval(fs.readFileSync(process.argv[2], 'utf8')); 23 process.stdout.write(JSON.stringify(eval(process.argv[3]))); 24 ''; 25 userJson = pkgs.writeText "user.json" (builtins.toJSON userCfg); 26 in 27 (pkgs.runCommand "${varName}.js" { } '' 28 ${pkgs.nodejs}/bin/node ${extractor} ${source} ${varName} > default.json 29 ( 30 echo "var ${varName} = " 31 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' default.json ${userJson} 32 echo ";" 33 echo ${escapeShellArg appendExtra} 34 ) > $out 35 ''); 36 37 # Essential config - it's probably not good to have these as option default because 38 # types.attrs doesn't do merging. Let's merge explicitly, can still be overridden if 39 # user desires. 40 defaultCfg = { 41 hosts = { 42 domain = cfg.hostName; 43 muc = "conference.${cfg.hostName}"; 44 focus = "focus.${cfg.hostName}"; 45 jigasi = "jigasi.${cfg.hostName}"; 46 }; 47 bosh = "//${cfg.hostName}/http-bind"; 48 websocket = "wss://${cfg.hostName}/xmpp-websocket"; 49 50 fileRecordingsEnabled = true; 51 liveStreamingEnabled = true; 52 hiddenDomain = "recorder.${cfg.hostName}"; 53 }; 54in 55{ 56 options.services.jitsi-meet = with types; { 57 enable = mkEnableOption "Jitsi Meet - Secure, Simple and Scalable Video Conferences"; 58 59 hostName = mkOption { 60 type = str; 61 example = "meet.example.org"; 62 description = '' 63 FQDN of the Jitsi Meet instance. 64 ''; 65 }; 66 67 config = mkOption { 68 type = attrs; 69 default = { }; 70 example = literalExpression '' 71 { 72 enableWelcomePage = false; 73 defaultLang = "fi"; 74 } 75 ''; 76 description = '' 77 Client-side web application settings that override the defaults in {file}`config.js`. 78 79 See <https://github.com/jitsi/jitsi-meet/blob/master/config.js> for default 80 configuration with comments. 81 ''; 82 }; 83 84 extraConfig = mkOption { 85 type = lines; 86 default = ""; 87 description = '' 88 Text to append to {file}`config.js` web application config file. 89 90 Can be used to insert JavaScript logic to determine user's region in cascading bridges setup. 91 ''; 92 }; 93 94 interfaceConfig = mkOption { 95 type = attrs; 96 default = { }; 97 example = literalExpression '' 98 { 99 SHOW_JITSI_WATERMARK = false; 100 SHOW_WATERMARK_FOR_GUESTS = false; 101 } 102 ''; 103 description = '' 104 Client-side web-app interface settings that override the defaults in {file}`interface_config.js`. 105 106 See <https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js> for 107 default configuration with comments. 108 ''; 109 }; 110 111 videobridge = { 112 enable = mkOption { 113 type = bool; 114 default = true; 115 description = '' 116 Jitsi Videobridge instance and configure it to connect to Prosody. 117 118 Additional configuration is possible with {option}`services.jitsi-videobridge` 119 ''; 120 }; 121 122 passwordFile = mkOption { 123 type = nullOr str; 124 default = null; 125 example = "/run/keys/videobridge"; 126 description = '' 127 File containing password to the Prosody account for videobridge. 128 129 If `null`, a file with password will be generated automatically. Setting 130 this option is useful if you plan to connect additional videobridges to the XMPP server. 131 ''; 132 }; 133 }; 134 135 jicofo.enable = mkOption { 136 type = bool; 137 default = true; 138 description = '' 139 Whether to enable JiCoFo instance and configure it to connect to Prosody. 140 141 Additional configuration is possible with {option}`services.jicofo`. 142 ''; 143 }; 144 145 jibri.enable = mkOption { 146 type = bool; 147 default = false; 148 description = '' 149 Whether to enable a Jibri instance and configure it to connect to Prosody. 150 151 Additional configuration is possible with {option}`services.jibri`, and 152 {option}`services.jibri.finalizeScript` is especially useful. 153 ''; 154 }; 155 156 jigasi.enable = mkOption { 157 type = bool; 158 default = false; 159 description = '' 160 Whether to enable jigasi instance and configure it to connect to Prosody. 161 162 Additional configuration is possible with <option>services.jigasi</option>. 163 ''; 164 }; 165 166 nginx.enable = mkOption { 167 type = bool; 168 default = true; 169 description = '' 170 Whether to enable nginx virtual host that will serve the javascript application and act as 171 a proxy for the XMPP server. Further nginx configuration can be done by adapting 172 {option}`services.nginx.virtualHosts.<hostName>`. 173 When this is enabled, ACME will be used to retrieve a TLS certificate by default. To disable 174 this, set the {option}`services.nginx.virtualHosts.<hostName>.enableACME` to 175 `false` and if appropriate do the same for 176 {option}`services.nginx.virtualHosts.<hostName>.forceSSL`. 177 ''; 178 }; 179 180 caddy.enable = mkEnableOption "caddy reverse proxy to expose jitsi-meet"; 181 182 prosody.enable = mkOption { 183 type = bool; 184 default = true; 185 example = false; 186 description = '' 187 Whether to configure Prosody to relay XMPP messages between Jitsi Meet components. Turn this 188 off if you want to configure it manually. 189 ''; 190 }; 191 192 prosody.allowners_muc = mkOption { 193 type = bool; 194 default = false; 195 description = '' 196 Add module allowners, any user in chat is able to 197 kick other. Usefull in jitsi-meet to kick ghosts. 198 ''; 199 }; 200 201 prosody.lockdown = mkOption { 202 type = bool; 203 default = false; 204 example = true; 205 description = '' 206 Whether to disable Prosody features not needed by Jitsi Meet. 207 208 The default Prosody configuration assumes that it will be used as a 209 general-purpose XMPP server rather than as a companion service for 210 Jitsi Meet. This option reconfigures Prosody to only listen on 211 localhost without support for TLS termination, XMPP federation or 212 the file transfer proxy. 213 ''; 214 }; 215 216 excalidraw.enable = mkEnableOption "Excalidraw collaboration backend for Jitsi"; 217 excalidraw.port = mkOption { 218 type = types.port; 219 default = 3002; 220 description = ''The port which the Excalidraw backend for Jitsi should listen to.''; 221 }; 222 223 secureDomain = { 224 enable = mkEnableOption "Authenticated room creation"; 225 authentication = mkOption { 226 type = types.str; 227 default = "internal_hashed"; 228 description = ''The authentication type to be used by jitsi''; 229 }; 230 }; 231 }; 232 233 config = mkIf cfg.enable { 234 services.prosody = mkIf cfg.prosody.enable { 235 236 # required for muc_breakout_rooms 237 package = lib.mkDefault ( 238 pkgs.prosody.override { 239 withExtraLuaPackages = p: with p; [ cjson ]; 240 } 241 ); 242 243 enable = mkDefault true; 244 xmppComplianceSuite = mkDefault false; 245 modules = { 246 admin_adhoc = mkDefault false; 247 bosh = mkDefault true; 248 ping = mkDefault true; 249 roster = mkDefault true; 250 saslauth = mkDefault true; 251 smacks = mkDefault true; 252 tls = mkDefault true; 253 websocket = mkDefault true; 254 proxy65 = mkIf cfg.prosody.lockdown (mkDefault false); 255 }; 256 httpInterfaces = mkIf cfg.prosody.lockdown (mkDefault [ "127.0.0.1" ]); 257 httpsPorts = mkIf cfg.prosody.lockdown (mkDefault [ ]); 258 muc = [ 259 { 260 domain = "conference.${cfg.hostName}"; 261 name = "Jitsi Meet MUC"; 262 allowners_muc = cfg.prosody.allowners_muc; 263 roomLocking = false; 264 roomDefaultPublicJids = true; 265 extraConfig = '' 266 restrict_room_creation = true 267 storage = "memory" 268 admins = { "focus@auth.${cfg.hostName}" } 269 ''; 270 } 271 { 272 domain = "breakout.${cfg.hostName}"; 273 name = "Jitsi Meet Breakout MUC"; 274 roomLocking = false; 275 roomDefaultPublicJids = true; 276 extraConfig = '' 277 restrict_room_creation = true 278 storage = "memory" 279 admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}" } 280 ''; 281 } 282 { 283 domain = "internal.auth.${cfg.hostName}"; 284 name = "Jitsi Meet Videobridge MUC"; 285 roomLocking = false; 286 roomDefaultPublicJids = true; 287 extraConfig = '' 288 storage = "memory" 289 admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}", "jigasi@auth.${cfg.hostName}" } 290 ''; 291 #-- muc_room_cache_size = 1000 292 } 293 { 294 domain = "lobby.${cfg.hostName}"; 295 name = "Jitsi Meet Lobby MUC"; 296 roomLocking = false; 297 roomDefaultPublicJids = true; 298 extraConfig = '' 299 restrict_room_creation = true 300 storage = "memory" 301 ''; 302 } 303 ]; 304 extraModules = [ 305 "pubsub" 306 "smacks" 307 "speakerstats" 308 "external_services" 309 "conference_duration" 310 "muc_lobby_rooms" 311 "muc_breakout_rooms" 312 "av_moderation" 313 "muc_hide_all" 314 "muc_meeting_id" 315 "muc_domain_mapper" 316 "muc_rate_limit" 317 "limits_exception" 318 "persistent_lobby" 319 "room_metadata" 320 ]; 321 extraPluginPaths = [ "${pkgs.jitsi-meet-prosody}/share/prosody-plugins" ]; 322 extraConfig = lib.mkMerge [ 323 (mkAfter '' 324 Component "focus.${cfg.hostName}" "client_proxy" 325 target_address = "focus@auth.${cfg.hostName}" 326 327 Component "jigasi.${cfg.hostName}" "client_proxy" 328 target_address = "jigasi@auth.${cfg.hostName}" 329 330 Component "speakerstats.${cfg.hostName}" "speakerstats_component" 331 muc_component = "conference.${cfg.hostName}" 332 333 Component "conferenceduration.${cfg.hostName}" "conference_duration_component" 334 muc_component = "conference.${cfg.hostName}" 335 336 Component "endconference.${cfg.hostName}" "end_conference" 337 muc_component = "conference.${cfg.hostName}" 338 339 Component "avmoderation.${cfg.hostName}" "av_moderation_component" 340 muc_component = "conference.${cfg.hostName}" 341 342 Component "metadata.${cfg.hostName}" "room_metadata_component" 343 muc_component = "conference.${cfg.hostName}" 344 breakout_rooms_component = "breakout.${cfg.hostName}" 345 '') 346 (mkBefore ( 347 '' 348 muc_mapper_domain_base = "${cfg.hostName}" 349 350 http_cors_override = { 351 websocket = { enabled = true } 352 } 353 consider_websocket_secure = true; 354 355 unlimited_jids = { 356 "focus@auth.${cfg.hostName}", 357 "jvb@auth.${cfg.hostName}" 358 } 359 '' 360 + optionalString cfg.prosody.lockdown '' 361 c2s_interfaces = { "127.0.0.1" }; 362 modules_disabled = { "s2s" }; 363 '' 364 )) 365 ]; 366 virtualHosts.${cfg.hostName} = { 367 enabled = true; 368 domain = cfg.hostName; 369 extraConfig = '' 370 authentication = ${ 371 if cfg.secureDomain.enable then "\"${cfg.secureDomain.authentication}\"" else "\"jitsi-anonymous\"" 372 } 373 c2s_require_encryption = false 374 admins = { "focus@auth.${cfg.hostName}" } 375 smacks_max_unacked_stanzas = 5 376 smacks_hibernation_time = 60 377 smacks_max_hibernated_sessions = 1 378 smacks_max_old_sessions = 1 379 380 av_moderation_component = "avmoderation.${cfg.hostName}" 381 speakerstats_component = "speakerstats.${cfg.hostName}" 382 conference_duration_component = "conferenceduration.${cfg.hostName}" 383 end_conference_component = "endconference.${cfg.hostName}" 384 385 lobby_muc = "lobby.${cfg.hostName}" 386 breakout_rooms_muc = "breakout.${cfg.hostName}" 387 room_metadata_component = "metadata.${cfg.hostName}" 388 main_muc = "conference.${cfg.hostName}" 389 ''; 390 ssl = { 391 cert = "/var/lib/jitsi-meet/jitsi-meet.crt"; 392 key = "/var/lib/jitsi-meet/jitsi-meet.key"; 393 }; 394 }; 395 virtualHosts."auth.${cfg.hostName}" = { 396 enabled = true; 397 domain = "auth.${cfg.hostName}"; 398 extraConfig = '' 399 authentication = "internal_hashed" 400 ''; 401 ssl = { 402 cert = "/var/lib/jitsi-meet/jitsi-meet.crt"; 403 key = "/var/lib/jitsi-meet/jitsi-meet.key"; 404 }; 405 }; 406 virtualHosts."recorder.${cfg.hostName}" = { 407 enabled = true; 408 domain = "recorder.${cfg.hostName}"; 409 extraConfig = '' 410 authentication = "internal_plain" 411 c2s_require_encryption = false 412 ''; 413 }; 414 virtualHosts."guest.${cfg.hostName}" = { 415 enabled = true; 416 domain = "guest.${cfg.hostName}"; 417 extraConfig = '' 418 authentication = "anonymous" 419 c2s_require_encryption = false 420 ''; 421 }; 422 }; 423 systemd.services.prosody = mkIf cfg.prosody.enable { 424 preStart = 425 let 426 videobridgeSecret = 427 if cfg.videobridge.passwordFile != null then 428 cfg.videobridge.passwordFile 429 else 430 "/var/lib/jitsi-meet/videobridge-secret"; 431 432 in 433 '' 434 ${config.services.prosody.package}/bin/prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)" 435 ${config.services.prosody.package}/bin/prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})" 436 ${config.services.prosody.package}/bin/prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName} 437 ${config.services.prosody.package}/bin/prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)" 438 ${config.services.prosody.package}/bin/prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)" 439 '' 440 + optionalString cfg.jigasi.enable '' 441 ${config.services.prosody.package}/bin/prosodyctl register jigasi auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jigasi-user-secret)" 442 ''; 443 444 serviceConfig = { 445 EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ]; 446 SupplementaryGroups = [ "jitsi-meet" ]; 447 }; 448 reloadIfChanged = true; 449 }; 450 451 users.groups.jitsi-meet = { }; 452 systemd.tmpfiles.rules = [ 453 "d '/var/lib/jitsi-meet' 0750 root jitsi-meet - -" 454 ]; 455 456 systemd.services.jitsi-meet-init-secrets = { 457 wantedBy = [ "multi-user.target" ]; 458 before = [ 459 "jicofo.service" 460 "jitsi-videobridge2.service" 461 ] 462 ++ (optional cfg.prosody.enable "prosody.service") 463 ++ (optional cfg.jigasi.enable "jigasi.service"); 464 serviceConfig = { 465 Type = "oneshot"; 466 UMask = "027"; 467 User = "root"; 468 Group = "jitsi-meet"; 469 WorkingDirectory = "/var/lib/jitsi-meet"; 470 }; 471 472 script = 473 let 474 secrets = [ 475 "jicofo-component-secret" 476 "jicofo-user-secret" 477 "jibri-auth-secret" 478 "jibri-recorder-secret" 479 ] 480 ++ (optionals cfg.jigasi.enable [ 481 "jigasi-user-secret" 482 "jigasi-component-secret" 483 ]) 484 ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret"); 485 in 486 '' 487 ${concatMapStringsSep "\n" (s: '' 488 if [ ! -f ${s} ]; then 489 tr -dc a-zA-Z0-9 </dev/urandom | head -c 64 > ${s} 490 fi 491 '') secrets} 492 493 # for easy access in prosody 494 echo "JICOFO_COMPONENT_SECRET=$(cat jicofo-component-secret)" > secrets-env 495 echo "JIGASI_COMPONENT_SECRET=$(cat jigasi-component-secret)" >> secrets-env 496 '' 497 + optionalString cfg.prosody.enable '' 498 # generate self-signed certificates 499 if [ ! -f /var/lib/jitsi-meet/jitsi-meet.crt ]; then 500 ${getBin pkgs.openssl}/bin/openssl req \ 501 -x509 \ 502 -newkey rsa:4096 \ 503 -keyout /var/lib/jitsi-meet/jitsi-meet.key \ 504 -out /var/lib/jitsi-meet/jitsi-meet.crt \ 505 -days 36500 \ 506 -nodes \ 507 -subj '/CN=${cfg.hostName}/CN=auth.${cfg.hostName}' 508 chmod 640 /var/lib/jitsi-meet/jitsi-meet.key 509 fi 510 ''; 511 }; 512 513 systemd.services.jitsi-excalidraw = mkIf cfg.excalidraw.enable { 514 description = "Excalidraw collaboration backend for Jitsi"; 515 after = [ "network.target" ]; 516 wantedBy = [ "multi-user.target" ]; 517 environment.PORT = toString cfg.excalidraw.port; 518 519 serviceConfig = { 520 Type = "simple"; 521 ExecStart = "${pkgs.jitsi-excalidraw}/bin/jitsi-excalidraw-backend"; 522 Restart = "on-failure"; 523 524 DynamicUser = true; 525 Group = "jitsi-meet"; 526 CapabilityBoundingSet = ""; 527 NoNewPrivileges = true; 528 ProtectSystem = "strict"; 529 ProtectClock = true; 530 ProtectHome = true; 531 ProtectProc = "noaccess"; 532 ProtectKernelLogs = true; 533 PrivateTmp = true; 534 PrivateDevices = true; 535 PrivateUsers = true; 536 ProtectHostname = true; 537 ProtectKernelTunables = true; 538 ProtectKernelModules = true; 539 ProtectControlGroups = true; 540 RestrictAddressFamilies = [ 541 "AF_INET" 542 "AF_INET6" 543 ]; 544 RestrictNamespaces = true; 545 LockPersonality = true; 546 RestrictRealtime = true; 547 RestrictSUIDSGID = true; 548 SystemCallFilter = [ 549 "@system-service @pkey" 550 "~@privileged" 551 ]; 552 }; 553 }; 554 555 services.nginx = mkIf cfg.nginx.enable { 556 enable = mkDefault true; 557 virtualHosts.${cfg.hostName} = { 558 enableACME = mkDefault true; 559 forceSSL = mkDefault true; 560 root = pkgs.jitsi-meet; 561 extraConfig = '' 562 ssi on; 563 ''; 564 locations."@root_path".extraConfig = '' 565 rewrite ^/(.*)$ / break; 566 ''; 567 locations."~ ^/([^/\\?&:'\"]+)$".tryFiles = "$uri @root_path"; 568 locations."^~ /xmpp-websocket" = { 569 priority = 100; 570 proxyPass = "http://localhost:5280/xmpp-websocket"; 571 proxyWebsockets = true; 572 }; 573 locations."=/http-bind" = { 574 proxyPass = "http://localhost:5280/http-bind"; 575 extraConfig = '' 576 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 577 proxy_set_header Host $host; 578 ''; 579 }; 580 locations."=/external_api.js" = mkDefault { 581 alias = "${pkgs.jitsi-meet}/libs/external_api.min.js"; 582 }; 583 locations."=/_api/room-info" = { 584 proxyPass = "http://localhost:5280/room-info"; 585 extraConfig = '' 586 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 587 proxy_set_header Host $host; 588 ''; 589 }; 590 locations."=/config.js" = mkDefault { 591 alias = 592 overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config) 593 cfg.extraConfig; 594 }; 595 locations."=/interface_config.js" = mkDefault { 596 alias = 597 overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig 598 ""; 599 }; 600 locations."/socket.io/" = mkIf cfg.excalidraw.enable { 601 proxyPass = "http://127.0.0.1:${toString cfg.excalidraw.port}"; 602 proxyWebsockets = true; 603 }; 604 }; 605 }; 606 607 services.caddy = mkIf cfg.caddy.enable { 608 enable = mkDefault true; 609 virtualHosts.${cfg.hostName} = { 610 extraConfig = 611 let 612 templatedJitsiMeet = pkgs.runCommand "templated-jitsi-meet" { } '' 613 cp -R --no-preserve=all ${pkgs.jitsi-meet}/* . 614 for file in *.html **/*.html ; do 615 ${pkgs.sd}/bin/sd '<!--#include virtual="(.*)" -->' '{{ include "$1" }}' $file 616 done 617 rm config.js 618 rm interface_config.js 619 cp -R . $out 620 cp ${ 621 overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config) 622 cfg.extraConfig 623 } $out/config.js 624 cp ${ 625 overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig "" 626 } $out/interface_config.js 627 cp ./libs/external_api.min.js $out/external_api.js 628 ''; 629 in 630 (optionalString cfg.excalidraw.enable '' 631 handle /socket.io/ { 632 reverse_proxy 127.0.0.1:${toString cfg.excalidraw.port} 633 } 634 '') 635 + '' 636 handle /http-bind { 637 header Host ${cfg.hostName} 638 reverse_proxy 127.0.0.1:5280 639 } 640 handle /xmpp-websocket { 641 reverse_proxy 127.0.0.1:5280 642 } 643 handle { 644 templates 645 root * ${templatedJitsiMeet} 646 try_files {path} {path} 647 try_files {path} /index.html 648 file_server 649 } 650 ''; 651 }; 652 }; 653 654 services.jitsi-meet.config = 655 recursiveUpdate 656 (mkIf cfg.excalidraw.enable { 657 whiteboard = { 658 enabled = true; 659 collabServerBaseUrl = "https://${cfg.hostName}"; 660 }; 661 }) 662 ( 663 mkIf cfg.secureDomain.enable { 664 hosts.anonymousdomain = "guest.${cfg.hostName}"; 665 } 666 ); 667 668 services.jitsi-videobridge = mkIf cfg.videobridge.enable { 669 enable = true; 670 xmppConfigs."localhost" = { 671 userName = "jvb"; 672 domain = "auth.${cfg.hostName}"; 673 passwordFile = "/var/lib/jitsi-meet/videobridge-secret"; 674 mucJids = "jvbbrewery@internal.auth.${cfg.hostName}"; 675 disableCertificateVerification = true; 676 }; 677 }; 678 679 services.jicofo = mkIf cfg.jicofo.enable { 680 enable = true; 681 xmppHost = "localhost"; 682 xmppDomain = cfg.hostName; 683 userDomain = "auth.${cfg.hostName}"; 684 userName = "focus"; 685 userPasswordFile = "/var/lib/jitsi-meet/jicofo-user-secret"; 686 componentPasswordFile = "/var/lib/jitsi-meet/jicofo-component-secret"; 687 bridgeMuc = "jvbbrewery@internal.auth.${cfg.hostName}"; 688 config = mkMerge [ 689 { 690 jicofo.xmpp.service.disable-certificate-verification = true; 691 jicofo.xmpp.client.disable-certificate-verification = true; 692 } 693 (lib.mkIf (config.services.jibri.enable || cfg.jibri.enable) { 694 jicofo.jibri = { 695 brewery-jid = "JibriBrewery@internal.auth.${cfg.hostName}"; 696 pending-timeout = "90"; 697 }; 698 }) 699 (lib.mkIf cfg.secureDomain.enable { 700 jicofo = { 701 authentication = { 702 enabled = "true"; 703 type = "XMPP"; 704 login-url = cfg.hostName; 705 }; 706 xmpp.client.client-proxy = "focus.${cfg.hostName}"; 707 }; 708 }) 709 ]; 710 }; 711 712 services.jibri = mkIf cfg.jibri.enable { 713 enable = true; 714 715 xmppEnvironments."jitsi-meet" = { 716 xmppServerHosts = [ "localhost" ]; 717 xmppDomain = cfg.hostName; 718 719 control.muc = { 720 domain = "internal.auth.${cfg.hostName}"; 721 roomName = "JibriBrewery"; 722 nickname = "jibri"; 723 }; 724 725 control.login = { 726 domain = "auth.${cfg.hostName}"; 727 username = "jibri"; 728 passwordFile = "/var/lib/jitsi-meet/jibri-auth-secret"; 729 }; 730 731 call.login = { 732 domain = "recorder.${cfg.hostName}"; 733 username = "recorder"; 734 passwordFile = "/var/lib/jitsi-meet/jibri-recorder-secret"; 735 }; 736 737 usageTimeout = "0"; 738 disableCertificateVerification = true; 739 stripFromRoomDomain = "conference."; 740 }; 741 }; 742 743 services.jigasi = mkIf cfg.jigasi.enable { 744 enable = true; 745 xmppHost = "localhost"; 746 xmppDomain = cfg.hostName; 747 userDomain = "auth.${cfg.hostName}"; 748 userName = "jigasi"; 749 userPasswordFile = "/var/lib/jitsi-meet/jigasi-user-secret"; 750 componentPasswordFile = "/var/lib/jitsi-meet/jigasi-component-secret"; 751 bridgeMuc = "jigasibrewery@internal.${cfg.hostName}"; 752 config = { 753 "org.jitsi.jigasi.ALWAYS_TRUST_MODE_ENABLED" = "true"; 754 }; 755 }; 756 }; 757 758 meta.doc = ./jitsi-meet.md; 759 meta.maintainers = lib.teams.jitsi.members; 760}