diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix index 21416be35877..c0f9d785eea2 100644 --- a/nixos/modules/services/web-apps/jitsi-meet.nix +++ b/nixos/modules/services/web-apps/jitsi-meet.nix @@ -169,6 +169,15 @@ in off if you want to configure it manually. ''; }; + + excalidraw.enable = mkEnableOption (lib.mdDoc "Excalidraw collaboration backend for Jitsi"); + excalidraw.port = mkOption { + type = types.port; + default = 3002; + description = lib.mdDoc ''The port which the Excalidraw backend for Jitsi should listen to.''; + }; + + secureDomain.enable = mkEnableOption (lib.mdDoc "Authenticated room creation"); }; config = mkIf cfg.enable { @@ -192,41 +201,118 @@ in roomLocking = false; roomDefaultPublicJids = true; extraConfig = '' + restrict_room_creation = true storage = "memory" + admins = { "focus@auth.${cfg.hostName}" } ''; } { - domain = "internal.${cfg.hostName}"; + domain = "breakout.${cfg.hostName}"; + name = "Jitsi Meet Breakout MUC"; + roomLocking = false; + roomDefaultPublicJids = true; + extraConfig = '' + restrict_room_creation = true + storage = "memory" + admins = { "focus@auth.${cfg.hostName}" } + ''; + } + { + domain = "internal.auth.${cfg.hostName}"; name = "Jitsi Meet Videobridge MUC"; + roomLocking = false; + roomDefaultPublicJids = true; extraConfig = '' storage = "memory" admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}" } ''; #-- muc_room_cache_size = 1000 } + { + domain = "lobby.${cfg.hostName}"; + name = "Jitsi Meet Lobby MUC"; + roomLocking = false; + roomDefaultPublicJids = true; + extraConfig = '' + restrict_room_creation = true + storage = "memory" + ''; + } + ]; + extraModules = [ + "pubsub" + "smacks" + "speakerstats" + "external_services" + "conference_duration" + "end_conference" + "muc_lobby_rooms" + "muc_breakout_rooms" + "av_moderation" + "muc_hide_all" + "muc_meeting_id" + "muc_domain_mapper" + "muc_rate_limit" + "limits_exception" + "persistent_lobby" + "room_metadata" ]; - extraModules = [ "pubsub" "smacks" ]; extraPluginPaths = [ "${pkgs.jitsi-meet-prosody}/share/prosody-plugins" ]; - extraConfig = lib.mkMerge [ (mkAfter '' - Component "focus.${cfg.hostName}" "client_proxy" - target_address = "focus@auth.${cfg.hostName}" + extraConfig = lib.mkMerge [ + (mkAfter '' + Component "focus.${cfg.hostName}" "client_proxy" + target_address = "focus@auth.${cfg.hostName}" + + Component "speakerstats.${cfg.hostName}" "speakerstats_component" + muc_component = "conference.${cfg.hostName}" + + Component "conferenceduration.${cfg.hostName}" "conference_duration_component" + muc_component = "conference.${cfg.hostName}" + + Component "endconference.${cfg.hostName}" "end_conference" + muc_component = "conference.${cfg.hostName}" + + Component "avmoderation.${cfg.hostName}" "av_moderation_component" + muc_component = "conference.${cfg.hostName}" + + Component "metadata.${cfg.hostName}" "room_metadata_component" + muc_component = "conference.${cfg.hostName}" + breakout_rooms_component = "breakout.${cfg.hostName}" '') (mkBefore '' + muc_mapper_domain_base = "${cfg.hostName}" + cross_domain_websocket = true; consider_websocket_secure = true; + + unlimited_jids = { + "focus@auth.${cfg.hostName}", + "jvb@auth.${cfg.hostName}" + } '') ]; virtualHosts.${cfg.hostName} = { enabled = true; domain = cfg.hostName; extraConfig = '' - authentication = "anonymous" + authentication = ${if cfg.secureDomain.enable then "\"internal_hashed\"" else "\"jitsi-anonymous\""} c2s_require_encryption = false admins = { "focus@auth.${cfg.hostName}" } smacks_max_unacked_stanzas = 5 smacks_hibernation_time = 60 smacks_max_hibernated_sessions = 1 smacks_max_old_sessions = 1 + + av_moderation_component = "avmoderation.${cfg.hostName}" + speakerstats_component = "speakerstats.${cfg.hostName}" + conference_duration_component = "conferenceduration.${cfg.hostName}" + end_conference_component = "endconference.${cfg.hostName}" + + c2s_require_encryption = false + lobby_muc = "lobby.${cfg.hostName}" + breakout_rooms_muc = "breakout.${cfg.hostName}" + room_metadata_component = "metadata.${cfg.hostName}" + main_muc = "conference.${cfg.hostName}" ''; ssl = { cert = "/var/lib/jitsi-meet/jitsi-meet.crt"; @@ -237,7 +323,7 @@ in enabled = true; domain = "auth.${cfg.hostName}"; extraConfig = '' - authentication = "internal_plain" + authentication = "internal_hashed" ''; ssl = { cert = "/var/lib/jitsi-meet/jitsi-meet.crt"; @@ -252,6 +338,14 @@ in c2s_require_encryption = false ''; }; + virtualHosts."guest.${cfg.hostName}" = { + enabled = true; + domain = "guest.${cfg.hostName}"; + extraConfig = '' + authentication = "anonymous" + c2s_require_encryption = false + ''; + }; }; systemd.services.prosody = mkIf cfg.prosody.enable { preStart = let @@ -270,7 +364,7 @@ in reloadIfChanged = true; }; - users.groups.jitsi-meet = {}; + users.groups.jitsi-meet = { }; systemd.tmpfiles.rules = [ "d '/var/lib/jitsi-meet' 0750 root jitsi-meet - -" ]; @@ -317,6 +411,20 @@ in ''; }; + systemd.services.jitsi-excalidraw = mkIf cfg.excalidraw.enable { + description = "Excalidraw collaboration backend for Jitsi"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + environment.PORT = toString cfg.excalidraw.port; + + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.jitsi-excalidraw}/bin/jitsi-excalidraw-backend"; + Restart = "on-failure"; + Group = "jitsi-meet"; + }; + }; + services.nginx = mkIf cfg.nginx.enable { enable = mkDefault true; virtualHosts.${cfg.hostName} = { @@ -345,12 +453,23 @@ in locations."=/external_api.js" = mkDefault { alias = "${pkgs.jitsi-meet}/libs/external_api.min.js"; }; + locations."=/_api/room-info" = { + proxyPass = "http://localhost:5280/room-info"; + extraConfig = '' + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + ''; + }; locations."=/config.js" = mkDefault { alias = overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config) cfg.extraConfig; }; locations."=/interface_config.js" = mkDefault { alias = overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig ""; }; + locations."/socket.io/" = mkIf cfg.excalidraw.enable { + proxyPass = "http://127.0.0.1:${toString cfg.excalidraw.port}"; + proxyWebsockets = true; + }; }; }; @@ -359,7 +478,7 @@ in virtualHosts.${cfg.hostName} = { extraConfig = let - templatedJitsiMeet = pkgs.runCommand "templated-jitsi-meet" {} '' + templatedJitsiMeet = pkgs.runCommand "templated-jitsi-meet" { } '' cp -R ${pkgs.jitsi-meet}/* . for file in *.html **/*.html ; do ${pkgs.sd}/bin/sd '' '{{ include "$1" }}' $file @@ -390,13 +509,24 @@ in }; }; + services.jitsi-meet.config = recursiveUpdate + (mkIf cfg.excalidraw.enable { + whiteboard = { + enabled = true; + collabServerBaseUrl = "https://${cfg.hostName}"; + }; + }) + (mkIf cfg.secureDomain.enable { + hosts.anonymousdomain = "guest.${cfg.hostName}"; + }); + services.jitsi-videobridge = mkIf cfg.videobridge.enable { enable = true; xmppConfigs."localhost" = { userName = "jvb"; domain = "auth.${cfg.hostName}"; passwordFile = "/var/lib/jitsi-meet/videobridge-secret"; - mucJids = "jvbbrewery@internal.${cfg.hostName}"; + mucJids = "jvbbrewery@internal.auth.${cfg.hostName}"; disableCertificateVerification = true; }; }; @@ -409,17 +539,27 @@ in userName = "focus"; userPasswordFile = "/var/lib/jitsi-meet/jicofo-user-secret"; componentPasswordFile = "/var/lib/jitsi-meet/jicofo-component-secret"; - bridgeMuc = "jvbbrewery@internal.${cfg.hostName}"; + bridgeMuc = "jvbbrewery@internal.auth.${cfg.hostName}"; config = mkMerge [{ jicofo.xmpp.service.disable-certificate-verification = true; jicofo.xmpp.client.disable-certificate-verification = true; - #} (lib.mkIf cfg.jibri.enable { - } (lib.mkIf (config.services.jibri.enable || cfg.jibri.enable) { - jicofo.jibri = { - brewery-jid = "JibriBrewery@internal.${cfg.hostName}"; - pending-timeout = "90"; - }; - })]; + } + (lib.mkIf (config.services.jibri.enable || cfg.jibri.enable) { + jicofo.jibri = { + brewery-jid = "JibriBrewery@internal.auth.${cfg.hostName}"; + pending-timeout = "90"; + }; + }) + (lib.mkIf cfg.secureDomain.enable { + jicofo = { + authentication = { + enabled = "true"; + type = "XMPP"; + login-url = cfg.hostName; + }; + xmpp.client.client-proxy = "focus.${cfg.hostName}"; + }; + })]; }; services.jibri = mkIf cfg.jibri.enable { @@ -430,7 +570,7 @@ in xmppDomain = cfg.hostName; control.muc = { - domain = "internal.${cfg.hostName}"; + domain = "internal.auth.${cfg.hostName}"; roomName = "JibriBrewery"; nickname = "jibri"; }; diff --git a/pkgs/servers/jitsi-excalidraw/default.nix b/pkgs/servers/jitsi-excalidraw/default.nix new file mode 100644 index 000000000000..bb8bb6c2dfa7 --- /dev/null +++ b/pkgs/servers/jitsi-excalidraw/default.nix @@ -0,0 +1,40 @@ +{ lib +, buildNpmPackage +, fetchFromGitHub +, nodejs +, python3 +}: + +buildNpmPackage rec { + pname = "jitsi-excalidraw-backend"; + version = "17"; + + src = fetchFromGitHub { + owner = "jitsi"; + repo = "excalidraw-backend"; + rev = "x${version}"; + hash = "sha256-aQePkVA8KRL06VewiD0ePRpj88pAItcV7B2SBnRRtCs="; + }; + + npmDepsHash = "sha256-BJqjaqTeg5i+ECGMuiBYVToK2i2XCOVP9yeDFz6nP4k="; + + nativeBuildInputs = [ python3 ]; + + installPhase = '' + mkdir -p $out/share + cp -r {node_modules,dist} $out/share + ''; + + postFixup = '' + makeWrapper ${nodejs}/bin/node $out/bin/jitsi-excalidraw-backend \ + --add-flags dist/index.js \ + --chdir $out/share + ''; + + meta = with lib; { + description = "Excalidraw collaboration backend for Jitsi"; + homepage = "https://github.com/jitsi/excalidraw-backend"; + license = licenses.mit; + maintainers = with maintainers; [ camillemndn ]; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 7fb9cdafdd72..a163c5af0e99 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -26552,6 +26552,8 @@ with pkgs; jicofo = callPackage ../servers/jicofo { }; + jitsi-excalidraw = callPackage ../servers/jitsi-excalidraw { }; + jitsi-meet = callPackage ../servers/web-apps/jitsi-meet { }; jitsi-meet-prosody = callPackage ../misc/jitsi-meet-prosody { };