From ff8ed90033f17d754b4609edf2cc58a99cb7c73a Mon Sep 17 00:00:00 2001 From: Cleeyv Date: Thu, 23 Sep 2021 20:30:10 -0400 Subject: nixos/jitsi-meet: add jibri.enable This option enables a jibri service on the same host that is running jitsi-meet. It was written, along with the jibri module, by @puckipedia for nixcon-video-infra 2020. Co-authored-by: Puck Meerburg --- .../modules/services/networking/jibri/default.nix | 353 +++++++++++++++++++++ nixos/modules/services/web-apps/jitsi-meet.nix | 71 ++++- 2 files changed, 419 insertions(+), 5 deletions(-) create mode 100644 nixos/modules/services/networking/jibri/default.nix (limited to 'nixos/modules/services') diff --git a/nixos/modules/services/networking/jibri/default.nix b/nixos/modules/services/networking/jibri/default.nix new file mode 100644 index 00000000000..6075676c1bd --- /dev/null +++ b/nixos/modules/services/networking/jibri/default.nix @@ -0,0 +1,353 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.jibri; + + # Copied from the jitsi-videobridge.nix file. + toHOCON = x: + if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}") + else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}" + else if isList x then "[${ concatMapStringsSep "," toHOCON x }]" + else builtins.toJSON x; + + # We're passing passwords in environment variables that have names generated + # from an attribute name, which may not be a valid bash identifier. + toVarName = s: "XMPP_PASSWORD_" + stringAsChars (c: if builtins.match "[A-Za-z0-9]" c != null then c else "_") s; + + defaultJibriConfig = { + id = ""; + single-use-mode = false; + + api = { + http.external-api-port = 2222; + http.internal-api-port = 3333; + + xmpp.environments = flip mapAttrsToList cfg.xmppEnvironments (name: env: { + inherit name; + + xmpp-server-hosts = env.xmppServerHosts; + xmpp-domain = env.xmppDomain; + control-muc = { + domain = env.control.muc.domain; + room-name = env.control.muc.roomName; + nickname = env.control.muc.nickname; + }; + + control-login = { + domain = env.control.login.domain; + username = env.control.login.username; + password.__hocon_envvar = toVarName "${name}_control"; + }; + + call-login = { + domain = env.call.login.domain; + username = env.call.login.username; + password.__hocon_envvar = toVarName "${name}_call"; + }; + + strip-from-room-domain = env.stripFromRoomDomain; + usage-timeout = env.usageTimeout; + trust-all-xmpp-certs = env.disableCertificateVerification; + }); + }; + + recording = { + recordings-directory = "/tmp/recordings"; + finalize-script = "${cfg.finalizeScript}"; + }; + + streaming.rtmp-allow-list = [ ".*" ]; + + chrome.flags = [ + "--use-fake-ui-for-media-stream" + "--start-maximized" + "--kiosk" + "--enabled" + "--disable-infobars" + "--autoplay-policy=no-user-gesture-required" + ]; + + stats.enable-stats-d = true; + webhook.subscribers = [ ]; + + jwt-info = { }; + + call-status-checks = { + no-media-timout = "30 seconds"; + all-muted-timeout = "10 minutes"; + default-call-empty-timout = "30 seconds"; + }; + }; + # Allow overriding leaves of the default config despite types.attrs not doing any merging. + jibriConfig = recursiveUpdate defaultJibriConfig cfg.config; + configFile = pkgs.writeText "jibri.conf" (toHOCON { jibri = jibriConfig; }); +in +{ + options.services.jibri = with types; { + enable = mkEnableOption "Jitsi BRoadcasting Infrastructure. Currently the only supported way of enabling a jibri instance is with "; + config = mkOption { + type = attrs; + default = { }; + description = '' + Jibri configuration. + See + for default configuration with comments. + ''; + }; + + finalizeScript = mkOption { + type = types.path; + default = pkgs.writeScript "finalize_recording.sh" '' + #!/bin/sh + + RECORDINGS_DIR=$1 + + echo "This is a dummy finalize script" > /tmp/finalize.out + echo "The script was invoked with recordings directory $RECORDINGS_DIR." >> /tmp/finalize.out + echo "You should put any finalize logic (renaming, uploading to a service" >> /tmp/finalize.out + echo "or storage provider, etc.) in this script" >> /tmp/finalize.out + + exit 0 + ''; + description = '' + This script runs when jibri finishes recording a video of a conference. + ''; + }; + + xmppEnvironments = mkOption { + description = '' + XMPP servers to connect to. + ''; + default = { }; + type = attrsOf (submodule ({ name, ... }: { + options = { + xmppServerHosts = mkOption { + type = listOf str; + example = [ "xmpp.example.org" ]; + description = '' + Hostnames of the XMPP servers to connect to. + ''; + }; + xmppDomain = mkOption { + type = str; + example = "xmpp.example.org"; + description = '' + The base XMPP domain. + ''; + }; + control.muc.domain = mkOption { + type = str; + description = '' + The domain part of the MUC to connect to for control. + ''; + }; + control.muc.roomName = mkOption { + type = str; + default = "JibriBrewery"; + description = '' + The room name of the MUC to connect to for control. + ''; + }; + control.muc.nickname = mkOption { + type = str; + default = "jibri"; + description = '' + The nickname for this Jibri instance in the MUC. + ''; + }; + control.login.domain = mkOption { + type = str; + description = '' + The domain part of the JID for this Jibri instance. + ''; + }; + control.login.username = mkOption { + type = str; + default = "jvb"; + description = '' + User part of the JID. + ''; + }; + control.login.passwordFile = mkOption { + type = str; + example = "/run/keys/jibri-xmpp1"; + description = '' + File containing the password for the user. + ''; + }; + + call.login.domain = mkOption { + type = str; + example = "recorder.xmpp.example.org"; + description = '' + The domain part of the JID for the recorder. + ''; + }; + call.login.username = mkOption { + type = str; + default = "recorder"; + description = '' + User part of the JID for the recorder. + ''; + }; + call.login.passwordFile = mkOption { + type = str; + example = "/run/keys/jibri-recorder-xmpp1"; + description = '' + File containing the password for the user. + ''; + }; + disableCertificateVerification = mkOption { + type = bool; + default = false; + description = '' + Whether to skip validation of the server's certificate. + ''; + }; + + stripFromRoomDomain = mkOption { + type = str; + default = "0"; + example = "conference."; + description = '' + The prefix to strip from the room's JID domain to derive the call URL. + ''; + }; + usageTimeout = mkOption { + type = str; + default = "0"; + example = "1 hour"; + description = '' + The duration that the Jibri session can be. + A value of zero means indefinitely. + ''; + }; + }; + + config = + let + nick = mkDefault (builtins.replaceStrings [ "." ] [ "-" ] ( + config.networking.hostName + optionalString (config.networking.domain != null) ".${config.networking.domain}" + )); + in + { + call.login.username = nick; + control.muc.nickname = nick; + }; + })); + }; + }; + + config = mkIf cfg.enable { + users.groups.jibri = { }; + users.users.jibri = { + isSystemUser = true; + group = "jibri"; + home = "/var/lib/jibri"; + extraGroups = [ "jitsi-meet" "adm" "audio" "video" "plugdev" ]; + }; + + systemd.services.jibri-xorg = { + description = "Jitsi Xorg Process"; + + after = [ "network.target" ]; + wantedBy = [ "jibri.service" "jibri-icewm.service" ]; + + preStart = '' + cp --no-preserve=mode,ownership ${pkgs.jibri}/etc/jitsi/jibri/* /var/lib/jibri + mv /var/lib/jibri/{,.}asoundrc + ''; + + environment.DISPLAY = ":0"; + serviceConfig = { + Type = "simple"; + + User = "jibri"; + Group = "jibri"; + KillMode = "process"; + Restart = "on-failure"; + RestartPreventExitStatus = 255; + + StateDirectory = "jibri"; + + ExecStart = "${pkgs.xorg.xorgserver}/bin/Xorg -nocursor -noreset +extension RANDR +extension RENDER -config ${pkgs.jibri}/etc/jitsi/jibri/xorg-video-dummy.conf -logfile /dev/null :0"; + }; + }; + + systemd.services.jibri-icewm = { + description = "Jitsi Window Manager"; + + requires = [ "jibri-xorg.service" ]; + after = [ "jibri-xorg.service" ]; + wantedBy = [ "jibri.service" ]; + + environment.DISPLAY = ":0"; + serviceConfig = { + Type = "simple"; + + User = "jibri"; + Group = "jibri"; + Restart = "on-failure"; + RestartPreventExitStatus = 255; + + StateDirectory = "jibri"; + + ExecStart = "${pkgs.icewm}/bin/icewm-session"; + }; + }; + + systemd.services.jibri = { + description = "Jibri Process"; + + requires = [ "jibri-icewm.service" "jibri-xorg.service" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + path = [ pkgs.chromedriver pkgs.chromium pkgs.ffmpeg-full ]; + + script = (concatStrings (mapAttrsToList + (name: env: '' + export ${toVarName "${name}_control"}=$(cat ${env.control.login.passwordFile}) + export ${toVarName "${name}_call"}=$(cat ${env.call.login.passwordFile}) + '') + cfg.xmppEnvironments)) + + '' + ${pkgs.jre8_headless}/bin/java -Djava.util.logging.config.file=${./logging.properties-journal} -Dconfig.file=${configFile} -jar ${pkgs.jibri}/opt/jitsi/jibri/jibri.jar --config /var/lib/jibri/jibri.json + ''; + + environment.HOME = "/var/lib/jibri"; + + serviceConfig = { + Type = "simple"; + + User = "jibri"; + Group = "jibri"; + Restart = "always"; + RestartPreventExitStatus = 255; + + StateDirectory = "jibri"; + }; + }; + + systemd.tmpfiles.rules = + [ + "d /var/log/jitsi/jibri 755 jibri" + ]; + + + + # Configure Chromium to not show the "Chrome is being controlled by automatic test software" message. + environment.etc."chromium/policies/managed/managed_policies.json".text = builtins.toJSON { CommandLineFlagSecurityWarningsEnabled = false; }; + + boot = { + extraModprobeConfig = '' + options snd-aloop enable=1,1,1,1,1,1,1,1 + ''; + kernelModules = [ "snd-aloop" ]; + }; + }; + + meta.maintainers = with lib.maintainers; [ ]; +} diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix index 2eacd87ae6f..8dc611c32e3 100644 --- a/nixos/modules/services/web-apps/jitsi-meet.nix +++ b/nixos/modules/services/web-apps/jitsi-meet.nix @@ -38,6 +38,10 @@ let }; bosh = "//${cfg.hostName}/http-bind"; websocket = "wss://${cfg.hostName}/xmpp-websocket"; + + fileRecordingsEnabled = true; + liveStreamingEnabled = true; + hiddenDomain = "recorder.${cfg.hostName}"; }; in { @@ -130,6 +134,18 @@ in ''; }; + jibri.enable = mkOption { + type = bool; + default = false; + description = '' + Whether to enable a Jibri instance and configure it to connect to Prosody. + + Although additional configuration is possible with , this is + currently not very supported and most users will only want to edit the finalize recordings + script at . + ''; + }; + nginx.enable = mkOption { type = bool; default = true; @@ -229,6 +245,14 @@ in key = "/var/lib/jitsi-meet/jitsi-meet.key"; }; }; + virtualHosts."recorder.${cfg.hostName}" = { + enabled = true; + domain = "recorder.${cfg.hostName}"; + extraConfig = '' + authentication = "internal_plain" + c2s_require_encryption = false + ''; + }; }; systemd.services.prosody.serviceConfig = mkIf cfg.prosody.enable { EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ]; @@ -243,12 +267,13 @@ in systemd.services.jitsi-meet-init-secrets = { wantedBy = [ "multi-user.target" ]; before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service"); + path = [ config.services.prosody.package ]; serviceConfig = { Type = "oneshot"; }; script = let - secrets = [ "jicofo-component-secret" "jicofo-user-secret" ] ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret"); + secrets = [ "jicofo-component-secret" "jicofo-user-secret" "jibri-auth-secret" "jibri-recorder-secret" ] ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret"); videobridgeSecret = if cfg.videobridge.passwordFile != null then cfg.videobridge.passwordFile else "/var/lib/jitsi-meet/videobridge-secret"; in '' @@ -267,9 +292,11 @@ in chmod 640 secrets-env '' + optionalString cfg.prosody.enable '' - ${config.services.prosody.package}/bin/prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)" - ${config.services.prosody.package}/bin/prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})" - ${config.services.prosody.package}/bin/prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName} + prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)" + prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})" + prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName} + prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)" + prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)" # generate self-signed certificates if [ ! -f /var/lib/jitsi-meet.crt ]; then @@ -380,8 +407,42 @@ in userPasswordFile = "/var/lib/jitsi-meet/jicofo-user-secret"; componentPasswordFile = "/var/lib/jitsi-meet/jicofo-component-secret"; bridgeMuc = "jvbbrewery@internal.${cfg.hostName}"; - config = { + config = mkMerge [{ "org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED" = "true"; + } (lib.mkIf cfg.jibri.enable { + "org.jitsi.jicofo.jibri.BREWERY" = "JibriBrewery@internal.${cfg.hostName}"; + "org.jitsi.jicofo.jibri.PENDING_TIMEOUT" = "90"; + })]; + }; + + services.jibri = mkIf cfg.jibri.enable { + enable = true; + + xmppEnvironments."jitsi-meet" = { + xmppServerHosts = [ "localhost" ]; + xmppDomain = cfg.hostName; + + control.muc = { + domain = "internal.${cfg.hostName}"; + roomName = "JibriBrewery"; + nickname = "jibri"; + }; + + control.login = { + domain = "auth.${cfg.hostName}"; + username = "jibri"; + passwordFile = "/var/lib/jitsi-meet/jibri-auth-secret"; + }; + + call.login = { + domain = "recorder.${cfg.hostName}"; + username = "recorder"; + passwordFile = "/var/lib/jitsi-meet/jibri-recorder-secret"; + }; + + usageTimeout = "0"; + disableCertificateVerification = true; + stripFromRoomDomain = "conference."; }; }; }; -- cgit 1.4.1 From 3473cff4b0ff09f29388558c0f54e7b44f98e2e8 Mon Sep 17 00:00:00 2001 From: Cleeyv Date: Thu, 23 Sep 2021 21:12:12 -0400 Subject: nixos/jibri: init at 8.0-93-g51fe7a2 This module was written by @puckipedia for nixcon-video-infra 2020. Minor changes made by @cleeyv for compat with existing jibri package. Co-authored-by: Puck Meerburg --- nixos/modules/module-list.nix | 1 + .../modules/services/networking/jibri/default.nix | 23 ++++++++++++---- .../networking/jibri/logging.properties-journal | 32 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 nixos/modules/services/networking/jibri/logging.properties-journal (limited to 'nixos/modules/services') diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d4aaa7ebd22..41a7db17c32 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -756,6 +756,7 @@ ./services/networking/iscsi/root-initiator.nix ./services/networking/iscsi/target.nix ./services/networking/iwd.nix + ./services/networking/jibri/default.nix ./services/networking/jicofo.nix ./services/networking/jitsi-videobridge.nix ./services/networking/kea.nix diff --git a/nixos/modules/services/networking/jibri/default.nix b/nixos/modules/services/networking/jibri/default.nix index 6075676c1bd..cae5ee9931e 100644 --- a/nixos/modules/services/networking/jibri/default.nix +++ b/nixos/modules/services/networking/jibri/default.nix @@ -55,7 +55,11 @@ let recording = { recordings-directory = "/tmp/recordings"; +<<<<<<< HEAD finalize-script = "${cfg.finalizeScript}"; +======= + finalize-script = "/path/to/finalize"; # TODO(puck): replace with actual noop default +>>>>>>> a1dc2ddd630 (nixos/jibri: init at 8.0-93-g51fe7a2) }; streaming.rtmp-allow-list = [ ".*" ]; @@ -116,6 +120,8 @@ in ''; }; +======= +>>>>>>> a1dc2ddd630 (nixos/jibri: init at 8.0-93-g51fe7a2) xmppEnvironments = mkOption { description = '' XMPP servers to connect to. @@ -242,6 +248,7 @@ in config = mkIf cfg.enable { users.groups.jibri = { }; + users.groups.plugdev = { }; users.users.jibri = { isSystemUser = true; group = "jibri"; @@ -305,7 +312,7 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.chromedriver pkgs.chromium pkgs.ffmpeg-full ]; + path = with pkgs; [ chromedriver chromium ffmpeg-full ]; script = (concatStrings (mapAttrsToList (name: env: '' @@ -314,7 +321,11 @@ in '') cfg.xmppEnvironments)) + '' +<<<<<<< HEAD ${pkgs.jre8_headless}/bin/java -Djava.util.logging.config.file=${./logging.properties-journal} -Dconfig.file=${configFile} -jar ${pkgs.jibri}/opt/jitsi/jibri/jibri.jar --config /var/lib/jibri/jibri.json +======= + ${pkgs.jre_headless}/bin/java -Djava.util.logging.config.file=${./logging.properties-journal} -Dconfig.file=${configFile} -jar ${pkgs.jibri}/opt/jitsi/jibri/jibri.jar --config /var/lib/jibri/jibri.json +>>>>>>> a1dc2ddd630 (nixos/jibri: init at 8.0-93-g51fe7a2) ''; environment.HOME = "/var/lib/jibri"; @@ -331,15 +342,15 @@ in }; }; - systemd.tmpfiles.rules = - [ - "d /var/log/jitsi/jibri 755 jibri" - ]; + systemd.tmpfiles.rules = [ + "d /var/log/jitsi/jibri 755 jibri jibri" + ]; # Configure Chromium to not show the "Chrome is being controlled by automatic test software" message. environment.etc."chromium/policies/managed/managed_policies.json".text = builtins.toJSON { CommandLineFlagSecurityWarningsEnabled = false; }; + warnings = [ "All security warnings for Chromium have been disabled. This is necessary for Jibri, but it also impacts all other uses of Chromium on this system." ]; boot = { extraModprobeConfig = '' @@ -349,5 +360,5 @@ in }; }; - meta.maintainers = with lib.maintainers; [ ]; + meta.maintainers = lib.teams.jitsi.members; } diff --git a/nixos/modules/services/networking/jibri/logging.properties-journal b/nixos/modules/services/networking/jibri/logging.properties-journal new file mode 100644 index 00000000000..61eadbfddcb --- /dev/null +++ b/nixos/modules/services/networking/jibri/logging.properties-journal @@ -0,0 +1,32 @@ +handlers = java.util.logging.FileHandler + +java.util.logging.FileHandler.level = FINE +java.util.logging.FileHandler.pattern = /var/log/jitsi/jibri/log.%g.txt +java.util.logging.FileHandler.formatter = net.java.sip.communicator.util.ScLogFormatter +java.util.logging.FileHandler.count = 10 +java.util.logging.FileHandler.limit = 10000000 + +org.jitsi.jibri.capture.ffmpeg.util.FfmpegFileHandler.level = FINE +org.jitsi.jibri.capture.ffmpeg.util.FfmpegFileHandler.pattern = /var/log/jitsi/jibri/ffmpeg.%g.txt +org.jitsi.jibri.capture.ffmpeg.util.FfmpegFileHandler.formatter = net.java.sip.communicator.util.ScLogFormatter +org.jitsi.jibri.capture.ffmpeg.util.FfmpegFileHandler.count = 10 +org.jitsi.jibri.capture.ffmpeg.util.FfmpegFileHandler.limit = 10000000 + +org.jitsi.jibri.sipgateway.pjsua.util.PjsuaFileHandler.level = FINE +org.jitsi.jibri.sipgateway.pjsua.util.PjsuaFileHandler.pattern = /var/log/jitsi/jibri/pjsua.%g.txt +org.jitsi.jibri.sipgateway.pjsua.util.PjsuaFileHandler.formatter = net.java.sip.communicator.util.ScLogFormatter +org.jitsi.jibri.sipgateway.pjsua.util.PjsuaFileHandler.count = 10 +org.jitsi.jibri.sipgateway.pjsua.util.PjsuaFileHandler.limit = 10000000 + +org.jitsi.jibri.selenium.util.BrowserFileHandler.level = FINE +org.jitsi.jibri.selenium.util.BrowserFileHandler.pattern = /var/log/jitsi/jibri/browser.%g.txt +org.jitsi.jibri.selenium.util.BrowserFileHandler.formatter = net.java.sip.communicator.util.ScLogFormatter +org.jitsi.jibri.selenium.util.BrowserFileHandler.count = 10 +org.jitsi.jibri.selenium.util.BrowserFileHandler.limit = 10000000 + +org.jitsi.level = FINE +org.jitsi.jibri.config.level = INFO + +org.glassfish.level = INFO +org.osgi.level = INFO +org.jitsi.xmpp.level = INFO -- cgit 1.4.1 From 57bd54d28b15b871d9aac758a0f5e3686f5bce2b Mon Sep 17 00:00:00 2001 From: Cleeyv Date: Fri, 1 Oct 2021 15:12:05 -0400 Subject: nixos/jibri: add finalize script option --- .../modules/services/networking/jibri/default.nix | 32 +++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'nixos/modules/services') diff --git a/nixos/modules/services/networking/jibri/default.nix b/nixos/modules/services/networking/jibri/default.nix index cae5ee9931e..b4e41a29282 100644 --- a/nixos/modules/services/networking/jibri/default.nix +++ b/nixos/modules/services/networking/jibri/default.nix @@ -55,11 +55,7 @@ let recording = { recordings-directory = "/tmp/recordings"; -<<<<<<< HEAD finalize-script = "${cfg.finalizeScript}"; -======= - finalize-script = "/path/to/finalize"; # TODO(puck): replace with actual noop default ->>>>>>> a1dc2ddd630 (nixos/jibri: init at 8.0-93-g51fe7a2) }; streaming.rtmp-allow-list = [ ".*" ]; @@ -115,13 +111,33 @@ in exit 0 ''; + defaultText = literalExpression '' + pkgs.writeScript "finalize_recording.sh" '''''' + #!/bin/sh + + RECORDINGS_DIR=$1 + + echo "This is a dummy finalize script" > /tmp/finalize.out + echo "The script was invoked with recordings directory $RECORDINGS_DIR." >> /tmp/finalize.out + echo "You should put any finalize logic (renaming, uploading to a service" >> /tmp/finalize.out + echo "or storage provider, etc.) in this script" >> /tmp/finalize.out + + exit 0 + ''''''; + ''; + example = literalExpression '' + pkgs.writeScript "finalize_recording.sh" '''''' + #!/bin/sh + RECORDINGS_DIR=$1 + ${pkgs.rclone}/bin/rclone copy $RECORDINGS_DIR RCLONE_REMOTE:jibri-recordings/ -v --log-file=/var/log/jitsi/jibri/recording-upload.txt + exit 0 + ''''''; + ''; description = '' This script runs when jibri finishes recording a video of a conference. ''; }; -======= ->>>>>>> a1dc2ddd630 (nixos/jibri: init at 8.0-93-g51fe7a2) xmppEnvironments = mkOption { description = '' XMPP servers to connect to. @@ -321,11 +337,7 @@ in '') cfg.xmppEnvironments)) + '' -<<<<<<< HEAD ${pkgs.jre8_headless}/bin/java -Djava.util.logging.config.file=${./logging.properties-journal} -Dconfig.file=${configFile} -jar ${pkgs.jibri}/opt/jitsi/jibri/jibri.jar --config /var/lib/jibri/jibri.json -======= - ${pkgs.jre_headless}/bin/java -Djava.util.logging.config.file=${./logging.properties-journal} -Dconfig.file=${configFile} -jar ${pkgs.jibri}/opt/jitsi/jibri/jibri.jar --config /var/lib/jibri/jibri.json ->>>>>>> a1dc2ddd630 (nixos/jibri: init at 8.0-93-g51fe7a2) ''; environment.HOME = "/var/lib/jibri"; -- cgit 1.4.1 From 917c5fae70f9d8ec43653e5a6e3ea764ce7bd2c8 Mon Sep 17 00:00:00 2001 From: Cleeyv Date: Mon, 11 Oct 2021 07:46:08 -0700 Subject: nixos/jibri: fix & docs for enable not via meet --- .../modules/services/networking/jibri/default.nix | 30 +++++++++++++++++++++- nixos/modules/services/web-apps/jitsi-meet.nix | 10 ++++---- 2 files changed, 34 insertions(+), 6 deletions(-) (limited to 'nixos/modules/services') diff --git a/nixos/modules/services/networking/jibri/default.nix b/nixos/modules/services/networking/jibri/default.nix index b4e41a29282..4aa60e31052 100644 --- a/nixos/modules/services/networking/jibri/default.nix +++ b/nixos/modules/services/networking/jibri/default.nix @@ -86,7 +86,7 @@ let in { options.services.jibri = with types; { - enable = mkEnableOption "Jitsi BRoadcasting Infrastructure. Currently the only supported way of enabling a jibri instance is with "; + enable = mkEnableOption "Jitsi BRoadcasting Infrastructure. Currently Jibri must be run on a host that is also running , so for most use cases it will be simpler to run "; config = mkOption { type = attrs; default = { }; @@ -142,6 +142,34 @@ in description = '' XMPP servers to connect to. ''; + example = literalExpression '' + "jitsi-meet" = { + xmppServerHosts = [ "localhost" ]; + xmppDomain = config.services.jitsi-meet.hostName; + + control.muc = { + domain = "internal.''${config.services.jitsi-meet.hostName}"; + roomName = "JibriBrewery"; + nickname = "jibri"; + }; + + control.login = { + domain = "auth.''${config.services.jitsi-meet.hostName}"; + username = "jibri"; + passwordFile = "/var/lib/jitsi-meet/jibri-auth-secret"; + }; + + call.login = { + domain = "recorder.''${config.services.jitsi-meet.hostName}"; + username = "recorder"; + passwordFile = "/var/lib/jitsi-meet/jibri-recorder-secret"; + }; + + usageTimeout = "0"; + disableCertificateVerification = true; + stripFromRoomDomain = "conference."; + }; + ''; default = { }; type = attrsOf (submodule ({ name, ... }: { options = { diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix index 8dc611c32e3..2f1c4acec1e 100644 --- a/nixos/modules/services/web-apps/jitsi-meet.nix +++ b/nixos/modules/services/web-apps/jitsi-meet.nix @@ -52,7 +52,7 @@ in type = str; example = "meet.example.org"; description = '' - Hostname of the Jitsi Meet instance. + FQDN of the Jitsi Meet instance. ''; }; @@ -140,9 +140,8 @@ in description = '' Whether to enable a Jibri instance and configure it to connect to Prosody. - Although additional configuration is possible with , this is - currently not very supported and most users will only want to edit the finalize recordings - script at . + Additional configuration is possible with , and + is especially useful. ''; }; @@ -409,7 +408,8 @@ in bridgeMuc = "jvbbrewery@internal.${cfg.hostName}"; config = mkMerge [{ "org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED" = "true"; - } (lib.mkIf cfg.jibri.enable { + #} (lib.mkIf cfg.jibri.enable { + } (lib.mkIf (config.services.jibri.enable || cfg.jibri.enable) { "org.jitsi.jicofo.jibri.BREWERY" = "JibriBrewery@internal.${cfg.hostName}"; "org.jitsi.jicofo.jibri.PENDING_TIMEOUT" = "90"; })]; -- cgit 1.4.1 From 29f4cb4b0accf703f042bd3f5a54dd4225ae20c1 Mon Sep 17 00:00:00 2001 From: Cleeyv Date: Fri, 15 Oct 2021 11:00:24 -0700 Subject: nixos/jibri: add nixos test --- .../modules/services/networking/jibri/default.nix | 15 ++++- nixos/tests/all-tests.nix | 1 + nixos/tests/jibri.nix | 69 ++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 nixos/tests/jibri.nix (limited to 'nixos/modules/services') diff --git a/nixos/modules/services/networking/jibri/default.nix b/nixos/modules/services/networking/jibri/default.nix index 4aa60e31052..96832b0eb55 100644 --- a/nixos/modules/services/networking/jibri/default.nix +++ b/nixos/modules/services/networking/jibri/default.nix @@ -67,7 +67,10 @@ let "--enabled" "--disable-infobars" "--autoplay-policy=no-user-gesture-required" - ]; + ] + ++ lists.optional cfg.ignoreCert + "--ignore-certificate-errors"; + stats.enable-stats-d = true; webhook.subscribers = [ ]; @@ -138,6 +141,16 @@ in ''; }; + ignoreCert = mkOption { + type = bool; + default = false; + example = true; + description = '' + Whether to enable the flag "--ignore-certificate-errors" for the Chromium browser opened by Jibri. + Intended for use in automated tests or anywhere else where using a verified cert for Jitsi-Meet is not possible. + ''; + }; + xmppEnvironments = mkOption { description = '' XMPP servers to connect to. diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 945d42344a3..9e5dbf1052b 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -207,6 +207,7 @@ in jackett = handleTest ./jackett.nix {}; jellyfin = handleTest ./jellyfin.nix {}; jenkins = handleTest ./jenkins.nix {}; + jibri = handleTest ./jibri.nix {}; jirafeau = handleTest ./jirafeau.nix {}; jitsi-meet = handleTest ./jitsi-meet.nix {}; k3s = handleTest ./k3s.nix {}; diff --git a/nixos/tests/jibri.nix b/nixos/tests/jibri.nix new file mode 100644 index 00000000000..3dd28e6aac1 --- /dev/null +++ b/nixos/tests/jibri.nix @@ -0,0 +1,69 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "jibri"; + meta = with pkgs.lib; { + maintainers = teams.jitsi.members; + }; + + machine = { config, pkgs, ... }: { + virtualisation.memorySize = 5120; + + services.jitsi-meet = { + enable = true; + hostName = "machine"; + jibri.enable = true; + }; + services.jibri.ignoreCert = true; + services.jitsi-videobridge.openFirewall = true; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx.virtualHosts.machine = { + enableACME = true; + forceSSL = true; + }; + + security.acme.email = "me@example.org"; + security.acme.acceptTerms = true; + security.acme.server = "https://example.com"; # self-signed only + }; + + testScript = '' + machine.wait_for_unit("jitsi-videobridge2.service") + machine.wait_for_unit("jicofo.service") + machine.wait_for_unit("nginx.service") + machine.wait_for_unit("prosody.service") + machine.wait_for_unit("jibri.service") + + machine.wait_until_succeeds( + "journalctl -b -u jitsi-videobridge2 -o cat | grep -q 'Performed a successful health check'", timeout=30 + ) + machine.wait_until_succeeds( + "journalctl -b -u prosody -o cat | grep -q 'Authenticated as focus@auth.machine'", timeout=31 + ) + machine.wait_until_succeeds( + "journalctl -b -u prosody -o cat | grep -q 'Authenticated as jvb@auth.machine'", timeout=32 + ) + machine.wait_until_succeeds( + "journalctl -b -u prosody -o cat | grep -q 'Authenticated as jibri@auth.machine'", timeout=33 + ) + machine.wait_until_succeeds( + "cat /var/log/jitsi/jibri/log.0.txt | grep -q 'Joined MUC: jibribrewery@internal.machine'", timeout=34 + ) + + assert '"busyStatus":"IDLE","health":{"healthStatus":"HEALTHY"' in machine.succeed( + "curl -X GET http://machine:2222/jibri/api/v1.0/health" + ) + machine.succeed( + """curl -H "Content-Type: application/json" -X POST http://localhost:2222/jibri/api/v1.0/startService -d '{"sessionId": "RecordTest","callParams":{"callUrlInfo":{"baseUrl": "https://machine","callName": "TestCall"}},"callLoginParams":{"domain": "recorder.machine", "username": "recorder", "password": "'"$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"'" },"sinkType": "file"}'""" + ) + machine.wait_until_succeeds( + "cat /var/log/jitsi/jibri/log.0.txt | grep -q 'File recording service transitioning from state Starting up to Running'", timeout=35 + ) + machine.succeed( + """sleep 15 && curl -H "Content-Type: application/json" -X POST http://localhost:2222/jibri/api/v1.0/stopService -d '{"sessionId": "RecordTest","callParams":{"callUrlInfo":{"baseUrl": "https://machine","callName": "TestCall"}},"callLoginParams":{"domain": "recorder.machine", "username": "recorder", "password": "'"$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"'" },"sinkType": "file"}'""" + ) + machine.wait_until_succeeds( + "cat /var/log/jitsi/jibri/log.0.txt | grep -q 'Recording finalize script finished with exit value 0'", timeout=36 + ) + ''; +}) -- cgit 1.4.1