at 24.11-pre 9.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.jitsi-videobridge; 7 attrsToArgs = a: concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") a); 8 9 format = pkgs.formats.hocon { }; 10 11 # We're passing passwords in environment variables that have names generated 12 # from an attribute name, which may not be a valid bash identifier. 13 toVarName = s: "XMPP_PASSWORD_" + stringAsChars (c: if builtins.match "[A-Za-z0-9]" c != null then c else "_") s; 14 15 defaultJvbConfig = { 16 videobridge = { 17 ice = { 18 tcp = { 19 enabled = true; 20 port = 4443; 21 }; 22 udp.port = 10000; 23 }; 24 stats = { 25 enabled = true; 26 transports = [ { type = "muc"; } ]; 27 }; 28 apis.xmpp-client.configs = flip mapAttrs cfg.xmppConfigs (name: xmppConfig: { 29 hostname = xmppConfig.hostName; 30 domain = xmppConfig.domain; 31 username = xmppConfig.userName; 32 password = format.lib.mkSubstitution (toVarName name); 33 muc_jids = xmppConfig.mucJids; 34 muc_nickname = xmppConfig.mucNickname; 35 disable_certificate_verification = xmppConfig.disableCertificateVerification; 36 }); 37 apis.rest.enabled = cfg.colibriRestApi; 38 }; 39 }; 40 41 # Allow overriding leaves of the default config despite types.attrs not doing any merging. 42 jvbConfig = recursiveUpdate defaultJvbConfig cfg.config; 43in 44{ 45 imports = [ 46 (mkRemovedOptionModule [ "services" "jitsi-videobridge" "apis" ] 47 "services.jitsi-videobridge.apis was broken and has been migrated into the boolean option services.jitsi-videobridge.colibriRestApi. It is set to false by default, setting it to true will correctly enable the private /colibri rest API." 48 ) 49 ]; 50 options.services.jitsi-videobridge = with types; { 51 enable = mkEnableOption "Jitsi Videobridge, a WebRTC compatible video router"; 52 53 config = mkOption { 54 type = attrs; 55 default = { }; 56 example = literalExpression '' 57 { 58 videobridge = { 59 ice.udp.port = 5000; 60 websockets = { 61 enabled = true; 62 server-id = "jvb1"; 63 }; 64 }; 65 } 66 ''; 67 description = '' 68 Videobridge configuration. 69 70 See <https://github.com/jitsi/jitsi-videobridge/blob/master/jvb/src/main/resources/reference.conf> 71 for default configuration with comments. 72 ''; 73 }; 74 75 xmppConfigs = mkOption { 76 description = '' 77 XMPP servers to connect to. 78 79 See <https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md> for more information. 80 ''; 81 default = { }; 82 example = literalExpression '' 83 { 84 "localhost" = { 85 hostName = "localhost"; 86 userName = "jvb"; 87 domain = "auth.xmpp.example.org"; 88 passwordFile = "/var/lib/jitsi-meet/videobridge-secret"; 89 mucJids = "jvbbrewery@internal.xmpp.example.org"; 90 }; 91 } 92 ''; 93 type = attrsOf (submodule ({ name, ... }: { 94 options = { 95 hostName = mkOption { 96 type = str; 97 example = "xmpp.example.org"; 98 description = '' 99 Hostname of the XMPP server to connect to. Name of the attribute set is used by default. 100 ''; 101 }; 102 domain = mkOption { 103 type = nullOr str; 104 default = null; 105 example = "auth.xmpp.example.org"; 106 description = '' 107 Domain part of JID of the XMPP user, if it is different from hostName. 108 ''; 109 }; 110 userName = mkOption { 111 type = str; 112 default = "jvb"; 113 description = '' 114 User part of the JID. 115 ''; 116 }; 117 passwordFile = mkOption { 118 type = str; 119 example = "/run/keys/jitsi-videobridge-xmpp1"; 120 description = '' 121 File containing the password for the user. 122 ''; 123 }; 124 mucJids = mkOption { 125 type = str; 126 example = "jvbbrewery@internal.xmpp.example.org"; 127 description = '' 128 JID of the MUC to join. JiCoFo needs to be configured to join the same MUC. 129 ''; 130 }; 131 mucNickname = mkOption { 132 # Upstream DEBs use UUID, let's use hostname instead. 133 type = str; 134 description = '' 135 Videobridges use the same XMPP account and need to be distinguished by the 136 nickname (aka resource part of the JID). By default, system hostname is used. 137 ''; 138 }; 139 disableCertificateVerification = mkOption { 140 type = bool; 141 default = false; 142 description = '' 143 Whether to skip validation of the server's certificate. 144 ''; 145 }; 146 }; 147 config = { 148 hostName = mkDefault name; 149 mucNickname = mkDefault (builtins.replaceStrings [ "." ] [ "-" ] ( 150 config.networking.fqdnOrHostName 151 )); 152 }; 153 })); 154 }; 155 156 nat = { 157 localAddress = mkOption { 158 type = nullOr str; 159 default = null; 160 example = "192.168.1.42"; 161 description = '' 162 Local address when running behind NAT. 163 ''; 164 }; 165 166 publicAddress = mkOption { 167 type = nullOr str; 168 default = null; 169 example = "1.2.3.4"; 170 description = '' 171 Public address when running behind NAT. 172 ''; 173 }; 174 }; 175 176 extraProperties = mkOption { 177 type = attrsOf str; 178 default = { }; 179 description = '' 180 Additional Java properties passed to jitsi-videobridge. 181 ''; 182 }; 183 184 openFirewall = mkOption { 185 type = bool; 186 default = false; 187 description = '' 188 Whether to open ports in the firewall for the videobridge. 189 ''; 190 }; 191 192 colibriRestApi = mkOption { 193 type = bool; 194 description = '' 195 Whether to enable the private rest API for the COLIBRI control interface. 196 Needed for monitoring jitsi, enabling scraping of the /colibri/stats endpoint. 197 ''; 198 default = false; 199 }; 200 }; 201 202 config = mkIf cfg.enable { 203 users.groups.jitsi-meet = {}; 204 205 services.jitsi-videobridge.extraProperties = optionalAttrs (cfg.nat.localAddress != null) { 206 "org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS" = cfg.nat.localAddress; 207 "org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS" = cfg.nat.publicAddress; 208 }; 209 210 systemd.services.jitsi-videobridge2 = let 211 jvbProps = { 212 "-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi"; 213 "-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "videobridge"; 214 "-Djava.util.logging.config.file" = "/etc/jitsi/videobridge/logging.properties"; 215 "-Dconfig.file" = format.generate "jvb.conf" jvbConfig; 216 # Mitigate CVE-2021-44228 217 "-Dlog4j2.formatMsgNoLookups" = true; 218 } // (mapAttrs' (k: v: nameValuePair "-D${k}" v) cfg.extraProperties); 219 in 220 { 221 aliases = [ "jitsi-videobridge.service" ]; 222 description = "Jitsi Videobridge"; 223 after = [ "network.target" ]; 224 wantedBy = [ "multi-user.target" ]; 225 226 environment.JAVA_SYS_PROPS = attrsToArgs jvbProps; 227 228 script = (concatStrings (mapAttrsToList (name: xmppConfig: 229 "export ${toVarName name}=$(cat ${xmppConfig.passwordFile})\n" 230 ) cfg.xmppConfigs)) 231 + '' 232 ${pkgs.jitsi-videobridge}/bin/jitsi-videobridge 233 ''; 234 235 serviceConfig = { 236 Type = "exec"; 237 238 DynamicUser = true; 239 User = "jitsi-videobridge"; 240 Group = "jitsi-meet"; 241 242 CapabilityBoundingSet = ""; 243 NoNewPrivileges = true; 244 ProtectSystem = "strict"; 245 ProtectHome = true; 246 PrivateTmp = true; 247 PrivateDevices = true; 248 ProtectHostname = true; 249 ProtectKernelTunables = true; 250 ProtectKernelModules = true; 251 ProtectControlGroups = true; 252 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; 253 RestrictNamespaces = true; 254 LockPersonality = true; 255 RestrictRealtime = true; 256 RestrictSUIDSGID = true; 257 258 TasksMax = 65000; 259 LimitNPROC = 65000; 260 LimitNOFILE = 65000; 261 }; 262 }; 263 264 environment.etc."jitsi/videobridge/logging.properties".source = 265 mkDefault "${pkgs.jitsi-videobridge}/etc/jitsi/videobridge/logging.properties-journal"; 266 267 # (from videobridge2 .deb) 268 # this sets the max, so that we can bump the JVB UDP single port buffer size. 269 boot.kernel.sysctl."net.core.rmem_max" = mkDefault 10485760; 270 boot.kernel.sysctl."net.core.netdev_max_backlog" = mkDefault 100000; 271 272 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall 273 [ jvbConfig.videobridge.ice.tcp.port ]; 274 networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall 275 [ jvbConfig.videobridge.ice.udp.port ]; 276 277 assertions = [{ 278 message = "publicAddress must be set if and only if localAddress is set"; 279 assertion = (cfg.nat.publicAddress == null) == (cfg.nat.localAddress == null); 280 }]; 281 }; 282 283 meta.maintainers = lib.teams.jitsi.members; 284}