diff --git a/maintainers/team-list.nix b/maintainers/team-list.nix
index b872142c313c..9885c9cfcf99 100644
--- a/maintainers/team-list.nix
+++ b/maintainers/team-list.nix
@@ -429,6 +429,7 @@ with lib.maintainers; {
cleeyv
ryantm
lassulus
+ yayayayaka
];
scope = "Maintain Jitsi.";
shortName = "Jitsi";
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 674707ec69e7..820ce54ddeb6 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -971,6 +971,7 @@
./services/networking/iwd.nix
./services/networking/jibri/default.nix
./services/networking/jicofo.nix
+ ./services/networking/jigasi.nix
./services/networking/jitsi-videobridge.nix
./services/networking/jool.nix
./services/networking/kea.nix
diff --git a/nixos/modules/services/networking/jigasi.nix b/nixos/modules/services/networking/jigasi.nix
new file mode 100644
index 000000000000..8d2d25c6edfc
--- /dev/null
+++ b/nixos/modules/services/networking/jigasi.nix
@@ -0,0 +1,237 @@
+{ 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 = literalExample ''
+ {
+ "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;
+}
diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix
index 0c0eb66e65b7..c4505534d635 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -35,6 +35,7 @@ let
domain = cfg.hostName;
muc = "conference.${cfg.hostName}";
focus = "focus.${cfg.hostName}";
+ jigasi = "jigasi.${cfg.hostName}";
};
bosh = "//${cfg.hostName}/http-bind";
websocket = "wss://${cfg.hostName}/xmpp-websocket";
@@ -145,6 +146,16 @@ in
'';
};
+ jigasi.enable = mkOption {
+ type = bool;
+ default = false;
+ description = ''
+ Whether to enable jigasi instance and configure it to connect to Prosody.
+
+ Additional configuration is possible with .
+ '';
+ };
+
nginx.enable = mkOption {
type = bool;
default = true;
@@ -224,7 +235,7 @@ in
roomDefaultPublicJids = true;
extraConfig = ''
storage = "memory"
- admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}" }
+ admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}", "jigasi@auth.${cfg.hostName}" }
'';
#-- muc_room_cache_size = 1000
}
@@ -263,6 +274,9 @@ in
Component "focus.${cfg.hostName}" "client_proxy"
target_address = "focus@auth.${cfg.hostName}"
+ Component "jigasi.${cfg.hostName}" "client_proxy"
+ target_address = "jigasi@auth.${cfg.hostName}"
+
Component "speakerstats.${cfg.hostName}" "speakerstats_component"
muc_component = "conference.${cfg.hostName}"
@@ -356,7 +370,10 @@ in
${config.services.prosody.package}/bin/prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName}
${config.services.prosody.package}/bin/prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)"
${config.services.prosody.package}/bin/prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"
+ '' + optionalString cfg.jigasi.enable ''
+ ${config.services.prosody.package}/bin/prosodyctl register jigasi auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jigasi-user-secret)"
'';
+
serviceConfig = {
EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ];
SupplementaryGroups = [ "jitsi-meet" ];
@@ -371,13 +388,13 @@ in
systemd.services.jitsi-meet-init-secrets = {
wantedBy = [ "multi-user.target" ];
- before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service");
+ before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service") ++ (optional cfg.jigasi.enable "jigasi.service");
serviceConfig = {
Type = "oneshot";
};
script = let
- secrets = [ "jicofo-component-secret" "jicofo-user-secret" "jibri-auth-secret" "jibri-recorder-secret" ] ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret");
+ secrets = [ "jicofo-component-secret" "jicofo-user-secret" "jibri-auth-secret" "jibri-recorder-secret" ] ++ (optionals cfg.jigasi.enable [ "jigasi-user-secret" "jigasi-component-secret" ]) ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret");
in
''
cd /var/lib/jitsi-meet
@@ -391,6 +408,7 @@ in
# for easy access in prosody
echo "JICOFO_COMPONENT_SECRET=$(cat jicofo-component-secret)" > secrets-env
+ echo "JIGASI_COMPONENT_SECRET=$(cat jigasi-component-secret)" >> secrets-env
chown root:jitsi-meet secrets-env
chmod 640 secrets-env
''
@@ -592,6 +610,20 @@ in
stripFromRoomDomain = "conference.";
};
};
+
+ services.jigasi = mkIf cfg.jigasi.enable {
+ enable = true;
+ xmppHost = "localhost";
+ xmppDomain = cfg.hostName;
+ userDomain = "auth.${cfg.hostName}";
+ userName = "jigasi";
+ userPasswordFile = "/var/lib/jitsi-meet/jigasi-user-secret";
+ componentPasswordFile = "/var/lib/jitsi-meet/jigasi-component-secret";
+ bridgeMuc = "jigasibrewery@internal.${cfg.hostName}";
+ config = {
+ "org.jitsi.jigasi.ALWAYS_TRUST_MODE_ENABLED" = "true";
+ };
+ };
};
meta.doc = ./jitsi-meet.md;
diff --git a/pkgs/servers/jigasi/default.nix b/pkgs/servers/jigasi/default.nix
new file mode 100644
index 000000000000..d9f8ed60c09b
--- /dev/null
+++ b/pkgs/servers/jigasi/default.nix
@@ -0,0 +1,46 @@
+{ lib, stdenv, fetchurl, dpkg, jdk11, nixosTests }:
+
+let
+ pname = "jigasi";
+ version = "1.1-311-g3de47d0";
+ src = fetchurl {
+ url = "https://download.jitsi.org/stable/${pname}_${version}-1_all.deb";
+ hash = "sha256-pwUgkId7AHFjbqYo02fBgm0gsiMqEz+wvwkdy6sgTD0=";
+ };
+in
+stdenv.mkDerivation {
+ inherit pname version src;
+
+ nativeBuildInputs = [ dpkg ];
+
+ dontBuild = true;
+
+ unpackCmd = "dpkg-deb -x $src debcontents";
+
+ installPhase = ''
+ runHook preInstall
+ substituteInPlace usr/share/${pname}/${pname}.sh \
+ --replace "exec java" "exec ${jdk11}/bin/java"
+
+ mkdir -p $out/{share,bin}
+ mv usr/share/${pname} $out/share/
+ mv etc $out/
+ ln -s $out/share/${pname}/${pname}.sh $out/bin/${pname}
+ runHook postInstall
+ '';
+
+ passthru.tests = {
+ single-node-smoke-test = nixosTests.jitsi-meet;
+ };
+
+ meta = with lib; {
+ description = "A server-side application that allows regular SIP clients to join Jitsi Meet conferences";
+ longDescription = ''
+ Jitsi Gateway to SIP: a server-side application that allows regular SIP clients to join Jitsi Meet conferences hosted by Jitsi Videobridge.
+ '';
+ homepage = "https://github.com/jitsi/jigasi";
+ license = licenses.asl20;
+ maintainers = teams.jitsi.members;
+ platforms = platforms.linux;
+ };
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index e4a45f63f44c..c72fc1749793 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -26587,6 +26587,8 @@ with pkgs;
jitsi-excalidraw = callPackage ../servers/jitsi-excalidraw { };
+ jigasi = callPackage ../servers/jigasi { };
+
jitsi-meet = callPackage ../servers/web-apps/jitsi-meet { };
jitsi-meet-prosody = callPackage ../misc/jitsi-meet-prosody { };