Merge pull request #137003 from yayayayaka/add-pkg-jigasi

jigasi: init at 1.1-311-g3de47d0 + module
This commit is contained in:
Lassulus 2023-12-09 02:28:50 +01:00 committed by GitHub
commit b8ca5f4f46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 322 additions and 3 deletions

View file

@ -429,6 +429,7 @@ with lib.maintainers; {
cleeyv
ryantm
lassulus
yayayayaka
];
scope = "Maintain Jitsi.";
shortName = "Jitsi";

View file

@ -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

View file

@ -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, <option>xmppHost</option> 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
<literal>JIGASI_SIPUSER</literal>,
<literal>JIGASI_SIPPWD</literal>,
<literal>JIGASI_SIPSERVER</literal> and
<literal>JIGASI_SIPPORT</literal>.
'';
};
config = mkOption {
type = attrsOf str;
default = { };
example = literalExample ''
{
"org.jitsi.jigasi.auth.URL" = "XMPP:jitsi-meet.example.com";
}
'';
description = ''
Contents of the <filename>sip-communicator.properties</filename> 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 <<EOF >>"$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;
}

View file

@ -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 <option>services.jigasi</option>.
'';
};
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;

View file

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

View file

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