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'")
+ '';
+}
|