Merge pull request #92468 from petabyteboy/jitsi-meet

nixos/jitsi-meet: init
This commit is contained in:
Ryan Mulligan 2020-08-03 12:43:37 -07:00 committed by GitHub
commit 4162c69b3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 967 additions and 0 deletions

View file

@ -646,6 +646,8 @@
./services/networking/iperf3.nix
./services/networking/ircd-hybrid/default.nix
./services/networking/iwd.nix
./services/networking/jicofo.nix
./services/networking/jitsi-videobridge.nix
./services/networking/keepalived/default.nix
./services/networking/keybase.nix
./services/networking/kippo.nix
@ -852,6 +854,7 @@
./services/web-apps/icingaweb2/module-monitoring.nix
./services/web-apps/ihatemoney
./services/web-apps/jirafeau.nix
./services/web-apps/jitsi-meet.nix
./services/web-apps/limesurvey.nix
./services/web-apps/mattermost.nix
./services/web-apps/mediawiki.nix

View file

@ -0,0 +1,152 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.jicofo;
in
{
options.services.jicofo = with types; {
enable = mkEnableOption "Jitsi Conference Focus - 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/jicofo-component";
description = ''
Path to file containing component secret.
'';
};
userName = mkOption {
type = str;
default = "focus";
description = ''
User part of the JID for XMPP user connection.
'';
};
userDomain = mkOption {
type = str;
example = "auth.meet.example.org";
description = ''
Domain part of the JID for XMPP user connection.
'';
};
userPasswordFile = mkOption {
type = str;
example = "/run/keys/jicofo-user";
description = ''
Path to file containing password for XMPP user connection.
'';
};
bridgeMuc = mkOption {
type = str;
example = "jvbbrewery@internal.meet.example.org";
description = ''
JID of the internal MUC used to communicate with Videobridges.
'';
};
config = mkOption {
type = attrsOf str;
default = { };
example = literalExample ''
{
"org.jitsi.jicofo.auth.URL" = "XMPP:jitsi-meet.example.com";
}
'';
description = ''
Contents of the <filename>sip-communicator.properties</filename> configuration file for jicofo.
'';
};
};
config = mkIf cfg.enable {
services.jicofo.config = mapAttrs (_: v: mkDefault v) {
"org.jitsi.jicofo.BRIDGE_MUC" = cfg.bridgeMuc;
};
users.groups.jitsi-meet = {};
systemd.services.jicofo = let
jicofoProps = {
"-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi";
"-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "jicofo";
"-Djava.util.logging.config.file" = "/etc/jitsi/jicofo/logging.properties";
};
in
{
description = "JItsi COnference FOcus";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
restartTriggers = [
config.environment.etc."jitsi/jicofo/sip-communicator.properties".source
];
environment.JAVA_SYS_PROPS = concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") jicofoProps);
script = ''
${pkgs.jicofo}/bin/jicofo \
--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})
'';
serviceConfig = {
Type = "exec";
DynamicUser = true;
User = "jicofo";
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;
};
};
environment.etc."jitsi/jicofo/sip-communicator.properties".source =
pkgs.writeText "sip-communicator.properties" (
generators.toKeyValue {} cfg.config
);
environment.etc."jitsi/jicofo/logging.properties".source =
mkDefault "${pkgs.jicofo}/etc/jitsi/jicofo/logging.properties-journal";
};
meta.maintainers = with lib.maintainers; [ ];
}

View file

