]> Git — Sourcephile - sourcephile-nix.git/blob - nixos/modules/services/networking/prosody.nix
nix: update to nixos-24.11
[sourcephile-nix.git] / nixos / modules / services / networking / prosody.nix
1 { options, config, pkgs, lib, ... }:
2 let
3 inherit (lib) isAttrs isBool isList isInt isString;
4 inherit (lib) mkChangedOptionModule mkDefault mkEnableOption mkForce mkIf mkMerge mkOption mkRenamedOptionModule;
5 inherit (lib) all any attrValues boolToString concatStringsSep elem filterAttrs genAttrs hasPrefix listToAttrs literalExpression mapAttrsToList mdDoc optionals optionalAttrs optionalString types unique;
6
7 config' = config;
8 cfg = config.services.prosody;
9
10 luaType = with types; let
11 valueType = nullOr
12 (oneOf [
13 bool
14 int
15 float
16 str
17 path
18 (attrsOf valueType)
19 (listOf valueType)
20 ]) // {
21 description = "Lua value";
22 };
23 in
24 valueType;
25
26 sslOption = mkOption {
27 type = with types; nullOr (submodule {
28 freeformType = luaType;
29 options = {
30
31 key = mkOption {
32 type = types.str;
33 description = mdDoc ''
34 Path to your private key file.
35 '';
36 };
37
38 certificate = mkOption {
39 type = types.str;
40 description = mdDoc ''
41 Path to your certificate file.
42 '';
43 };
44
45 cafile = mkOption {
46 type = types.str;
47 default = "/etc/ssl/certs/ca-bundle.crt";
48 description = mdDoc ''
49 Path to a file containing root certificates that you wish Prosody to trust.
50 '';
51 };
52
53 };
54 });
55 default = null;
56 description = mdDoc ''
57 Advanced SSL/TLS configuration, as described by <https://prosody.im/doc/advanced_ssl_config>.
58
59 ::: {.note}
60 Prosody passes the contents of the `ssl` option from the config file almost directly to LuaSec, the
61 library used for SSL/TLS support in Prosody. LuaSec accepts a range of options here, mostly things
62 that it passes directly to OpenSSL. It is recommended to leave Prosody's defaults in most cases, unless
63 you know what you are doing.
64 You could easily reduce security or introduce unnecessary compatibility issues with clients and other servers!
65 :::
66 '';
67 };
68
69 componentsOption = mkOption {
70 type = with types; attrsOf (submodule ({ name, config, ... }: {
71 options = {
72 module = mkOption {
73 type = with types; nullOr str;
74 default = null;
75 description = mdDoc ''
76 The name of the plugin you wish to use for the component if internal, or `null` if the
77 component is an external component.
78 '';
79 };
80
81 settings = mkOption {
82 type = luaType;
83 default = { };
84 description = mdDoc ''
85 Values specified here are applied to a specific component. Refer to <https://prosody.im/doc/components>
86 for additional details.
87 '';
88 };
89
90 extraConfig = mkOption {
91 type = types.lines;
92 default = "";
93 description = mdDoc ''
94 Additional component specific configuration.
95 '';
96 };
97
98 };
99
100 config.settings = mkMerge [
101 (mkIf (config.module == "http_upload") {
102 http_upload_path = mkIf (config.module == "http_upload_path") cfg.dataDir;
103 })
104 (mkIf (config.module == "muc") {
105 modules_enabled = [ "muc_mam" ];
106 })
107 ];
108 }));
109 default = { };
110 description = mdDoc ''
111 Components are extra services on a server which are available to clients, usually on a subdomain
112 of the main server (such as mycomponent.example.com). Example components might be chatroom
113 servers, user directories, or gateways to other protocols.
114
115 See <https://prosody.im/doc/components> for details.
116 '';
117 };
118
119 acmeHosts = unique (mapAttrsToList (domain: hostOpts: hostOpts.useACMEHost) (filterAttrs (_: v: v.useACMEHost != null) cfg.virtualHosts));
120 in
121 {
122 imports = [
123 (mkRenamedOptionModule [ "services" "prosody" "allowRegistration" ] [ "services" "prosody" "settings" "allow_registration" ])
124 (mkRenamedOptionModule [ "services" "prosody" "httpPorts" ] [ "services" "prosody" "settings" "http_ports" ])
125 (mkRenamedOptionModule [ "services" "prosody" "httpInterfaces" ] [ "services" "prosody" "settings" "http_interfaces" ])
126 (mkRenamedOptionModule [ "services" "prosody" "httpsPorts" ] [ "services" "prosody" "settings" "https_ports" ])
127 (mkRenamedOptionModule [ "services" "prosody" "httpsInterfaces" ] [ "services" "prosody" "settings" "https_interfaces" ])
128 (mkRenamedOptionModule [ "services" "prosody" "c2sRequireEncryption" ] [ "services" "prosody" "settings" "c2s_require_encryption" ])
129 (mkRenamedOptionModule [ "services" "prosody" "s2sRequireEncryption" ] [ "services" "prosody" "settings" "s2s_require_encryption" ])
130 (mkRenamedOptionModule [ "services" "prosody" "s2sSecureAuth" ] [ "services" "prosody" "settings" "s2s_secure_auth" ])
131 (mkRenamedOptionModule [ "services" "prosody" "s2sInsecureDomains" ] [ "services" "prosody" "settings" "s2s_insecure_domains" ])
132 (mkRenamedOptionModule [ "services" "prosody" "s2sSecureDomains" ] [ "services" "prosody" "settings" "s2s_secure_domains" ])
133 (mkRenamedOptionModule [ "services" "prosody" "extraModules" ] [ "services" "prosody" "settings" "modules_enabled" ])
134 (mkRenamedOptionModule [ "services" "prosody" "extraPluginPaths" ] [ "services" "prosody" "settings" "plugin_paths" ])
135 (mkRenamedOptionModule [ "services" "prosody" "ssl" "key" ] [ "services" "prosody" "settings" "ssl" "key" ])
136 (mkRenamedOptionModule [ "services" "prosody" "ssl" "cert" ] [ "services" "prosody" "settings" "ssl" "certificate" ])
137 (mkRenamedOptionModule [ "services" "prosody" "ssl" "extraOptions" ] [ "services" "prosody" "settings" "ssl" ])
138 (mkRenamedOptionModule [ "services" "prosody" "admins" ] [ "services" "prosody" "settings" "admins" ])
139 (mkRenamedOptionModule [ "services" "prosody" "authentication" ] [ "services" "prosody" "settings" "authentication" ])
140 (mkChangedOptionModule [ "services" "prosody" "disco_items" ] [ "services" "prosody" "settings" "disco_items" ] (config:
141 map ({ url, description }: [ url description ]) config.services.prosody.disco_items
142 ))
143 (mkChangedOptionModule [ "services" "prosody" "uploadHttp" ] [ "services" "prosody" "components" ] (config:
144 let
145 cfg = config.services.prosody;
146 in
147 {
148 ${cfg.uploadHttp.domain} = {
149 module = "http_upload";
150 # these values are to preserve compatibility with this module pre nixos 23.11
151 settings = {
152 http_upload_file_size_limit = { __compat = true; value = cfg.uploadHttp.uploadFileSizeLimit or "50 * 1024 * 1024"; };
153 http_upload_expire_after = { __compat = true; value = cfg.uploadHttp.uploadExpireAfter or "60 * 60 * 24 * 7"; };
154 http_upload_path = cfg.uploadHttp.httpUploadPath or "/var/lib/prosody";
155 } // optionalAttrs (cfg.uploadHttp ? userQuota) {
156 http_upload_quota = cfg.uploadHttp.userQuota;
157 };
158 };
159 }
160 ))
161 (mkChangedOptionModule [ "services" "prosody" "muc" ] [ "services" "prosody" "components" ] (config:
162 listToAttrs (map
163 (muc: {
164 name = muc.domain;
165 value = {
166 module = "muc";
167 extraConfig = muc.extraConfig or "";
168 # these values are to preserve compatibility with this module pre nixos 23.11
169 settings = {
170 modules_enabled = optionals (muc.vcard_muc or true) [ "vcard_muc" ];
171 name = muc.name or "Prosody Chatrooms";
172 restrict_room_creation = muc.restrictRoomCreation or "local";
173 max_history_messages = muc.maxHistoryMessages or 20;
174 muc_room_locking = muc.roomLocking or true;
175 muc_room_lock_timeout = muc.roomLockTimeout or 300;
176 muc_tombstones = muc.tombstones or true;
177 muc_tombstone_expiry = muc.tombstoneExpiry or 2678400;
178 muc_room_default_public = muc.roomDefaultPublic or true;
179 muc_room_default_members_only = muc.roomDefaultMembersOnly or false;
180 muc_room_default_moderated = muc.roomDefaultModerated or false;
181 muc_room_default_public_jids = muc.roomDefaultPublicJids or false;
182 muc_room_default_change_subject = muc.roomDefaultChangeSubject or false;
183 muc_room_default_history_length = muc.roomDefaultHistoryLength or 20;
184 muc_room_default_language = muc.roomDefaultLanguage or "en";
185 };
186 };
187 })
188 config.services.prosody.muc)
189 ))
190 ];
191
192 options.services.prosody = {
193 enable = mkEnableOption "Prosody, the modern XMPP communication server";
194
195 xmppComplianceSuite = mkOption {
196 type = types.bool;
197 default = true;
198 description = mdDoc ''
199 The XEP-0423 defines a set of recommended XEPs to implement
200 for a server. It's generally a good idea to implement this
201 set of extensions if you want to provide your users with a
202 good XMPP experience.
203
204 This NixOS module aims to provide an "advanced server"
205 experience as per defined in the [XEP-0423](https://xmpp.org/extensions/xep-0423.html) specification.
206
207 Setting this option to `true` will prevent you from building a
208 NixOS configuration which won't comply with this standard.
209 You can explicitly decide to ignore this standard if you
210 know what you are doing by setting this option to `false`.
211 '';
212 };
213
214 package = mkOption {
215 type = types.package;
216 description = mdDoc "Prosody package to use.";
217 default = pkgs.prosody;
218 defaultText = literalExpression "pkgs.prosody";
219 example = literalExpression ''
220 pkgs.prosody.override {
221 withExtraLibs = [ pkgs.luaPackages.lpty ];
222 withCommunityModules = [ "auth_external" ];
223 };
224 '';
225 };
226
227 dataDir = mkOption {
228 type = types.path;
229 default = "/var/lib/prosody";
230 description = mdDoc ''
231 The Prosody home directory used to store all data.
232
233 ::: {.note}
234 If left as the default value this directory will automatically be created
235 before the Prosody server starts, otherwise you are responsible for
236 ensuring the directory exists with appropriate ownership and permissions.
237 :::
238 '';
239 };
240
241 user = mkOption {
242 type = types.str;
243 default = "prosody";
244 description = mdDoc ''
245 User account under which Prosody runs.
246
247 ::: {.note}
248 If left as the default value this user will automatically be created
249 on system activation, otherwise you are responsible for
250 ensuring the user exists before the Prosody service starts.
251 :::
252 '';
253 };
254
255 group = mkOption {
256 type = types.str;
257 default = "prosody";
258 description = mdDoc ''
259 Group account under which Prosody runs.
260
261 ::: {.note}
262 If left as the default value this group will automatically be created
263 on system activation, otherwise you are responsible for
264 ensuring the group exists before the Prosody service starts.
265 :::
266 '';
267 };
268
269 environmentFile = mkOption {
270 type = with types; nullOr path;
271 default = null;
272 description = mdDoc ''
273 File which may contain secrets in the format of an EnvironmentFile as described
274 by systemd.exec(5). Variables here can be used in the Prosody configuration
275 file by prefixing the environment variable name with `ENV_` in the configuration.
276
277 ::: {.note}
278 Environment variables in this file should *not* begin with an `ENV_` prefix, only
279 when referenced in the Prosody configuration file.
280 :::
281 '';
282 };
283
284 openFirewall = mkOption {
285 type = types.bool;
286 default = false;
287 description = mdDoc ''
288 Whether to automatically open ports specified by the following options:
289
290 - {option}`services.prosody.settings.c2s_ports`
291 - {option}`services.prosody.settings.s2s_ports`
292 - {option}`services.prosody.settings.https_ports`
293 '';
294 };
295
296 settings = mkOption {
297 type = types.submodule {
298 freeformType = luaType;
299 options = {
300
301 modules_enabled = mkOption {
302 type = with types; listOf str;
303 apply = x: unique x;
304 default = [ ];
305 description = mdDoc ''
306 List of modules to load for all virtual hosts.
307 '';
308 };
309
310 modules_disabled = mkOption {
311 type = with types; listOf str;
312 apply = x: unique x;
313 default = [ ];
314 description = mdDoc ''
315 Allows you to disable the loading of a list of modules for all virtual hosts
316 if those modules are set in the global settings.
317 '';
318 };
319
320 ssl = sslOption;
321
322 };
323 };
324 default = { };
325 description = mdDoc ''
326 Values specified here are applied to the whole server, and are the default for all
327 virtual hosts. Refer to <https://prosody.im/doc/configure> for additional details.
328
329 ::: {.note}
330 It's also possible to refer to environment variables (defined in
331 [services.prosody.environmentFile](#opt-services.prosody.environmentFile)) using the
332 syntax `"ENV_VARIABLE_NAME"` where the environment file contains a `VARIABLE_NAME` entry.
333 :::
334 '';
335 example = literalExpression ''
336 {
337 modules_enabled = [ "turn_external" ];
338 turn_external_host = "turn.example.com";
339 turn_external_port = 3478;
340 turn_external_secret = "ENV_TURN_EXTERNAL_SECRET";
341 }
342 '';
343 };
344
345 components = componentsOption;
346
347 virtualHosts = let config' = config; in mkOption {
348 type = with types; attrsOf (submodule ({ name, config, ... }: {
349 options = {
350 domain = mkOption {
351 type = types.str;
352 default = name;
353 description = mdDoc "Domain name.";
354 };
355
356 useACMEHost = mkOption {
357 type = with types; nullOr str;
358 default = null;
359 description = mdDoc ''
360 A host of an existing Let's Encrypt certificate to use.
361
362 ::: {.note}
363 Note that this option does not create any certificates, nor it does add subdomains to existing
364 ones – you will need to create them manually using [](#opt-security.acme.certs).
365 :::
366 '';
367 };
368
369 components = componentsOption;
370
371 settings = mkOption {
372 type = types.submodule {
373 freeformType = luaType;
374 options = {
375
376 enabled = mkOption {
377 type = types.bool;
378 default = true;
379 description = mdDoc ''
380 Specifies whether this host is enabled or not. Disabled hosts are not loaded and
381 do not accept connections while Prosody is running.
382 '';
383 };
384
385 modules_enabled = mkOption {
386 type = with types; listOf str;
387 apply = x: unique x;
388 default = [ ];
389 description = mdDoc ''
390 List of modules to load for the virtual host.
391 '';
392 };
393
394 modules_disabled = mkOption {
395 type = with types; listOf str;
396 apply = x: unique x;
397 default = [ ];
398 description = mdDoc ''
399 Allows you to disable the loading of a list of modules for a particular host.
400 '';
401 };
402
403 ssl = sslOption;
404 };
405 };
406 default = { };
407 description = mdDoc ''
408 Values specified here are applied to a specific virtual host and will override values set
409 in the global [settings](#opt-services.prosody.settings) option. Refer to <https://prosody.im/doc/configure>
410 for additional details.
411 '';
412 };
413
414 extraConfig = mkOption {
415 type = types.lines;
416 default = "";
417 description = mdDoc ''
418 Additional virtual host specific configuration.
419 '';
420 };
421
422 # options to preserve compatibility with this module pre nixos 23.11
423
424 enabled = mkOption {
425 type = with types; nullOr bool;
426 default = null;
427 description = mdDoc "Whether to enable the virtual host.";
428 };
429
430 ssl = mkOption {
431 type = types.nullOr (types.submodule {
432 options = {
433 key = mkOption {
434 type = types.path;
435 description = lib.mdDoc "Path to the key file.";
436 };
437
438 cert = mkOption {
439 type = types.path;
440 description = lib.mdDoc "Path to the certificate file.";
441 };
442
443 extraOptions = mkOption {
444 type = types.attrs;
445 default = { };
446 description = lib.mdDoc "Extra SSL configuration options.";
447 };
448 };
449 });
450 default = null;
451 description = mdDoc "Paths to SSL files.";
452 };
453
454 };
455
456 config.settings = {
457 ssl = mkMerge [
458 (mkIf (config.ssl != null) ({
459 key = config.ssl.key;
460 certificate = config.ssl.cert;
461 } // config.ssl.extraOptions))
462 (mkIf (config.useACMEHost != null) {
463 key = "${config'.security.acme.certs.${config.useACMEHost}.directory}/key.pem";
464 certificate = "${config'.security.acme.certs.${config.useACMEHost}.directory}/fullchain.pem";
465 })
466 ];
467
468 disco_items =
469 mapAttrsToList (k: v: [ k "${k} HTTP upload endpoint" ]) (filterAttrs (k: v: v.module == "http_upload") config.components) ++
470 mapAttrsToList (k: v: [ k "${k} MUC endpoint" ]) (filterAttrs (k: v: v.module == "muc") config.components)
471 ;
472
473 enabled = mkIf (config.enabled != null) config.enabled;
474 };
475 }));
476 default = {
477 localhost = { };
478 };
479 description = mdDoc ''
480 A host in Prosody is a domain on which user accounts can be created. For example if you want your users to have addresses
481 like `john.smith@example.com` then you need to add a host `example.com`.
482 '';
483 example = literalExpression ''
484 {
485 "example.net" = {
486 useACMEHost = "example.net";
487 settings = {
488 admins = [ "admin1@example.net" "admin2@example.net" ];
489 c2s_require_encryption = true;
490 modules_enabled = [
491 "announce"
492 ];
493 };
494 };
495 }
496 '';
497 };
498
499 extraConfig = mkOption {
500 type = types.lines;
501 default = "";
502 description = mdDoc ''
503 Additional prosody configuration.
504 '';
505 };
506
507 # options to preserve compatibility with this module pre nixos 23.11
508
509 modules = mkOption {
510 type = types.attrsOf types.bool;
511 default = { };
512 description = mdDoc ''
513 '';
514 };
515 };
516
517 config = mkIf cfg.enable {
518
519 assertions =
520 let
521 hasAnyModules = mods: b: any (e: elem e.module mods) (attrValues b.components);
522 virtualHosts = filterAttrs (_: v: v.settings.enabled) cfg.virtualHosts;
523
524 # ensure a given module (e.g. muc or http_upload) is applied or available to every virtual host
525 mkAssertion = modules: message: {
526 assertion = cfg.xmppComplianceSuite -> hasAnyModules modules cfg || all (hasAnyModules modules) (attrValues virtualHosts);
527 message = message + ''
528
529 Having a server not XEP-0423-compliant might make your XMPP
530 experience terrible. See the NixOS manual for further
531 information.
532
533 If you know what you're doing, you can disable this warning by
534 setting config.services.prosody.xmppComplianceSuite to false.
535 '';
536 };
537 in
538 [
539 (mkAssertion [ "muc" ] ''
540 You need to setup at least a MUC domain to comply with
541 XEP-0423
542 '')
543
544 (mkAssertion [ "http_upload" "http_file_share" ] ''
545 You need to setup the http_upload or http_file_share module through
546 config.services.prosody.components to comply with
547 XEP-0423.
548 '')
549 ];
550
551 warnings =
552 optionals (cfg.modules != { }) [ "The option `services.prosody.modules' has been and split into two separate options: `services.prosody.settings.modules_enabled' and `services.prosody.settings.modules_disabled'." ] ++
553 mapAttrsToList (k: v: "The option `services.prosody.virtualHosts.${k}.enabled' has been renamed to `services.prosody.virtualHosts.${k}.settings.enabled'.") (filterAttrs (_: v: v.enabled != null) cfg.virtualHosts) ++
554 mapAttrsToList (k: v: "The option `services.prosody.virtualHosts.${k}.ssl' has been renamed to `services.prosody.virtualHosts.${k}.settings.ssl'.") (filterAttrs (_: v: v.ssl != null) cfg.virtualHosts)
555 ;
556
557 services.prosody.settings = {
558 log = mkDefault "*syslog";
559 data_path = mkForce cfg.dataDir;
560 network_backend = "event";
561 pidfile = "/run/prosody/prosody.pid";
562
563 authentication = mkDefault "internal_hashed";
564 reload_modules = mkIf (acmeHosts != [ ]) [ "tls" ];
565
566 modules_enabled = [
567 # required for compliance with https://compliance.conversations.im/about/
568 "dialback"
569 "disco"
570 "roster"
571 "saslauth"
572 "tls"
573
574 # not essential, but recommended
575 "blocklist"
576 "bookmarks"
577 "carbons"
578 "cloud_notify"
579 "csi"
580 "pep"
581 "private"
582 "vcard_legacy"
583
584 # nice to have
585 "mam"
586 "ping"
587 "register"
588 "smacks"
589 "time"
590 "uptime"
591 "version"
592
593 # admin interfaces
594 "admin_adhoc"
595 "http_files"
596 "proxy65"
597 ] ++ cfg.package.communityModules
598 ++ mapAttrsToList (k: _: k) (filterAttrs (_: v: v == true) cfg.modules);
599
600 modules_disabled = mapAttrsToList (k: _: k) (filterAttrs (_: v: v == false) cfg.modules);
601
602 disco_items =
603 mapAttrsToList (k: v: [ k "${k} HTTP upload endpoint" ]) (filterAttrs (k: v: v.module == "http_upload") cfg.components) ++
604 mapAttrsToList (k: v: [ k "${k} MUC endpoint" ]) (filterAttrs (k: v: v.module == "muc") cfg.components)
605 ;
606
607 # mod_tls configuration
608 c2s_require_encryption = mkDefault true;
609 s2s_require_encryption = mkDefault true;
610
611 # upstream defaults - useful for `services.prosody.openFirewall` logic
612 c2s_ports = mkDefault [ 5222 ];
613 s2s_ports = mkDefault [ 5269 ];
614 https_ports = mkDefault [ 5281 ];
615 # TODO:
616 # proxy65_ports = [ 5000 ];
617 };
618
619 environment.systemPackages = [ cfg.package ];
620 environment.etc."prosody/prosody.cfg.lua".text =
621 let
622 toFormat = attrs: concatStringsSep "\n" (mapAttrsToList (k: v: ''${k} = ${toStr v}'') (filterAttrs (_: v: v != null) attrs));
623
624 toStr = v:
625 if isString v then
626 # prosody will directly pass environment variables into its configuration file which have `ENV_` as a prefix
627 if hasPrefix "ENV_" v then v else ''"${toString v}"''
628 else if isBool v then boolToString v
629 else if isInt v then toString v
630 else if isList v then ''{ ${concatStringsSep ", " (map (n: toStr n) v)} }''
631 else if isAttrs v then
632 if v ? __compat then v.value else ''{ ${concatStringsSep ", " (mapAttrsToList (a: b: ''["${a}"] = ${toStr b}'') v)} }''
633 else throw "Invalid Lua value";
634
635 componentsToStr = components:
636 concatStringsSep "\n" (
637 mapAttrsToList
638 (domain: component:
639 ''
640 Component "${domain}" ${optionalString (component.module != null) "\"${component.module}\""}
641 ${toFormat component.settings}
642 ${component.extraConfig}
643 ''
644 )
645 components
646 );
647
648 virtualHostsToStr = virtualHosts:
649 concatStringsSep "\n" (
650 mapAttrsToList
651 (_: virtualHost:
652 ''
653 VirtualHost "${virtualHost.domain}"
654 ${toFormat virtualHost.settings}
655 ${virtualHost.extraConfig}
656
657 ${componentsToStr virtualHost.components}
658 ''
659 )
660 virtualHosts
661 );
662 in
663 ''
664 ${toFormat cfg.settings}
665 ${cfg.extraConfig}
666
667 ${componentsToStr cfg.components}
668 ${virtualHostsToStr cfg.virtualHosts}
669 '';
670
671 systemd.services.prosody = {
672 description = "Prosody XMPP server";
673 wantedBy = [ "multi-user.target" ];
674 before = map (domain: "acme-${domain}.service") acmeHosts;
675 after = [ "network-online.target" ] ++ map (domain: "acme-selfsigned-${domain}.service") acmeHosts;
676 wants = [ "network-online.target" ] ++ map (domain: "acme-finished-${domain}.target") acmeHosts;
677
678 restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
679 serviceConfig = mkMerge [
680 {
681 User = cfg.user;
682 Group = cfg.group;
683 Type = "forking";
684 PIDFile = cfg.settings.pidfile;
685 ExecStart = "${cfg.package}/bin/prosodyctl start";
686 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
687 EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
688
689 MemoryDenyWriteExecute = true;
690 PrivateDevices = true;
691 PrivateMounts = true;
692 PrivateTmp = true;
693 ProtectControlGroups = true;
694 ProtectHome = true;
695 ProtectHostname = true;
696 ProtectKernelModules = true;
697 ProtectKernelTunables = true;
698 RestrictNamespaces = true;
699 RestrictRealtime = true;
700 RestrictSUIDSGID = true;
701 }
702 (mkIf (cfg.dataDir == "/var/lib/prosody") {
703 StateDirectory = "prosody";
704 StateDirectoryMode = "0750";
705 })
706 (mkIf (cfg.settings.pidfile == "/run/prosody/prosody.pid") {
707 RuntimeDirectory = [ "prosody" ];
708 })
709 ];
710 };
711
712 security.acme.certs = genAttrs acmeHosts (_: {
713 reloadServices = [ "prosody.service" ];
714 });
715
716 networking.firewall = optionalAttrs cfg.openFirewall {
717 allowedTCPPorts = cfg.settings.c2s_ports
718 ++ cfg.settings.s2s_ports
719 ++ cfg.settings.https_ports
720 # TODO: cfg.settings.proxy65_ports
721 ;
722 };
723
724 users.users.prosody = mkIf (cfg.user == "prosody") {
725 uid = config.ids.uids.prosody;
726 description = "Prosody user";
727 inherit (cfg) group;
728 home = cfg.dataDir;
729 };
730
731 users.groups.prosody = mkIf (cfg.group == "prosody") {
732 gid = config.ids.gids.prosody;
733 };
734 };
735
736 meta.doc = ./prosody.md;
737 }