{ 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; }