@ -0,0 +1,276 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.jitsi-videobridge;
attrsToArgs = a: concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") a);
# HOCON is a JSON superset that videobridge2 uses for configuration.
# It can substitute environment variables which we use for passwords here.
# https://github.com/lightbend/config/blob/master/README.md
#
# Substitution for environment variable FOO is represented as attribute set
# { __hocon_envvar = "FOO"; }
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;
defaultJvbConfig = {
videobridge = {
ice = {
tcp = {
enabled = true;
port = 4443;
};
udp.port = 10000;
};
stats = {
enabled = true;
transports = [ { type = "muc"; } ];
};
apis.xmpp-client.configs = flip mapAttrs cfg.xmppConfigs (name: xmppConfig: {
hostname = xmppConfig.hostName;
domain = xmppConfig.domain;
username = xmppConfig.userName;
password = { __hocon_envvar = toVarName name; };
muc_jids = xmppConfig.mucJids;
muc_nickname = xmppConfig.mucNickname;
disable_certificate_verification = xmppConfig.disableCertificateVerification;
});
};
};
# Allow overriding leaves of the default config despite types.attrs not doing any merging.
jvbConfig = recursiveUpdate defaultJvbConfig cfg.config;
in
{
options.services.jitsi-videobridge = with types; {
enable = mkEnableOption "Jitsi Videobridge, a WebRTC compatible video router";
config = mkOption {
type = attrs;
default = { };
example = literalExample ''
{
videobridge = {
ice.udp.port = 5000;
websockets = {
enabled = true;
server-id = "jvb1";
};
};
}
'';
description = ''
Videobridge configuration.
See <link xlink:href="https://github.com/jitsi/jitsi-videobridge/blob/master/src/main/resources/reference.conf" />
for default configuration with comments.
'';
};
xmppConfigs = mkOption {
description = ''
XMPP servers to connect to.
See <link xlink:href="https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md" /> for more information.
'';
default = { };
example = literalExample ''
{
"localhost" = {
hostName = "localhost";
userName = "jvb";
domain = "auth.xmpp.example.org";
passwordFile = "/var/lib/jitsi-meet/videobridge-secret";
mucJids = "jvbbrewery@internal.xmpp.example.org";
};
}
'';
type = attrsOf (submodule ({ name, ... }: {
options = {
hostName = mkOption {
type = str;
example = "xmpp.example.org";
description = ''
Hostname of the XMPP server to connect to. Name of the attribute set is used by default.
'';
};
domain = mkOption {
type = nullOr str;
default = null;
example = "auth.xmpp.example.org";
description = ''
Domain part of JID of the XMPP user, if it is different from hostName.
'';
};
userName = mkOption {
type = str;
default = "jvb";
description = ''
User part of the JID.
'';
};
passwordFile = mkOption {
type = str;
example = "/run/keys/jitsi-videobridge-xmpp1";
description = ''
File containing the password for the user.
'';
};
mucJids = mkOption {
type = str;
example = "jvbbrewery@internal.xmpp.example.org";
description = ''
JID of the MUC to join. JiCoFo needs to be configured to join the same MUC.
'';
};
mucNickname = mkOption {
# Upstream DEBs use UUID, let's use hostname instead.
type = str;
description = ''
Videobridges use the same XMPP account and need to be distinguished by the
nickname (aka resource part of the JID). By default, system hostname is used.
'';
};
disableCertificateVerification = mkOption {
type = bool;
default = false;
description = ''
Whether to skip validation of the server's certificate.
'';
};
};
config = {
hostName = mkDefault name;
mucNickname = mkDefault (builtins.replaceStrings [ "." ] [ "-" ] (
config.networking.hostName + optionalString (config.networking.domain != null) ".${config.networking.domain}"
));
};
}));
};
nat = {
localAddress = mkOption {
type = nullOr str;
default = null;
example = "192.168.1.42";
description = ''
Local address when running behind NAT.
'';
};
publicAddress = mkOption {
type = nullOr str;
default = null;
example = "1.2.3.4";
description = ''
Public address when running behind NAT.
'';
};
};
extraProperties = mkOption {
type = attrsOf str;
default = { };
description = ''
Additional Java properties passed to jitsi-videobridge.
'';
};
openFirewall = mkOption {
type = bool;
default = false;
description = ''
Whether to open ports in the firewall for the videobridge.
'';
};
};
config = mkIf cfg.enable {
users.groups.jitsi-meet = {};
services.jitsi-videobridge.extraProperties = optionalAttrs (cfg.nat.localAddress != null) {
"org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS" = cfg.nat.localAddress;
"org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS" = cfg.nat.publicAddress;
};
systemd.services.jitsi-videobridge2 = let
jvbProps = {
"-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi";
"-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "videobridge";
"-Djava.util.logging.config.file" = "/etc/jitsi/videobridge/logging.properties";
"-Dconfig.file" = pkgs.writeText "jvb.conf" (toHOCON jvbConfig);
} // (mapAttrs' (k: v: nameValuePair "-D${k}" v) cfg.extraProperties);
in
{
aliases = [ "jitsi-videobridge.service" ];
description = "Jitsi Videobridge";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment.JAVA_SYS_PROPS = attrsToArgs jvbProps;
script = (concatStrings (mapAttrsToList (name: xmppConfig:
"export ${toVarName name}=$(cat ${xmppConfig.passwordFile})\n"
) cfg.xmppConfigs))
+ ''
${pkgs.jitsi-videobridge}/bin/jitsi-videobridge --apis=none
'';
serviceConfig = {
Type = "exec";
DynamicUser = true;
User = "jitsi-videobridge";
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;
TasksMax = 65000;
LimitNPROC = 65000;
LimitNOFILE = 65000;
};
};
environment.etc."jitsi/videobridge/logging.properties".source =
mkDefault "${pkgs.jitsi-videobridge}/etc/jitsi/videobridge/logging.properties-journal";
# (from videobridge2 .deb)
# this sets the max, so that we can bump the JVB UDP single port buffer size.
boot.kernel.sysctl."net.core.rmem_max" = mkDefault 10485760;
boot.kernel.sysctl."net.core.netdev_max_backlog" = mkDefault 100000;
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall
[ jvbConfig.videobridge.ice.tcp.port ];
networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall
[ jvbConfig.videobridge.ice.udp.port ];
assertions = [{
message = "publicAddress must be set if and only if localAddress is set";
assertion = (cfg.nat.publicAddress == null) == (cfg.nat.localAddress == null);
}];
};
meta.maintainers = with lib.maintainers; [ ];
}

