Unverified Commit b8ca5f4f authored by Lassulus's avatar Lassulus Committed by GitHub
Browse files

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

jigasi: init at 1.1-311-g3de47d0 + module
parents 59766684 2797a55a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -429,6 +429,7 @@ with lib.maintainers; {
      cleeyv
      ryantm
      lassulus
      yayayayaka
    ];
    scope = "Maintain Jitsi.";
    shortName = "Jitsi";
+1 −0
Original line number Diff line number Diff line
@@ -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
+237 −0
Original line number Diff line number Diff line
{ 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;
}
+35 −3
Original line number Diff line number Diff line
@@ -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;
+46 −0
Original line number Diff line number Diff line
{ 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;
  };
}
Loading