parent
b128516ef1
commit
c695d57895
@ -0,0 +1,276 @@ |
||||
{ config, lib, pkgs, ... }: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
cfg = config.services.jitsi-videobridge; |
||||
attrsToArgs = a: concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") a); |
||||
|
||||
# HOCON is a JSON superset that videobridge2 uses for configuration. |
||||
# It can substitute environment variables which we use for passwords here. |
||||
# https://github.com/lightbend/config/blob/master/README.md |
||||
# |
||||
# Substitution for environment variable FOO is represented as attribute set |
||||
# { __hocon_envvar = "FOO"; } |
||||
toHOCON = x: if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}") |
||||
else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}" |
||||
else if isList x then "[${ concatMapStringsSep "," toHOCON x }]" |
||||
else builtins.toJSON x; |
||||
|
||||
# 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_" + 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 = flip mapAttrs cfg.xmppConfigs (name: xmppConfig: { |
||||
hostname = xmppConfig.hostName; |
||||
domain = xmppConfig.domain; |
||||
username = xmppConfig.userName; |
||||
password = { __hocon_envvar = toVarName name; }; |
||||
muc_jids = xmppConfig.mucJids; |
||||
muc_nickname = xmppConfig.mucNickname; |
||||
disable_certificate_verification = xmppConfig.disableCertificateVerification; |
||||
}); |
||||
}; |
||||
}; |
||||
|
||||
# Allow overriding leaves of the default config despite types.attrs not doing any merging. |
||||
jvbConfig = recursiveUpdate defaultJvbConfig cfg.config; |
||||
in |
||||
{ |
||||
options.services.jitsi-videobridge = with types; { |
||||
enable = mkEnableOption "Jitsi Videobridge, a WebRTC compatible video router"; |
||||
|
||||
config = mkOption { |
||||
type = attrs; |
||||
default = { }; |
||||
example = literalExample '' |
||||
{ |
||||
videobridge = { |
||||
ice.udp.port = 5000; |
||||
websockets = { |
||||
enabled = true; |
||||
server-id = "jvb1"; |
||||
}; |
||||
}; |
||||
} |
||||
''; |
||||
description = '' |
||||
Videobridge configuration. |
||||
|
||||
See <link xlink:href="https://github.com/jitsi/jitsi-videobridge/blob/master/src/main/resources/reference.conf" /> |
||||
for default configuration with comments. |
||||
''; |
||||
}; |
||||
|
||||
xmppConfigs = mkOption { |
||||
description = '' |
||||
XMPP servers to connect to. |
||||
|
||||
See <link xlink:href="https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md" /> for more information. |
||||
''; |
||||
default = { }; |
||||
example = literalExample '' |
||||
{ |
||||
"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 = 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 = 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 = mkOption { |
||||
type = str; |
||||
default = "jvb"; |
||||
description = '' |
||||
User part of the JID. |
||||
''; |
||||
}; |
||||
passwordFile = mkOption { |
||||
type = str; |
||||
example = "/run/keys/jitsi-videobridge-xmpp1"; |
||||
description = '' |
||||
File containing the password for the user. |
||||
''; |
||||
}; |
||||
mucJids = 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 = 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 = mkOption { |
||||
type = bool; |
||||
default = false; |
||||
description = '' |
||||
Whether to skip validation of the server's certificate. |
||||
''; |
||||
}; |
||||
}; |
||||
config = { |
||||
hostName = mkDefault name; |
||||
mucNickname = mkDefault (builtins.replaceStrings [ "." ] [ "-" ] ( |
||||
config.networking.hostName + optionalString (config.networking.domain != null) ".${config.networking.domain}" |
||||
)); |
||||
}; |
||||
})); |
||||
}; |
||||
|
||||
nat = { |
||||
localAddress = mkOption { |
||||
type = nullOr str; |
||||
default = null; |
||||
example = "192.168.1.42"; |
||||
description = '' |
||||
Local address when running behind NAT. |
||||
''; |
||||
}; |
||||
|
||||
publicAddress = mkOption { |
||||
type = nullOr str; |
||||
default = null; |
||||
example = "1.2.3.4"; |
||||
description = '' |
||||
Public address when running behind NAT. |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
extraProperties = mkOption { |
||||
type = attrsOf str; |
||||
default = { }; |
||||
description = '' |
||||
Additional Java properties passed to jitsi-videobridge. |
||||
''; |
||||
}; |
||||
|
||||
openFirewall = mkOption { |
||||
type = bool; |
||||
default = false; |
||||
description = '' |
||||
Whether to open ports in the firewall for the videobridge. |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
config = mkIf cfg.enable { |
||||
users.groups.jitsi-meet = {}; |
||||
|
||||
services.jitsi-videobridge.extraProperties = optionalAttrs (cfg.nat.localAddress != null) { |
||||
"org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS" = cfg.nat.localAddress; |
||||
"org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS" = cfg.nat.publicAddress; |
||||
}; |
||||
|
||||
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" = pkgs.writeText "jvb.conf" (toHOCON jvbConfig); |
||||
} // (mapAttrs' (k: v: 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 = (concatStrings (mapAttrsToList (name: xmppConfig: |
||||
"export ${toVarName name}=$(cat ${xmppConfig.passwordFile})\n" |
||||
) cfg.xmppConfigs)) |
||||
+ '' |
||||
${pkgs.jitsi-videobridge}/bin/jitsi-videobridge --apis=none |
||||
''; |
||||
|
||||
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 = |
||||
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" = mkDefault 10485760; |
||||
boot.kernel.sysctl."net.core.netdev_max_backlog" = mkDefault 100000; |
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall |
||||
[ jvbConfig.videobridge.ice.tcp.port ]; |
||||
networking.firewall.allowedUDPPorts = 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 = with lib.maintainers; [ ]; |
||||
} |
Loading…
Reference in new issue