at 21.11-pre 11 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.jitsi-meet; 7 8 # The configuration files are JS of format "var <<string>> = <<JSON>>;". In order to 9 # override only some settings, we need to extract the JSON, use jq to merge it with 10 # the config provided by user, and then reconstruct the file. 11 overrideJs = 12 source: varName: userCfg: appendExtra: 13 let 14 extractor = pkgs.writeText "extractor.js" '' 15 var fs = require("fs"); 16 eval(fs.readFileSync(process.argv[2], 'utf8')); 17 process.stdout.write(JSON.stringify(eval(process.argv[3]))); 18 ''; 19 userJson = pkgs.writeText "user.json" (builtins.toJSON userCfg); 20 in (pkgs.runCommand "${varName}.js" { } '' 21 ${pkgs.nodejs}/bin/node ${extractor} ${source} ${varName} > default.json 22 ( 23 echo "var ${varName} = " 24 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' default.json ${userJson} 25 echo ";" 26 echo ${escapeShellArg appendExtra} 27 ) > $out 28 ''); 29 30 # Essential config - it's probably not good to have these as option default because 31 # types.attrs doesn't do merging. Let's merge explicitly, can still be overriden if 32 # user desires. 33 defaultCfg = { 34 hosts = { 35 domain = cfg.hostName; 36 muc = "conference.${cfg.hostName}"; 37 focus = "focus.${cfg.hostName}"; 38 }; 39 bosh = "//${cfg.hostName}/http-bind"; 40 }; 41in 42{ 43 options.services.jitsi-meet = with types; { 44 enable = mkEnableOption "Jitsi Meet - Secure, Simple and Scalable Video Conferences"; 45 46 hostName = mkOption { 47 type = str; 48 example = "meet.example.org"; 49 description = '' 50 Hostname of the Jitsi Meet instance. 51 ''; 52 }; 53 54 config = mkOption { 55 type = attrs; 56 default = { }; 57 example = literalExample '' 58 { 59 enableWelcomePage = false; 60 defaultLang = "fi"; 61 } 62 ''; 63 description = '' 64 Client-side web application settings that override the defaults in <filename>config.js</filename>. 65 66 See <link xlink:href="https://github.com/jitsi/jitsi-meet/blob/master/config.js" /> for default 67 configuration with comments. 68 ''; 69 }; 70 71 extraConfig = mkOption { 72 type = lines; 73 default = ""; 74 description = '' 75 Text to append to <filename>config.js</filename> web application config file. 76 77 Can be used to insert JavaScript logic to determine user's region in cascading bridges setup. 78 ''; 79 }; 80 81 interfaceConfig = mkOption { 82 type = attrs; 83 default = { }; 84 example = literalExample '' 85 { 86 SHOW_JITSI_WATERMARK = false; 87 SHOW_WATERMARK_FOR_GUESTS = false; 88 } 89 ''; 90 description = '' 91 Client-side web-app interface settings that override the defaults in <filename>interface_config.js</filename>. 92 93 See <link xlink:href="https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js" /> for 94 default configuration with comments. 95 ''; 96 }; 97 98 videobridge = { 99 enable = mkOption { 100 type = bool; 101 default = true; 102 description = '' 103 Whether to enable Jitsi Videobridge instance and configure it to connect to Prosody. 104 105 Additional configuration is possible with <option>services.jitsi-videobridge</option>. 106 ''; 107 }; 108 109 passwordFile = mkOption { 110 type = nullOr str; 111 default = null; 112 example = "/run/keys/videobridge"; 113 description = '' 114 File containing password to the Prosody account for videobridge. 115 116 If <literal>null</literal>, a file with password will be generated automatically. Setting 117 this option is useful if you plan to connect additional videobridges to the XMPP server. 118 ''; 119 }; 120 }; 121 122 jicofo.enable = mkOption { 123 type = bool; 124 default = true; 125 description = '' 126 Whether to enable JiCoFo instance and configure it to connect to Prosody. 127 128 Additional configuration is possible with <option>services.jicofo</option>. 129 ''; 130 }; 131 132 nginx.enable = mkOption { 133 type = bool; 134 default = true; 135 description = '' 136 Whether to enable nginx virtual host that will serve the javascript application and act as 137 a proxy for the XMPP server. Further nginx configuration can be done by adapting 138 <option>services.nginx.virtualHosts.&lt;hostName&gt;</option>. 139 When this is enabled, ACME will be used to retrieve a TLS certificate by default. To disable 140 this, set the <option>services.nginx.virtualHosts.&lt;hostName&gt;.enableACME</option> to 141 <literal>false</literal> and if appropriate do the same for 142 <option>services.nginx.virtualHosts.&lt;hostName&gt;.forceSSL</option>. 143 ''; 144 }; 145 146 prosody.enable = mkOption { 147 type = bool; 148 default = true; 149 description = '' 150 Whether to configure Prosody to relay XMPP messages between Jitsi Meet components. Turn this 151 off if you want to configure it manually. 152 ''; 153 }; 154 }; 155 156 config = mkIf cfg.enable { 157 services.prosody = mkIf cfg.prosody.enable { 158 enable = mkDefault true; 159 xmppComplianceSuite = mkDefault false; 160 modules = { 161 admin_adhoc = mkDefault false; 162 bosh = mkDefault true; 163 ping = mkDefault true; 164 roster = mkDefault true; 165 saslauth = mkDefault true; 166 tls = mkDefault true; 167 }; 168 muc = [ 169 { 170 domain = "conference.${cfg.hostName}"; 171 name = "Jitsi Meet MUC"; 172 roomLocking = false; 173 roomDefaultPublicJids = true; 174 extraConfig = '' 175 storage = "memory" 176 ''; 177 } 178 { 179 domain = "internal.${cfg.hostName}"; 180 name = "Jitsi Meet Videobridge MUC"; 181 extraConfig = '' 182 storage = "memory" 183 admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}" } 184 ''; 185 #-- muc_room_cache_size = 1000 186 } 187 ]; 188 extraModules = [ "pubsub" ]; 189 extraConfig = mkAfter '' 190 Component "focus.${cfg.hostName}" 191 component_secret = os.getenv("JICOFO_COMPONENT_SECRET") 192 ''; 193 virtualHosts.${cfg.hostName} = { 194 enabled = true; 195 domain = cfg.hostName; 196 extraConfig = '' 197 authentication = "anonymous" 198 c2s_require_encryption = false 199 admins = { "focus@auth.${cfg.hostName}" } 200 ''; 201 ssl = { 202 cert = "/var/lib/jitsi-meet/jitsi-meet.crt"; 203 key = "/var/lib/jitsi-meet/jitsi-meet.key"; 204 }; 205 }; 206 virtualHosts."auth.${cfg.hostName}" = { 207 enabled = true; 208 domain = "auth.${cfg.hostName}"; 209 extraConfig = '' 210 authentication = "internal_plain" 211 ''; 212 ssl = { 213 cert = "/var/lib/jitsi-meet/jitsi-meet.crt"; 214 key = "/var/lib/jitsi-meet/jitsi-meet.key"; 215 }; 216 }; 217 }; 218 systemd.services.prosody.serviceConfig = mkIf cfg.prosody.enable { 219 EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ]; 220 SupplementaryGroups = [ "jitsi-meet" ]; 221 }; 222 223 users.groups.jitsi-meet = {}; 224 systemd.tmpfiles.rules = [ 225 "d '/var/lib/jitsi-meet' 0750 root jitsi-meet - -" 226 ]; 227 228 systemd.services.jitsi-meet-init-secrets = { 229 wantedBy = [ "multi-user.target" ]; 230 before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service"); 231 serviceConfig = { 232 Type = "oneshot"; 233 }; 234 235 script = let 236 secrets = [ "jicofo-component-secret" "jicofo-user-secret" ] ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret"); 237 videobridgeSecret = if cfg.videobridge.passwordFile != null then cfg.videobridge.passwordFile else "/var/lib/jitsi-meet/videobridge-secret"; 238 in 239 '' 240 cd /var/lib/jitsi-meet 241 ${concatMapStringsSep "\n" (s: '' 242 if [ ! -f ${s} ]; then 243 tr -dc a-zA-Z0-9 </dev/urandom | head -c 64 > ${s} 244 chown root:jitsi-meet ${s} 245 chmod 640 ${s} 246 fi 247 '') secrets} 248 249 # for easy access in prosody 250 echo "JICOFO_COMPONENT_SECRET=$(cat jicofo-component-secret)" > secrets-env 251 chown root:jitsi-meet secrets-env 252 chmod 640 secrets-env 253 '' 254 + optionalString cfg.prosody.enable '' 255 ${config.services.prosody.package}/bin/prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)" 256 ${config.services.prosody.package}/bin/prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})" 257 258 # generate self-signed certificates 259 if [ ! -f /var/lib/jitsi-meet.crt ]; then 260 ${getBin pkgs.openssl}/bin/openssl req \ 261 -x509 \ 262 -newkey rsa:4096 \ 263 -keyout /var/lib/jitsi-meet/jitsi-meet.key \ 264 -out /var/lib/jitsi-meet/jitsi-meet.crt \ 265 -days 36500 \ 266 -nodes \ 267 -subj '/CN=${cfg.hostName}/CN=auth.${cfg.hostName}' 268 chmod 640 /var/lib/jitsi-meet/jitsi-meet.{crt,key} 269 chown root:jitsi-meet /var/lib/jitsi-meet/jitsi-meet.{crt,key} 270 fi 271 ''; 272 }; 273 274 services.nginx = mkIf cfg.nginx.enable { 275 enable = mkDefault true; 276 virtualHosts.${cfg.hostName} = { 277 enableACME = mkDefault true; 278 forceSSL = mkDefault true; 279 root = pkgs.jitsi-meet; 280 extraConfig = '' 281 ssi on; 282 ''; 283 locations."@root_path".extraConfig = '' 284 rewrite ^/(.*)$ / break; 285 ''; 286 locations."~ ^/([^/\\?&:'\"]+)$".tryFiles = "$uri @root_path"; 287 locations."=/http-bind" = { 288 proxyPass = "http://localhost:5280/http-bind"; 289 extraConfig = '' 290 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 291 proxy_set_header Host $host; 292 ''; 293 }; 294 locations."=/external_api.js" = mkDefault { 295 alias = "${pkgs.jitsi-meet}/libs/external_api.min.js"; 296 }; 297 locations."=/config.js" = mkDefault { 298 alias = overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config) cfg.extraConfig; 299 }; 300 locations."=/interface_config.js" = mkDefault { 301 alias = overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig ""; 302 }; 303 }; 304 }; 305 306 services.jitsi-videobridge = mkIf cfg.videobridge.enable { 307 enable = true; 308 xmppConfigs."localhost" = { 309 userName = "jvb"; 310 domain = "auth.${cfg.hostName}"; 311 passwordFile = "/var/lib/jitsi-meet/videobridge-secret"; 312 mucJids = "jvbbrewery@internal.${cfg.hostName}"; 313 disableCertificateVerification = true; 314 }; 315 }; 316 317 services.jicofo = mkIf cfg.jicofo.enable { 318 enable = true; 319 xmppHost = "localhost"; 320 xmppDomain = cfg.hostName; 321 userDomain = "auth.${cfg.hostName}"; 322 userName = "focus"; 323 userPasswordFile = "/var/lib/jitsi-meet/jicofo-user-secret"; 324 componentPasswordFile = "/var/lib/jitsi-meet/jicofo-component-secret"; 325 bridgeMuc = "jvbbrewery@internal.${cfg.hostName}"; 326 config = { 327 "org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED" = "true"; 328 }; 329 }; 330 }; 331 332 meta.doc = ./jitsi-meet.xml; 333 meta.maintainers = lib.teams.jitsi.members; 334}