at 18.09-beta 13 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.prosody; 8 9 sslOpts = { ... }: { 10 11 options = { 12 13 key = mkOption { 14 type = types.path; 15 description = "Path to the key file."; 16 }; 17 18 # TODO: rename to certificate to match the prosody config 19 cert = mkOption { 20 type = types.path; 21 description = "Path to the certificate file."; 22 }; 23 24 extraOptions = mkOption { 25 type = types.attrs; 26 default = {}; 27 description = "Extra SSL configuration options."; 28 }; 29 30 }; 31 }; 32 33 moduleOpts = { 34 # Generally required 35 roster = mkOption { 36 type = types.bool; 37 default = true; 38 description = "Allow users to have a roster"; 39 }; 40 41 saslauth = mkOption { 42 type = types.bool; 43 default = true; 44 description = "Authentication for clients and servers. Recommended if you want to log in."; 45 }; 46 47 tls = mkOption { 48 type = types.bool; 49 default = true; 50 description = "Add support for secure TLS on c2s/s2s connections"; 51 }; 52 53 dialback = mkOption { 54 type = types.bool; 55 default = true; 56 description = "s2s dialback support"; 57 }; 58 59 disco = mkOption { 60 type = types.bool; 61 default = true; 62 description = "Service discovery"; 63 }; 64 65 # Not essential, but recommended 66 carbons = mkOption { 67 type = types.bool; 68 default = true; 69 description = "Keep multiple clients in sync"; 70 }; 71 72 pep = mkOption { 73 type = types.bool; 74 default = true; 75 description = "Enables users to publish their mood, activity, playing music and more"; 76 }; 77 78 private = mkOption { 79 type = types.bool; 80 default = true; 81 description = "Private XML storage (for room bookmarks, etc.)"; 82 }; 83 84 blocklist = mkOption { 85 type = types.bool; 86 default = true; 87 description = "Allow users to block communications with other users"; 88 }; 89 90 vcard = mkOption { 91 type = types.bool; 92 default = true; 93 description = "Allow users to set vCards"; 94 }; 95 96 # Nice to have 97 version = mkOption { 98 type = types.bool; 99 default = true; 100 description = "Replies to server version requests"; 101 }; 102 103 uptime = mkOption { 104 type = types.bool; 105 default = true; 106 description = "Report how long server has been running"; 107 }; 108 109 time = mkOption { 110 type = types.bool; 111 default = true; 112 description = "Let others know the time here on this server"; 113 }; 114 115 ping = mkOption { 116 type = types.bool; 117 default = true; 118 description = "Replies to XMPP pings with pongs"; 119 }; 120 121 register = mkOption { 122 type = types.bool; 123 default = true; 124 description = "Allow users to register on this server using a client and change passwords"; 125 }; 126 127 mam = mkOption { 128 type = types.bool; 129 default = false; 130 description = "Store messages in an archive and allow users to access it"; 131 }; 132 133 # Admin interfaces 134 admin_adhoc = mkOption { 135 type = types.bool; 136 default = true; 137 description = "Allows administration via an XMPP client that supports ad-hoc commands"; 138 }; 139 140 admin_telnet = mkOption { 141 type = types.bool; 142 default = false; 143 description = "Opens telnet console interface on localhost port 5582"; 144 }; 145 146 # HTTP modules 147 bosh = mkOption { 148 type = types.bool; 149 default = false; 150 description = "Enable BOSH clients, aka 'Jabber over HTTP'"; 151 }; 152 153 websocket = mkOption { 154 type = types.bool; 155 default = false; 156 description = "Enable WebSocket support"; 157 }; 158 159 http_files = mkOption { 160 type = types.bool; 161 default = false; 162 description = "Serve static files from a directory over HTTP"; 163 }; 164 165 # Other specific functionality 166 limits = mkOption { 167 type = types.bool; 168 default = false; 169 description = "Enable bandwidth limiting for XMPP connections"; 170 }; 171 172 groups = mkOption { 173 type = types.bool; 174 default = false; 175 description = "Shared roster support"; 176 }; 177 178 server_contact_info = mkOption { 179 type = types.bool; 180 default = false; 181 description = "Publish contact information for this service"; 182 }; 183 184 announce = mkOption { 185 type = types.bool; 186 default = false; 187 description = "Send announcement to all online users"; 188 }; 189 190 welcome = mkOption { 191 type = types.bool; 192 default = false; 193 description = "Welcome users who register accounts"; 194 }; 195 196 watchregistrations = mkOption { 197 type = types.bool; 198 default = false; 199 description = "Alert admins of registrations"; 200 }; 201 202 motd = mkOption { 203 type = types.bool; 204 default = false; 205 description = "Send a message to users when they log in"; 206 }; 207 208 legacyauth = mkOption { 209 type = types.bool; 210 default = false; 211 description = "Legacy authentication. Only used by some old clients and bots"; 212 }; 213 214 proxy65 = mkOption { 215 type = types.bool; 216 default = false; 217 description = "Enables a file transfer proxy service which clients behind NAT can use"; 218 }; 219 220 }; 221 222 toLua = x: 223 if builtins.isString x then ''"${x}"'' 224 else if builtins.isBool x then (if x == true then "true" else "false") 225 else if builtins.isInt x then toString x 226 else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }'' 227 else throw "Invalid Lua value"; 228 229 createSSLOptsStr = o: '' 230 ssl = { 231 key = "${o.key}"; 232 certificate = "${o.cert}"; 233 ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)} 234 }; 235 ''; 236 237 vHostOpts = { ... }: { 238 239 options = { 240 241 # TODO: require attribute 242 domain = mkOption { 243 type = types.str; 244 description = "Domain name"; 245 }; 246 247 enabled = mkOption { 248 type = types.bool; 249 default = false; 250 description = "Whether to enable the virtual host"; 251 }; 252 253 ssl = mkOption { 254 type = types.nullOr (types.submodule sslOpts); 255 default = null; 256 description = "Paths to SSL files"; 257 }; 258 259 extraConfig = mkOption { 260 type = types.lines; 261 default = ""; 262 description = "Additional virtual host specific configuration"; 263 }; 264 265 }; 266 267 }; 268 269in 270 271{ 272 273 ###### interface 274 275 options = { 276 277 services.prosody = { 278 279 enable = mkOption { 280 type = types.bool; 281 default = false; 282 description = "Whether to enable the prosody server"; 283 }; 284 285 package = mkOption { 286 type = types.package; 287 description = "Prosody package to use"; 288 default = pkgs.prosody; 289 defaultText = "pkgs.prosody"; 290 example = literalExample '' 291 pkgs.prosody.override { 292 withExtraLibs = [ pkgs.luaPackages.lpty ]; 293 withCommunityModules = [ "auth_external" ]; 294 }; 295 ''; 296 }; 297 298 dataDir = mkOption { 299 type = types.string; 300 description = "Directory where Prosody stores its data"; 301 default = "/var/lib/prosody"; 302 }; 303 304 user = mkOption { 305 type = types.str; 306 default = "prosody"; 307 description = "User account under which prosody runs."; 308 }; 309 310 group = mkOption { 311 type = types.str; 312 default = "prosody"; 313 description = "Group account under which prosody runs."; 314 }; 315 316 allowRegistration = mkOption { 317 type = types.bool; 318 default = false; 319 description = "Allow account creation"; 320 }; 321 322 c2sRequireEncryption = mkOption { 323 type = types.bool; 324 default = true; 325 description = '' 326 Force clients to use encrypted connections? This option will 327 prevent clients from authenticating unless they are using encryption. 328 ''; 329 }; 330 331 s2sRequireEncryption = mkOption { 332 type = types.bool; 333 default = true; 334 description = '' 335 Force servers to use encrypted connections? This option will 336 prevent servers from authenticating unless they are using encryption. 337 Note that this is different from authentication. 338 ''; 339 }; 340 341 s2sSecureAuth = mkOption { 342 type = types.bool; 343 default = false; 344 description = '' 345 Force certificate authentication for server-to-server connections? 346 This provides ideal security, but requires servers you communicate 347 with to support encryption AND present valid, trusted certificates. 348 For more information see https://prosody.im/doc/s2s#security 349 ''; 350 }; 351 352 s2sInsecureDomains = mkOption { 353 type = types.listOf types.str; 354 default = []; 355 example = [ "insecure.example.com" ]; 356 description = '' 357 Some servers have invalid or self-signed certificates. You can list 358 remote domains here that will not be required to authenticate using 359 certificates. They will be authenticated using DNS instead, even 360 when s2s_secure_auth is enabled. 361 ''; 362 }; 363 364 s2sSecureDomains = mkOption { 365 type = types.listOf types.str; 366 default = []; 367 example = [ "jabber.org" ]; 368 description = '' 369 Even if you leave s2s_secure_auth disabled, you can still require valid 370 certificates for some domains by specifying a list here. 371 ''; 372 }; 373 374 375 modules = moduleOpts; 376 377 extraModules = mkOption { 378 type = types.listOf types.str; 379 default = []; 380 description = "Enable custom modules"; 381 }; 382 383 extraPluginPaths = mkOption { 384 type = types.listOf types.path; 385 default = []; 386 description = "Addtional path in which to look find plugins/modules"; 387 }; 388 389 virtualHosts = mkOption { 390 391 description = "Define the virtual hosts"; 392 393 type = with types; loaOf (submodule vHostOpts); 394 395 example = { 396 myhost = { 397 domain = "my-xmpp-example-host.org"; 398 enabled = true; 399 }; 400 }; 401 402 default = { 403 localhost = { 404 domain = "localhost"; 405 enabled = true; 406 }; 407 }; 408 409 }; 410 411 ssl = mkOption { 412 type = types.nullOr (types.submodule sslOpts); 413 default = null; 414 description = "Paths to SSL files"; 415 }; 416 417 admins = mkOption { 418 type = types.listOf types.str; 419 default = []; 420 example = [ "admin1@example.com" "admin2@example.com" ]; 421 description = "List of administrators of the current host"; 422 }; 423 424 extraConfig = mkOption { 425 type = types.lines; 426 default = ""; 427 description = "Additional prosody configuration"; 428 }; 429 430 }; 431 }; 432 433 434 ###### implementation 435 436 config = mkIf cfg.enable { 437 438 environment.systemPackages = [ cfg.package ]; 439 440 environment.etc."prosody/prosody.cfg.lua".text = '' 441 442 pidfile = "/run/prosody/prosody.pid" 443 444 log = "*syslog" 445 446 data_path = "${cfg.dataDir}" 447 plugin_paths = { 448 ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) } 449 } 450 451 ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) } 452 453 admins = ${toLua cfg.admins} 454 455 -- we already build with libevent, so we can just enable it for a more performant server 456 use_libevent = true 457 458 modules_enabled = { 459 460 ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList 461 (name: val: optionalString val "${toLua name};") 462 cfg.modules) } 463 ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)} 464 ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)} 465 }; 466 467 allow_registration = ${toLua cfg.allowRegistration} 468 469 c2s_require_encryption = ${toLua cfg.c2sRequireEncryption} 470 471 s2s_require_encryption = ${toLua cfg.s2sRequireEncryption} 472 473 s2s_secure_auth = ${toLua cfg.s2sSecureAuth} 474 475 s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains} 476 477 s2s_secure_domains = ${toLua cfg.s2sSecureDomains} 478 479 480 ${ cfg.extraConfig } 481 482 ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: '' 483 VirtualHost "${v.domain}" 484 enabled = ${boolToString v.enabled}; 485 ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) } 486 ${ v.extraConfig } 487 '') cfg.virtualHosts) } 488 ''; 489 490 users.users.prosody = mkIf (cfg.user == "prosody") { 491 uid = config.ids.uids.prosody; 492 description = "Prosody user"; 493 createHome = true; 494 inherit (cfg) group; 495 home = "${cfg.dataDir}"; 496 }; 497 498 users.groups.prosody = mkIf (cfg.group == "prosody") { 499 gid = config.ids.gids.prosody; 500 }; 501 502 systemd.services.prosody = { 503 description = "Prosody XMPP server"; 504 after = [ "network-online.target" ]; 505 wants = [ "network-online.target" ]; 506 wantedBy = [ "multi-user.target" ]; 507 restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ]; 508 serviceConfig = { 509 User = cfg.user; 510 Group = cfg.group; 511 Type = "forking"; 512 RuntimeDirectory = [ "prosody" ]; 513 PIDFile = "/run/prosody/prosody.pid"; 514 ExecStart = "${cfg.package}/bin/prosodyctl start"; 515 }; 516 }; 517 518 }; 519 520}