View file

@ -0,0 +1,333 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.jitsi-meet;
# The configuration files are JS of format "var <<string>> = <<JSON>>;". In order to
# override only some settings, we need to extract the JSON, use jq to merge it with
# the config provided by user, and then reconstruct the file.
overrideJs =
source: varName: userCfg: appendExtra:
let
extractor = pkgs.writeText "extractor.js" ''
var fs = require("fs");
eval(fs.readFileSync(process.argv[2], 'utf8'));
process.stdout.write(JSON.stringify(eval(process.argv[3])));
'';
userJson = pkgs.writeText "user.json" (builtins.toJSON userCfg);
in (pkgs.runCommand "${varName}.js" { } ''
${pkgs.nodejs}/bin/node ${extractor} ${source} ${varName} > default.json
(
echo "var ${varName} = "
${pkgs.jq}/bin/jq -s '.[0] * .[1]' default.json ${userJson}
echo ";"
echo ${escapeShellArg appendExtra}
) > $out
'');
# Essential config - it's probably not good to have these as option default because
# types.attrs doesn't do merging. Let's merge explicitly, can still be overriden if
# user desires.
defaultCfg = {
hosts = {
domain = cfg.hostName;
muc = "conference.${cfg.hostName}";
focus = "focus.${cfg.hostName}";
};
bosh = "//${cfg.hostName}/http-bind";
};
in
{
options.services.jitsi-meet = with types; {
enable = mkEnableOption "Jitsi Meet - Secure, Simple and Scalable Video Conferences";
hostName = mkOption {
type = str;
example = "meet.example.org";
description = ''
Hostname of the Jitsi Meet instance.
'';
};
config = mkOption {
type = attrs;
default = { };
example = literalExample ''
{
enableWelcomePage = false;
defaultLang = "fi";
}
'';
description = ''
Client-side web application settings that override the defaults in <filename>config.js</filename>.
See <link xlink:href="https://github.com/jitsi/jitsi-meet/blob/master/config.js" /> for default
configuration with comments.
'';
};
extraConfig = mkOption {
type = lines;
default = "";
description = ''
Text to append to <filename>config.js</filename> web application config file.
Can be used to insert JavaScript logic to determine user's region in cascading bridges setup.
'';
};
interfaceConfig = mkOption {
type = attrs;
default = { };
example = literalExample ''
{
SHOW_JITSI_WATERMARK = false;
SHOW_WATERMARK_FOR_GUESTS = false;
}
'';
description = ''
Client-side web-app interface settings that override the defaults in <filename>interface_config.js</filename>.
See <link xlink:href="https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js" /> for
default configuration with comments.
'';
};
videobridge = {
enable = mkOption {
type = bool;
default = true;
description = ''
Whether to enable Jitsi Videobridge instance and configure it to connect to Prosody.
Additional configuration is possible with <option>services.jitsi-videobridge</option>.
'';
};
passwordFile = mkOption {
type = nullOr str;
default = null;
example = "/run/keys/videobridge";
description = ''
File containing password to the Prosody account for videobridge.
If <literal>null</literal>, a file with password will be generated automatically. Setting
this option is useful if you plan to connect additional videobridges to the XMPP server.
'';
};
};
jicofo.enable = mkOption {
type = bool;
default = true;
description = ''
Whether to enable JiCoFo instance and configure it to connect to Prosody.
Additional configuration is possible with <option>services.jicofo</option>.
'';
};
nginx.enable = mkOption {
type = bool;
default = true;
description = ''
Whether to enable nginx virtual host that will serve the javascript application and act as
a proxy for the XMPP server. Further nginx configuration can be done by adapting
<option>services.nginx.virtualHosts.&lt;hostName&gt;</option>.
When this is enabled, ACME will be used to retrieve a TLS certificate by default. To disable
this, set the <option>services.nginx.virtualHosts.&lt;hostName&gt;.enableACME</option> to
<literal>false</literal> and if appropriate do the same for
<option>services.nginx.virtualHosts.&lt;hostName&gt;.forceSSL</option>.
'';
};
prosody.enable = mkOption {
type = bool;
default = true;
description = ''
Whether to configure Prosody to relay XMPP messages between Jitsi Meet components. Turn this
off if you want to configure it manually.
'';
};
};
config = mkIf cfg.enable {
services.prosody = mkIf cfg.prosody.enable {
enable = mkDefault true;
xmppComplianceSuite = mkDefault false;
modules = {
admin_adhoc = mkDefault false;
bosh = mkDefault true;
ping = mkDefault true;
roster = mkDefault true;
saslauth = mkDefault true;
tls = mkDefault true;
};
muc = [
{
domain = "conference.${cfg.hostName}";
name = "Jitsi Meet MUC";
roomLocking = false;
roomDefaultPublicJids = true;
extraConfig = ''
storage = "memory"
'';
}
{
domain = "internal.${cfg.hostName}";
name = "Jitsi Meet Videobridge MUC";
extraConfig = ''
storage = "memory"
admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}" }
'';
#-- muc_room_cache_size = 1000
}
];
extraModules = [ "pubsub" ];
extraConfig = mkAfter ''
Component "focus.${cfg.hostName}"
component_secret = os.getenv("JICOFO_COMPONENT_SECRET")
'';
virtualHosts.${cfg.hostName} = {
enabled = true;
domain = cfg.hostName;
extraConfig = ''
authentication = "anonymous"
c2s_require_encryption = false
admins = { "focus@auth.${cfg.hostName}" }
'';
ssl = {
cert = "/var/lib/jitsi-meet/jitsi-meet.crt";
key = "/var/lib/jitsi-meet/jitsi-meet.key";
};
};
virtualHosts."auth.${cfg.hostName}" = {
enabled = true;
domain = "auth.${cfg.hostName}";
extraConfig = ''
authentication = "internal_plain"
'';
ssl = {
cert = "/var/lib/jitsi-meet/jitsi-meet.crt";
key = "/var/lib/jitsi-meet/jitsi-meet.key";
};
};
};
systemd.services.prosody.serviceConfig = mkIf cfg.prosody.enable {
EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ];
SupplementaryGroups = [ "jitsi-meet" ];
};
users.groups.jitsi-meet = {};
systemd.tmpfiles.rules = [
"d '/var/lib/jitsi-meet' 0750 root jitsi-meet - -"
];
systemd.services.jitsi-meet-init-secrets = {
wantedBy = [ "multi-user.target" ];
before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service");
serviceConfig = {
Type = "oneshot";
};
script = let
secrets = [ "jicofo-component-secret" "jicofo-user-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
''
cd /var/lib/jitsi-meet
${concatMapStringsSep "\n" (s: ''
if [ ! -f ${s} ]; then
tr -dc a-zA-Z0-9 </dev/urandom | head -c 64 > ${s}
chown root:jitsi-meet ${s}
chmod 640 ${s}
fi
'') secrets}
# for easy access in prosody
echo "JICOFO_COMPONENT_SECRET=$(cat jicofo-component-secret)" > secrets-env
chown root:jitsi-meet secrets-env
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})"
# generate self-signed certificates
if [ ! -f /var/lib/jitsi-meet.crt ]; then
${getBin pkgs.openssl}/bin/openssl req \
-x509 \
-newkey rsa:4096 \
-keyout /var/lib/jitsi-meet/jitsi-meet.key \
-out /var/lib/jitsi-meet/jitsi-meet.crt \
-days 36500 \
-nodes \
-subj '/CN=${cfg.hostName}/CN=auth.${cfg.hostName}'
chmod 640 /var/lib/jitsi-meet/jitsi-meet.{crt,key}
chown root:jitsi-meet /var/lib/jitsi-meet/jitsi-meet.{crt,key}
fi
'';
};
services.nginx = mkIf cfg.nginx.enable {
enable = mkDefault true;
virtualHosts.${cfg.hostName} = {
enableACME = mkDefault true;
forceSSL = mkDefault true;
root = pkgs.jitsi-meet;
extraConfig = ''
ssi on;
'';
locations."@root_path".extraConfig = ''
rewrite ^/(.*)$ / break;
'';
locations."~ ^/([^/\\?&:'\"]+)$".tryFiles = "$uri @root_path";
locations."=/http-bind" = {
proxyPass = "http://localhost:5280/http-bind";
extraConfig = ''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
'';
};
locations."=/external_api.js" = mkDefault {
alias = "${pkgs.jitsi-meet}/libs/external_api.min.js";
};
locations."=/config.js" = mkDefault {
alias = overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config) cfg.extraConfig;
};
locations."=/interface_config.js" = mkDefault {
alias = overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig "";
};
};
};
services.jitsi-videobridge = mkIf cfg.videobridge.enable {
enable = true;
xmppConfigs."localhost" = {
userName = "jvb";
domain = "auth.${cfg.hostName}";
passwordFile = "/var/lib/jitsi-meet/videobridge-secret";
mucJids = "jvbbrewery@internal.${cfg.hostName}";
disableCertificateVerification = true;
};
};
services.jicofo = mkIf cfg.jicofo.enable {
enable = true;
xmppHost = "localhost";
xmppDomain = cfg.hostName;
userDomain = "auth.${cfg.hostName}";
userName = "focus";
userPasswordFile = "/var/lib/jitsi-meet/jicofo-user-secret";
componentPasswordFile = "/var/lib/jitsi-meet/jicofo-component-secret";
bridgeMuc = "jvbbrewery@internal.${cfg.hostName}";
config = {
"org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED" = "true";
};
};
};
meta.maintainers = with lib.maintainers; [ ];
}

