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