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