View file

@ -162,6 +162,7 @@ in
jellyfin = handleTest ./jellyfin.nix {};
jenkins = handleTest ./jenkins.nix {};
jirafeau = handleTest ./jirafeau.nix {};
jitsi-meet = handleTest ./jitsi-meet.nix {};
k3s = handleTest ./k3s.nix {};
kafka = handleTest ./kafka.nix {};
keepalived = handleTest ./keepalived.nix {};

View file

@ -0,0 +1,55 @@
import ./make-test-python.nix ({ pkgs, ... }: {
name = "jitsi-meet";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ mmilata ];
};
nodes = {
client = { nodes, pkgs, ... }: {
};
server = { config, pkgs, ... }: {
services.jitsi-meet = {
enable = true;
hostName = "server";
};
services.jitsi-videobridge.openFirewall = true;
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.nginx.virtualHosts.server = {
enableACME = true;
forceSSL = true;
};
security.acme.email = "me@example.org";
security.acme.acceptTerms = true;
security.acme.server = "https://example.com"; # self-signed only
};
};
testScript = ''
server.wait_for_unit("jitsi-videobridge2.service")
server.wait_for_unit("jicofo.service")
server.wait_for_unit("nginx.service")
server.wait_for_unit("prosody.service")
server.wait_until_succeeds(
"journalctl -b -u jitsi-videobridge2 -o cat | grep -q 'Performed a successful health check'"
)
server.wait_until_succeeds(
"journalctl -b -u jicofo -o cat | grep -q 'connected .JID: focus@auth.server'"
)
server.wait_until_succeeds(
"journalctl -b -u prosody -o cat | grep -q 'Authenticated as focus@auth.server'"
)
server.wait_until_succeeds(
"journalctl -b -u prosody -o cat | grep -q 'focus.server:component: External component successfully authenticated'"
)
server.wait_until_succeeds(
"journalctl -b -u prosody -o cat | grep -q 'Authenticated as jvb@auth.server'"
)
client.wait_for_unit("network.target")
assert "<title>Jitsi Meet</title>" in client.succeed("curl -sSfkL http://server/")
'';
})

