{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.jigasi;
homeDirName = "jigasi-home";
stateDir = "/tmp";
sipCommunicatorPropertiesFile = "${stateDir}/${homeDirName}/sip-communicator.properties";
sipCommunicatorPropertiesFileUnsubstituted = "${pkgs.jigasi}/etc/jitsi/jigasi/sip-communicator.properties";
in
{
options.services.jigasi = with types; {
enable = mkEnableOption "Jitsi Gateway to SIP - component of Jitsi Meet";
xmppHost = mkOption {
type = str;
example = "localhost";
description = ''
Hostname of the XMPP server to connect to.
'';
};
xmppDomain = mkOption {
type = nullOr str;
example = "meet.example.org";
description = ''
Domain name of the XMMP server to which to connect as a component.
If null, is used.
'';
};
componentPasswordFile = mkOption {
type = str;
example = "/run/keys/jigasi-component";
description = ''
Path to file containing component secret.
'';
};
userName = mkOption {
type = str;
default = "callcontrol";
description = ''
User part of the JID for XMPP user connection.
'';
};
userDomain = mkOption {
type = str;
example = "internal.meet.example.org";
description = ''
Domain part of the JID for XMPP user connection.
'';
};
userPasswordFile = mkOption {
type = str;
example = "/run/keys/jigasi-user";
description = ''
Path to file containing password for XMPP user connection.
'';
};
bridgeMuc = mkOption {
type = str;
example = "jigasibrewery@internal.meet.example.org";
description = ''
JID of the internal MUC used to communicate with Videobridges.
'';
};
defaultJvbRoomName = mkOption {
type = str;
default = "";
example = "siptest";
description = ''
Name of the default JVB room that will be joined if no special header is included in SIP invite.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
File containing environment variables to be passed to the jigasi service,
in which secret tokens can be specified securely by defining values for
JIGASI_SIPUSER,
JIGASI_SIPPWD,
JIGASI_SIPSERVER and
JIGASI_SIPPORT.
'';
};
config = mkOption {
type = attrsOf str;
default = { };
example = literalExpression ''
{
"org.jitsi.jigasi.auth.URL" = "XMPP:jitsi-meet.example.com";
}
'';
description = ''
Contents of the sip-communicator.properties configuration file for jigasi.
'';
};
};
config = mkIf cfg.enable {
services.jicofo.config = {
"org.jitsi.jicofo.jigasi.BREWERY" = "${cfg.bridgeMuc}";
};
services.jigasi.config = mapAttrs (_: v: mkDefault v) {
"org.jitsi.jigasi.BRIDGE_MUC" = cfg.bridgeMuc;
};
users.groups.jitsi-meet = {};
systemd.services.jigasi = let
jigasiProps = {
"-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "${stateDir}";
"-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "${homeDirName}";
"-Djava.util.logging.config.file" = "${pkgs.jigasi}/etc/jitsi/jigasi/logging.properties";
};
in
{
description = "Jitsi Gateway to SIP";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
preStart = ''
[ -f "${sipCommunicatorPropertiesFile}" ] && rm -f "${sipCommunicatorPropertiesFile}"
mkdir -p "$(dirname ${sipCommunicatorPropertiesFile})"
temp="${sipCommunicatorPropertiesFile}.unsubstituted"
export DOMAIN_BASE="${cfg.xmppDomain}"
export JIGASI_XMPP_PASSWORD=$(cat "${cfg.userPasswordFile}")
export JIGASI_DEFAULT_JVB_ROOM_NAME="${cfg.defaultJvbRoomName}"
# encode the credentials to base64
export JIGASI_SIPPWD=$(echo -n "$JIGASI_SIPPWD" | base64 -w 0)
export JIGASI_XMPP_PASSWORD_BASE64=$(cat "${cfg.userPasswordFile}" | base64 -w 0)
cp "${sipCommunicatorPropertiesFileUnsubstituted}" "$temp"
chmod 644 "$temp"
cat <>"$temp"
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.SERVER_PORT=$JIGASI_SIPPORT
net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PREFERRED_TRANSPORT=udp
EOF
chmod 444 "$temp"
# Replace <<$VAR_NAME>> from example config to $VAR_NAME for environment substitution
sed -i -E \
's/<<([^>]+)>>/\$\1/g' \
"$temp"
sed -i \
's|\(net\.java\.sip\.communicator\.impl\.protocol\.jabber\.acc-xmpp-1\.PASSWORD=\).*|\1\$JIGASI_XMPP_PASSWORD_BASE64|g' \
"$temp"
sed -i \
's|\(#\)\(org.jitsi.jigasi.DEFAULT_JVB_ROOM_NAME=\).*|\2\$JIGASI_DEFAULT_JVB_ROOM_NAME|g' \
"$temp"
${pkgs.envsubst}/bin/envsubst \
-o "${sipCommunicatorPropertiesFile}" \
-i "$temp"
# Set the brewery room name
sed -i \
's|\(net\.java\.sip\.communicator\.impl\.protocol\.jabber\.acc-xmpp-1\.BREWERY=\).*|\1${cfg.bridgeMuc}|g' \
"${sipCommunicatorPropertiesFile}"
sed -i \
's|\(org\.jitsi\.jigasi\.ALLOWED_JID=\).*|\1${cfg.bridgeMuc}|g' \
"${sipCommunicatorPropertiesFile}"
# Disable certificate verification for self-signed certificates
sed -i \
's|\(# \)\(net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true\)|\2|g' \
"${sipCommunicatorPropertiesFile}"
'';
restartTriggers = [
config.environment.etc."jitsi/jigasi/sip-communicator.properties".source
];
environment.JAVA_SYS_PROPS = concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") jigasiProps);
script = ''
${pkgs.jigasi}/bin/jigasi \
--host="${cfg.xmppHost}" \
--domain="${if cfg.xmppDomain == null then cfg.xmppHost else cfg.xmppDomain}" \
--secret="$(cat ${cfg.componentPasswordFile})" \
--user_name="${cfg.userName}" \
--user_domain="${cfg.userDomain}" \
--user_password="$(cat ${cfg.userPasswordFile})" \
--configdir="${stateDir}" \
--configdirname="${homeDirName}"
'';
serviceConfig = {
Type = "exec";
DynamicUser = true;
User = "jigasi";
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;
StateDirectory = baseNameOf stateDir;
EnvironmentFile = cfg.environmentFile;
};
};
environment.etc."jitsi/jigasi/sip-communicator.properties".source =
mkDefault "${sipCommunicatorPropertiesFile}";
environment.etc."jitsi/jigasi/logging.properties".source =
mkDefault "${stateDir}/logging.properties-journal";
};
meta.maintainers = lib.teams.jitsi.members;
}