about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hosts/by-name/server2/configuration.nix4
-rw-r--r--modules/by-name/ji/jitsi-meet/module.nix108
-rw-r--r--tests/by-name/ji/jitsi-meet/test.nix103
3 files changed, 215 insertions, 0 deletions
diff --git a/hosts/by-name/server2/configuration.nix b/hosts/by-name/server2/configuration.nix
index 65e3b24..a492aed 100644
--- a/hosts/by-name/server2/configuration.nix
+++ b/hosts/by-name/server2/configuration.nix
@@ -57,6 +57,10 @@
         "invidious-router.sils.li"
       ];
     };
+    jitsi-meet = {
+      enable = true;
+      domain = "jitsi-meet.vhack.eu";
+    };
     mail = {
       enable = true;
       fqdn = "mail.foss-syndicate.org";
diff --git a/modules/by-name/ji/jitsi-meet/module.nix b/modules/by-name/ji/jitsi-meet/module.nix
new file mode 100644
index 0000000..d5844be
--- /dev/null
+++ b/modules/by-name/ji/jitsi-meet/module.nix
@@ -0,0 +1,108 @@
+{
+  config,
+  lib,
+  ...
+}: let
+  cfg = config.vhack.jitsi-meet;
+in {
+  options.vhack.jitsi-meet = {
+    enable = lib.mkEnableOption "jitsi-meet";
+
+    domain = lib.mkOption {
+      type = lib.types.str;
+      description = "The domain jitsi-meet should be served on.";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    nixpkgs.config.permittedInsecurePackages = [
+      # Jitsi uses libolm for E2EE, which is no longer maintained upstream by the element
+      # team (as they switch to a rust new based crypto library.)
+      #
+      # libolm has two CVEs about timing based side-channel attacks in their crypt
+      # primitives. This is not ideal, but it has not (yet) been exploited in the wild and
+      # upstream (i.e. the matrix/element team) claims, that the CVEs are very difficult to
+      # exploit (they have been know _long_ before element switched to the rust version).
+      #
+      # Considering the lack of deployable video conferencing alternatives, the active
+      # interest in upstream to resolve this issue [1] and the fact, that we are unlikely
+      # to be attacked via a target attack, permitting this package seems viable.
+      #
+      # [1]: https://github.com/jitsi/jitsi-meet/issues/15107
+      "jitsi-meet-1.0.8043"
+    ];
+
+    services = {
+      nginx.virtualHosts.${cfg.domain} = {
+        enableACME = true;
+        forceSSL = true;
+      };
+
+      jitsi-meet = {
+        enable = true;
+        hostName = cfg.domain;
+
+        nginx.enable = true;
+
+        config = {
+          enableWelcomePage = true;
+          requireDisplayName = true;
+          analytics.disabled = true;
+
+          # Don't try to GET gravata stuff.
+          disableThirdPartyRequests = true;
+
+          # Avoids a heavy load on conference start.
+          startAudioOnly = true;
+
+          # Only transmit the last four members.
+          channelLastN = 4;
+
+          constraints.video.height = {
+            ideal = 720;
+            max = 1080;
+            min = 240;
+          };
+
+          remoteVideoMenu.disabled = false;
+          breakoutRooms.hideAddRoomButton = false;
+          maxFullResolutionParticipants = 1;
+
+          prejoinPageEnabled = true;
+          defaultLang = "sv";
+        };
+
+        interfaceConfig = {
+          GENERATE_ROOMNAMES_ON_WELCOME_PAGE = false;
+          DISABLE_PRESENCE_STATUS = true;
+
+          SHOW_CHROME_EXTENSION_BANNER = false;
+
+          # The default google play android apps comes with trackers.
+          MOBILE_DOWNLOAD_LINK_ANDROID = "https://f-droid.org/en/packages/org.jitsi.meet/";
+
+          # Don't try to promote the mobile app.
+          MOBILE_APP_PROMO = false;
+
+          SHOW_JITSI_WATERMARK = false;
+          SHOW_WATERMARK_FOR_GUESTS = false;
+        };
+
+        prosody = {
+          enable = true;
+
+          # We only use prosody for jitsi XMPP communication, and therefore can remove support
+          # for general XMPP server stuff.
+          lockdown = true;
+        };
+      };
+
+      jitsi-videobridge = {
+        openFirewall = true;
+        config.videobridge = {
+          cc.assumed-bandwidth-limit = "1000 Mbps";
+        };
+      };
+    };
+  };
+}
diff --git a/tests/by-name/ji/jitsi-meet/test.nix b/tests/by-name/ji/jitsi-meet/test.nix
new file mode 100644
index 0000000..76d8539
--- /dev/null
+++ b/tests/by-name/ji/jitsi-meet/test.nix
@@ -0,0 +1,103 @@
+{
+  nixos-lib,
+  pkgsUnstable,
+  nixpkgs-unstable,
+  vhackPackages,
+  pkgs,
+  extraModules,
+  nixLib,
+  ...
+}:
+nixos-lib.runTest {
+  hostPkgs = pkgs;
+
+  name = "jitsi-meet";
+
+  node = {
+    specialArgs = {inherit pkgsUnstable extraModules vhackPackages nixpkgs-unstable nixLib;};
+
+    # Use the nixpkgs as constructed by the `nixpkgs.*` options
+    pkgs = null;
+  };
+
+  nodes = {
+    acme = {...}: {
+      imports = [
+        ../../../common/acme/server.nix
+        ../../../common/dns/client.nix
+      ];
+    };
+    name_server = {nodes, ...}: {
+      imports =
+        extraModules
+        ++ [
+          ../../../common/acme/client.nix
+          ../../../common/dns/server.nix
+        ];
+
+      vhack.dns.zones = {
+        "jitsi-meet.server" = {
+          SOA = {
+            nameServer = "ns";
+            adminEmail = "admin@server.com";
+            serial = 2025012301;
+          };
+          useOrigin = false;
+
+          A = [
+            nodes.server.networking.primaryIPAddress
+          ];
+          AAAA = [
+            nodes.server.networking.primaryIPv6Address
+          ];
+        };
+      };
+    };
+
+    server = {config, ...}: {
+      imports =
+        extraModules
+        ++ [
+          ../../../../modules
+          ../../../common/acme/client.nix
+          ../../../common/dns/client.nix
+        ];
+
+      vhack = {
+        nginx.enable = true;
+        jitsi-meet = {
+          enable = true;
+          domain = "jitsi-meet.server";
+        };
+      };
+    };
+
+    client = {...}: {
+      imports = [
+        ../../../common/acme/client.nix
+        ../../../common/dns/client.nix
+      ];
+    };
+  };
+
+  testScript = {nodes, ...}: let
+    acme = import ../../../common/acme {inherit pkgs;};
+  in
+    acme.prepare ["server" "client"]
+    # Python
+    ''
+      server.wait_for_unit("jitsi-videobridge.service")
+      server.wait_for_unit("jitsi-videobridge2.service")
+
+      with subtest("All services running"):
+        import json
+        def all_services_running(host):
+          (status, output) = host.systemctl("list-units --state=failed --plain --no-pager --output=json")
+          host_failed = json.loads(output)
+          assert len(host_failed) == 0, f"Expected zero failing services, but found: {json.dumps(host_failed, indent=4)}"
+        all_services_running(server)
+
+      client.wait_until_succeeds("curl --silent https://jitsi-meet.server")
+      client.succeed("curl --silent https://jitsi-meet.server | grep 'Join a WebRTC video conference'")
+    '';
+}