nixpkgs/nixos/modules/services/networking/jitsi-videobridge.nix
Silvan Mosberger 4f0dadbf38 treewide: format all inactive Nix files
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.

Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.

A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.

This commit was automatically created and can be verified using

    nix-build a08b3a4d19.tar.gz \
      --argstr baseRev b32a094368
    result/bin/apply-formatting $NIXPKGS_PATH
2024-12-10 20:26:33 +01:00

334 lines
10 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.jitsi-videobridge;
attrsToArgs = a: lib.concatStringsSep " " (lib.mapAttrsToList (k: v: "${k}=${toString v}") a);
format = pkgs.formats.hocon { };
# We're passing passwords in environment variables that have names generated
# from an attribute name, which may not be a valid bash identifier.
toVarName =
s:
"XMPP_PASSWORD_"
+ lib.stringAsChars (c: if builtins.match "[A-Za-z0-9]" c != null then c else "_") s;
defaultJvbConfig = {
videobridge = {
ice = {
tcp = {
enabled = true;
port = 4443;
};
udp.port = 10000;
};
stats = {
enabled = true;
transports = [ { type = "muc"; } ];
};
apis.xmpp-client.configs = lib.flip lib.mapAttrs cfg.xmppConfigs (
name: xmppConfig: {
hostname = xmppConfig.hostName;
domain = xmppConfig.domain;
username = xmppConfig.userName;
password = format.lib.mkSubstitution (toVarName name);
muc_jids = xmppConfig.mucJids;
muc_nickname = xmppConfig.mucNickname;
disable_certificate_verification = xmppConfig.disableCertificateVerification;
}
);
apis.rest.enabled = cfg.colibriRestApi;
};
};
# Allow overriding leaves of the default config despite types.attrs not doing any merging.
jvbConfig = lib.recursiveUpdate defaultJvbConfig cfg.config;
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "jitsi-videobridge" "apis" ]
"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."
)
];
options.services.jitsi-videobridge = with lib.types; {
enable = lib.mkEnableOption "Jitsi Videobridge, a WebRTC compatible video router";
config = lib.mkOption {
type = attrs;
default = { };
example = lib.literalExpression ''
{
videobridge = {
ice.udp.port = 5000;
websockets = {
enabled = true;
server-id = "jvb1";
};
};
}
'';
description = ''
Videobridge configuration.
See <https://github.com/jitsi/jitsi-videobridge/blob/master/jvb/src/main/resources/reference.conf>
for default configuration with comments.
'';
};
xmppConfigs = lib.mkOption {
description = ''
XMPP servers to connect to.
See <https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md> for more information.
'';
default = { };
example = lib.literalExpression ''
{
"localhost" = {
hostName = "localhost";
userName = "jvb";
domain = "auth.xmpp.example.org";
passwordFile = "/var/lib/jitsi-meet/videobridge-secret";
mucJids = "jvbbrewery@internal.xmpp.example.org";
};
}
'';
type = attrsOf (
submodule (
{ name, ... }:
{
options = {
hostName = lib.mkOption {
type = str;
example = "xmpp.example.org";
description = ''
Hostname of the XMPP server to connect to. Name of the attribute set is used by default.
'';
};
domain = lib.mkOption {
type = nullOr str;
default = null;
example = "auth.xmpp.example.org";
description = ''
Domain part of JID of the XMPP user, if it is different from hostName.
'';
};
userName = lib.mkOption {
type = str;
default = "jvb";
description = ''
User part of the JID.
'';
};
passwordFile = lib.mkOption {
type = str;
example = "/run/keys/jitsi-videobridge-xmpp1";
description = ''
File containing the password for the user.
'';
};
mucJids = lib.mkOption {
type = str;
example = "jvbbrewery@internal.xmpp.example.org";
description = ''
JID of the MUC to join. JiCoFo needs to be configured to join the same MUC.
'';
};
mucNickname = lib.mkOption {
# Upstream DEBs use UUID, let's use hostname instead.
type = str;
description = ''
Videobridges use the same XMPP account and need to be distinguished by the
nickname (aka resource part of the JID). By default, system hostname is used.
'';
};
disableCertificateVerification = lib.mkOption {
type = bool;
default = false;
description = ''
Whether to skip validation of the server's certificate.
'';
};
};
config = {
hostName = lib.mkDefault name;
mucNickname = lib.mkDefault (
builtins.replaceStrings [ "." ] [ "-" ] (config.networking.fqdnOrHostName)
);
};
}
)
);
};
nat = {
localAddress = lib.mkOption {
type = nullOr str;
default = null;
example = "192.168.1.42";
description = ''
Local address to assume when running behind NAT.
'';
};
publicAddress = lib.mkOption {
type = nullOr str;
default = null;
example = "1.2.3.4";
description = ''
Public address to assume when running behind NAT.
'';
};
harvesterAddresses = lib.mkOption {
type = listOf str;
default = [
"stunserver.stunprotocol.org:3478"
"stun.framasoft.org:3478"
"meet-jit-si-turnrelay.jitsi.net:443"
];
example = [ ];
description = ''
Addresses of public STUN services to use to automatically find
the public and local addresses of this Jitsi-Videobridge instance
without the need for manual configuration.
This option is ignored if {option}`services.jitsi-videobridge.nat.localAddress`
and {option}`services.jitsi-videobridge.nat.publicAddress` are set.
'';
};
};
extraProperties = lib.mkOption {
type = attrsOf str;
default = { };
description = ''
Additional Java properties passed to jitsi-videobridge.
'';
};
openFirewall = lib.mkOption {
type = bool;
default = false;
description = ''
Whether to open ports in the firewall for the videobridge.
'';
};
colibriRestApi = lib.mkOption {
type = bool;
description = ''
Whether to enable the private rest API for the COLIBRI control interface.
Needed for monitoring jitsi, enabling scraping of the /colibri/stats endpoint.
'';
default = false;
};
};
config = lib.mkIf cfg.enable {
users.groups.jitsi-meet = { };
services.jitsi-videobridge.extraProperties =
if (cfg.nat.localAddress != null) then
{
"org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS" = cfg.nat.localAddress;
"org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS" = cfg.nat.publicAddress;
}
else
{
"org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES" =
lib.concatStringsSep "," cfg.nat.harvesterAddresses;
};
systemd.services.jitsi-videobridge2 =
let
jvbProps = {
"-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi";
"-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "videobridge";
"-Djava.util.logging.config.file" = "/etc/jitsi/videobridge/logging.properties";
"-Dconfig.file" = format.generate "jvb.conf" jvbConfig;
# Mitigate CVE-2021-44228
"-Dlog4j2.formatMsgNoLookups" = true;
} // (lib.mapAttrs' (k: v: lib.nameValuePair "-D${k}" v) cfg.extraProperties);
in
{
aliases = [ "jitsi-videobridge.service" ];
description = "Jitsi Videobridge";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment.JAVA_SYS_PROPS = attrsToArgs jvbProps;
script =
(lib.concatStrings (
lib.mapAttrsToList (
name: xmppConfig: "export ${toVarName name}=$(cat ${xmppConfig.passwordFile})\n"
) cfg.xmppConfigs
))
+ ''
${pkgs.jitsi-videobridge}/bin/jitsi-videobridge
'';
serviceConfig = {
Type = "exec";
DynamicUser = true;
User = "jitsi-videobridge";
Group = "jitsi-meet";
CapabilityBoundingSet = "";
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
ProtectHostname = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
LockPersonality = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
TasksMax = 65000;
LimitNPROC = 65000;
LimitNOFILE = 65000;
};
};
environment.etc."jitsi/videobridge/logging.properties".source =
lib.mkDefault "${pkgs.jitsi-videobridge}/etc/jitsi/videobridge/logging.properties-journal";
# (from videobridge2 .deb)
# this sets the max, so that we can bump the JVB UDP single port buffer size.
boot.kernel.sysctl."net.core.rmem_max" = lib.mkDefault 10485760;
boot.kernel.sysctl."net.core.netdev_max_backlog" = lib.mkDefault 100000;
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
jvbConfig.videobridge.ice.tcp.port
];
networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [
jvbConfig.videobridge.ice.udp.port
];
assertions = [
{
message = "publicAddress must be set if and only if localAddress is set";
assertion = (cfg.nat.publicAddress == null) == (cfg.nat.localAddress == null);
}
];
};
meta.maintainers = lib.teams.jitsi.members;
}