diff --git a/nixos/module/hectic/service/jitsi.nix b/nixos/module/hectic/service/jitsi.nix new file mode 100644 index 0000000..5130203 --- /dev/null +++ b/nixos/module/hectic/service/jitsi.nix @@ -0,0 +1,97 @@ +{ + inputs, + flake, + self, +}: +{ + pkgs, + lib, + config, + ... +}: let + cfg = config.hectic.services.jitsi; +in { + options = { + hectic.services.jitsi = { + enable = lib.mkEnableOption "Jitsi Meet video conferencing with Prosody XMPP backend"; + hostName = lib.mkOption { + type = lib.types.str; + description = '' + FQDN for the Jitsi Meet instance (e.g. "meet.example.org"). + Prosody VirtualHosts, nginx, and ACME certs are derived from this. + ''; + }; + secureDomain = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Require authentication to create rooms. Guests can still join + existing rooms anonymously. + ''; + }; + lockdown = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Restrict Prosody to localhost only (no S2S federation, c2s + only on 127.0.0.1). Set to false when running alongside a + general-purpose XMPP server (hectic.services.xmpp). + ''; + }; + videobridgePasswordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + Path to a file containing the Jitsi Videobridge XMPP password. + If null, a random password is auto-generated. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + services.jitsi-meet = { + enable = true; + hostName = cfg.hostName; + + prosody = { + enable = true; + lockdown = cfg.lockdown; + }; + + nginx.enable = true; + videobridge = { + enable = true; + } // lib.optionalAttrs (cfg.videobridgePasswordFile != null) { + passwordFile = cfg.videobridgePasswordFile; + }; + jicofo.enable = true; + + secureDomain = lib.mkIf cfg.secureDomain { + enable = true; + }; + }; + + services.jitsi-videobridge.openFirewall = true; + + services.nginx.virtualHosts.${cfg.hostName} = { + enableACME = true; + forceSSL = true; + }; + + security.acme = { + acceptTerms = true; + defaults = { + email = "hectic.yukkop.it@gmail.com"; + enableDebugLogs = true; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ + 80 443 # HTTP/HTTPS (nginx + ACME) + 5222 # XMPP c2s (if not locked down) + ]; + }; + }; +} diff --git a/nixos/module/hectic/service/xmpp.nix b/nixos/module/hectic/service/xmpp.nix new file mode 100644 index 0000000..2c7450b --- /dev/null +++ b/nixos/module/hectic/service/xmpp.nix @@ -0,0 +1,119 @@ +{ + inputs, + flake, + self, +}: +{ + pkgs, + lib, + config, + ... +}: let + cfg = config.hectic.services.xmpp; +in { + options = { + hectic.services.xmpp = { + enable = lib.mkEnableOption "General-purpose XMPP server (Prosody) for messaging clients like Monocles Chat"; + domain = lib.mkOption { + type = lib.types.str; + description = '' + Primary XMPP domain. Users will have JIDs like user@domain. + ''; + }; + admins = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + example = [ "admin@example.org" ]; + description = '' + List of admin JIDs. + ''; + }; + allowRegistration = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Allow in-band account registration from clients. + If false, create accounts with: prosodyctl register + ''; + }; + uploadFileSizeLimit = lib.mkOption { + type = lib.types.int; + default = 10485760; + description = '' + Maximum file upload size in bytes (default 10 MiB). + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + services.prosody = { + enable = true; + admins = cfg.admins; + allowRegistration = cfg.allowRegistration; + + ssl = { + cert = "/var/lib/acme/${cfg.domain}/fullchain.pem"; + key = "/var/lib/acme/${cfg.domain}/key.pem"; + }; + + virtualHosts.${cfg.domain} = { + enabled = true; + domain = cfg.domain; + ssl = { + cert = "/var/lib/acme/${cfg.domain}/fullchain.pem"; + key = "/var/lib/acme/${cfg.domain}/key.pem"; + }; + }; + + muc = [ + { domain = "conference.${cfg.domain}"; } + ]; + + httpFileShare = { + domain = "upload.${cfg.domain}"; + size_limit = cfg.uploadFileSizeLimit; + }; + }; + + # Grant prosody read access to ACME certs (group is "nginx" since + # the nginx vhost requests the cert via enableACME) + users.users.prosody.extraGroups = [ "nginx" ]; + + # nginx vhost handles ACME HTTP-01 challenge for the XMPP domain. + # The cert also covers conference.* and upload.* subdomains. + services.nginx = { + enable = true; + virtualHosts.${cfg.domain} = { + enableACME = true; + forceSSL = true; + locations."/".return = "301 https://meet.${cfg.domain}"; + }; + }; + + security.acme = { + acceptTerms = true; + defaults = { + email = "hectic.yukkop.it@gmail.com"; + enableDebugLogs = true; + }; + # Add MUC + upload subdomains to the nginx-managed cert + certs.${cfg.domain} = { + extraDomainNames = [ + "conference.${cfg.domain}" + "upload.${cfg.domain}" + ]; + reloadServices = [ "prosody" ]; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ + 5222 # c2s (client-to-server) + 5269 # s2s (server-to-server federation) + 80 # ACME HTTP-01 challenge + 443 # HTTPS + ]; + }; + }; +} diff --git a/nixos/system/neuro/default.nix b/nixos/system/neuro/default.nix index 7f63e35..389a518 100644 --- a/nixos/system/neuro/default.nix +++ b/nixos/system/neuro/default.nix @@ -40,6 +40,10 @@ in self.lib.nixpkgs-lib.nixosSystem { "libcusparse" "cudnn" ]; + # jitsi-meet depends on libolm which is marked insecure (CVE-2024-4519x) + config.permittedInsecurePackages = [ + "jitsi-meet-1.0.8792" + ]; }; modules = [ { networking.hostName = name; } diff --git a/nixos/system/neuro/neuro.nix b/nixos/system/neuro/neuro.nix index ea93381..deb1c93 100644 --- a/nixos/system/neuro/neuro.nix +++ b/nixos/system/neuro/neuro.nix @@ -63,6 +63,17 @@ # matrixDomain = "accord.tube"; #}; + hectic.services.jitsi = { + enable = true; + hostName = "meet.accord.tube"; + }; + + hectic.services.xmpp = { + enable = true; + domain = "accord.tube"; + admins = [ "yukkop@accord.tube" ]; + }; + networking = { networkmanager.enable = true; useDHCP = lib.mkDefault true; @@ -91,13 +102,13 @@ age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; defaultSopsFile = ../../../sus/neuro.yaml; - secrets."init-postgresql" = {}; - secrets."matrix/secrets" = {}; - secrets."matrix/turn-secret" = { - owner = "turnserver"; - group = "turnserver"; - mode = "0400"; - }; + #secrets."init-postgresql" = {}; + #secrets."matrix/secrets" = {}; + #secrets."matrix/turn-secret" = { + # owner = "turnserver"; + # group = "turnserver"; + # mode = "0400"; + #}; }; boot.loader.systemd-boot.enable = true;