View file

@ -0,0 +1,43 @@
{ pkgs, stdenv, fetchurl, dpkg, jre_headless, nixosTests }:
let
pname = "jicofo";
version = "1.0-589";
src = fetchurl {
url = "https://download.jitsi.org/stable/${pname}_${version}-1_all.deb";
sha256 = "0bsagnmw2rxf9s9kjl4y7gfqx408iv0qlwgy3mz0339g5503p5r9";
};
in
stdenv.mkDerivation {
inherit pname version src;
dontBuild = true;
unpackCmd = "${dpkg}/bin/dpkg-deb -x $src debcontents";
installPhase = ''
substituteInPlace usr/share/jicofo/jicofo.sh \
--replace "exec java" "exec ${jre_headless}/bin/java"
mkdir -p $out/{share,bin}
mv usr/share/jicofo $out/share/
mv etc $out/
cp ${./logging.properties-journal} $out/etc/jitsi/jicofo/logging.properties-journal
ln -s $out/share/jicofo/jicofo.sh $out/bin/jicofo
'';
passthru.tests = {
single-node-smoke-test = nixosTests.jitsi-meet;
};
meta = with stdenv.lib; {
description = "A server side focus component used in Jitsi Meet conferences";
longDescription = ''
JItsi COnference FOcus is a server side focus component used in Jitsi Meet conferences.
'';
homepage = "https://github.com/jitsi/jicofo";
license = licenses.asl20;
maintainers = with maintainers; [ ];
platforms = platforms.linux;
};
}

View file

@ -0,0 +1,10 @@
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter
.level = INFO
net.sf.level = SEVERE
net.java.sip.communicator.plugin.reconnectplugin.level = FINE
org.ice4j.level = SEVERE
org.jitsi.impl.neomedia.level = SEVERE
net.java.sip.communicator.service.resources.AbstractResourcesService.level = SEVERE
net.java.sip.communicator.util.ScLogFormatter.disableTimestamp = true

View file

@ -0,0 +1,47 @@
{ stdenv, fetchurl, dpkg, jre_headless, nixosTests }:
let
pname = "jitsi-videobridge2";
version = "2.1-202-g5f9377b9";
src = fetchurl {
url = "https://download.jitsi.org/stable/${pname}_${version}-1_all.deb";
sha256 = "16xj4m6kz4di6y3vxrjkwajd7sfm92zzhrc6q9ljmrwiqnly5z0a";
};
in
stdenv.mkDerivation {
inherit pname version src;
dontBuild = true;
unpackCmd = "${dpkg}/bin/dpkg-deb -x $src debcontents";
installPhase = ''
substituteInPlace usr/share/jitsi-videobridge/jvb.sh \
--replace "exec java" "exec ${jre_headless}/bin/java"
mkdir -p $out/{bin,share/jitsi-videobridge,etc/jitsi/videobridge}
mv etc/jitsi/videobridge/logging.properties $out/etc/jitsi/videobridge/
cp ${./logging.properties-journal} $out/etc/jitsi/videobridge/logging.properties-journal
mv usr/share/jitsi-videobridge/* $out/share/jitsi-videobridge/
ln -s $out/share/jitsi-videobridge/jvb.sh $out/bin/jitsi-videobridge
'';
passthru.tests = {
single-host-smoke-test = nixosTests.jitsi-meet;
};
meta = with stdenv.lib; {
description = "A WebRTC compatible video router";
longDescription = ''
Jitsi Videobridge is an XMPP server component that allows for multiuser video communication.
Unlike the expensive dedicated hardware videobridges, Jitsi Videobridge does not mix the video
channels into a composite video stream, but only relays the received video channels to all call
participants. Therefore, while it does need to run on a server with good network bandwidth,
CPU horsepower is not that critical for performance.
'';
homepage = "https://github.com/jitsi/jitsi-videobridge";
license = licenses.asl20;
maintainers = with maintainers; [ ];
platforms = platforms.linux;
};
}

View file

@ -0,0 +1,7 @@
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = org.jitsi.utils.logging2.JitsiLogFormatter
.level = INFO
org.jitsi.videobridge.xmpp.ComponentImpl.level = FINE
org.jitsi.impl.neomedia.MediaStreamImpl.level = WARNING
org.jitsi.utils.logging2.JitsiLogFormatter.disableTimestamp = true

View file

@ -0,0 +1,34 @@
{ pkgs, stdenv, fetchurl, nixosTests }:
stdenv.mkDerivation rec {
pname = "jitsi-meet";
version = "1.0.4127";
src = fetchurl {
url = "https://download.jitsi.org/jitsi-meet/src/jitsi-meet-${version}.tar.bz2";
sha256 = "1jrrsvgysihd73pjqfv605ax01pg2gn76znr64v7nhli55ddgzqx";
};
dontBuild = true;
installPhase = ''
mkdir $out
mv * $out/
'';
passthru.tests = {
single-host-smoke-test = nixosTests.jitsi-meet;
};
meta = with stdenv.lib; {
description = "Secure, Simple and Scalable Video Conferences";
longDescription = ''
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses Jitsi Videobridge
to provide high quality, secure and scalable video conferences.
'';
homepage = "https://github.com/jitsi/jitsi-meet";
license = licenses.asl20;
maintainers = with maintainers; [ ];
platforms = platforms.all;
};
}

View file

@ -16076,6 +16076,12 @@ in
jetty = callPackage ../servers/http/jetty { };
jicofo = callPackage ../servers/jicofo { };
jitsi-meet = callPackage ../servers/web-apps/jitsi-meet { };
jitsi-videobridge = callPackage ../servers/jitsi-videobridge { };
kapow = callPackage ../servers/kapow { };
keycloak = callPackage ../servers/keycloak { };