1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 cfg = config.services.prosody;
6
7 sslOpts = { ... }: {
8
9 options = {
10
11 key = mkOption {
12 type = types.path;
13 description = "Path to the key file.";
14 };
15
16 # TODO: rename to certificate to match the prosody config
17 cert = mkOption {
18 type = types.path;
19 description = "Path to the certificate file.";
20 };
21
22 extraOptions = mkOption {
23 type = types.attrs;
24 default = {};
25 description = "Extra SSL configuration options.";
26 };
27
28 };
29 };
30
31 discoOpts = {
32 options = {
33 url = mkOption {
34 type = types.str;
35 description = "URL of the endpoint you want to make discoverable";
36 };
37 description = mkOption {
38 type = types.str;
39 description = "A short description of the endpoint you want to advertise";
40 };
41 };
42 };
43
44 moduleOpts = {
45 # Required for compliance with https://compliance.conversations.im/about/
46 roster = mkOption {
47 type = types.bool;
48 default = true;
49 description = "Allow users to have a roster";
50 };
51
52 saslauth = mkOption {
53 type = types.bool;
54 default = true;
55 description = "Authentication for clients and servers. Recommended if you want to log in.";
56 };
57
58 tls = mkOption {
59 type = types.bool;
60 default = true;
61 description = "Add support for secure TLS on c2s/s2s connections";
62 };
63
64 dialback = mkOption {
65 type = types.bool;
66 default = true;
67 description = "s2s dialback support";
68 };
69
70 disco = mkOption {
71 type = types.bool;
72 default = true;
73 description = "Service discovery";
74 };
75
76 # Not essential, but recommended
77 carbons = mkOption {
78 type = types.bool;
79 default = true;
80 description = "Keep multiple clients in sync";
81 };
82
83 csi = mkOption {
84 type = types.bool;
85 default = true;
86 description = "Implements the CSI protocol that allows clients to report their active/inactive state to the server";
87 };
88
89 cloud_notify = mkOption {
90 type = types.bool;
91 default = true;
92 description = "Push notifications to inform users of new messages or other pertinent information even when they have no XMPP clients online";
93 };
94
95 pep = mkOption {
96 type = types.bool;
97 default = true;
98 description = "Enables users to publish their mood, activity, playing music and more";
99 };
100
101 private = mkOption {
102 type = types.bool;
103 default = true;
104 description = "Private XML storage (for room bookmarks, etc.)";
105 };
106
107 blocklist = mkOption {
108 type = types.bool;
109 default = true;
110 description = "Allow users to block communications with other users";
111 };
112
113 vcard = mkOption {
114 type = types.bool;
115 default = false;
116 description = "Allow users to set vCards";
117 };
118
119 vcard_legacy = mkOption {
120 type = types.bool;
121 default = true;
122 description = "Converts users profiles and Avatars between old and new formats";
123 };
124
125 bookmarks = mkOption {
126 type = types.bool;
127 default = true;
128 description = "Allows interop between older clients that use XEP-0048: Bookmarks in its 1.0 version and recent clients which use it in PEP";
129 };
130
131 # Nice to have
132 version = mkOption {
133 type = types.bool;
134 default = true;
135 description = "Replies to server version requests";
136 };
137
138 uptime = mkOption {
139 type = types.bool;
140 default = true;
141 description = "Report how long server has been running";
142 };
143
144 time = mkOption {
145 type = types.bool;
146 default = true;
147 description = "Let others know the time here on this server";
148 };
149
150 ping = mkOption {
151 type = types.bool;
152 default = true;
153 description = "Replies to XMPP pings with pongs";
154 };
155
156 register = mkOption {
157 type = types.bool;
158 default = true;
159 description = "Allow users to register on this server using a client and change passwords";
160 };
161
162 mam = mkOption {
163 type = types.bool;
164 default = true;
165 description = "Store messages in an archive and allow users to access it";
166 };
167
168 smacks = mkOption {
169 type = types.bool;
170 default = true;
171 description = "Allow a client to resume a disconnected session, and prevent message loss";
172 };
173
174 # Admin interfaces
175 admin_adhoc = mkOption {
176 type = types.bool;
177 default = true;
178 description = "Allows administration via an XMPP client that supports ad-hoc commands";
179 };
180
181 http_files = mkOption {
182 type = types.bool;
183 default = true;
184 description = "Serve static files from a directory over HTTP";
185 };
186
187 proxy65 = mkOption {
188 type = types.bool;
189 default = true;
190 description = "Enables a file transfer proxy service which clients behind NAT can use";
191 };
192
193 admin_telnet = mkOption {
194 type = types.bool;
195 default = false;
196 description = "Opens telnet console interface on localhost port 5582";
197 };
198
199 # HTTP modules
200 bosh = mkOption {
201 type = types.bool;
202 default = false;
203 description = "Enable BOSH clients, aka 'Jabber over HTTP'";
204 };
205
206 websocket = mkOption {
207 type = types.bool;
208 default = false;
209 description = "Enable WebSocket support";
210 };
211
212 # Other specific functionality
213 limits = mkOption {
214 type = types.bool;
215 default = false;
216 description = "Enable bandwidth limiting for XMPP connections";
217 };
218
219 groups = mkOption {
220 type = types.bool;
221 default = false;
222 description = "Shared roster support";
223 };
224
225 server_contact_info = mkOption {
226 type = types.bool;
227 default = false;
228 description = "Publish contact information for this service";
229 };
230
231 announce = mkOption {
232 type = types.bool;
233 default = false;
234 description = "Send announcement to all online users";
235 };
236
237 welcome = mkOption {
238 type = types.bool;
239 default = false;
240 description = "Welcome users who register accounts";
241 };
242
243 watchregistrations = mkOption {
244 type = types.bool;
245 default = false;
246 description = "Alert admins of registrations";
247 };
248
249 motd = mkOption {
250 type = types.bool;
251 default = false;
252 description = "Send a message to users when they log in";
253 };
254
255 legacyauth = mkOption {
256 type = types.bool;
257 default = false;
258 description = "Legacy authentication. Only used by some old clients and bots";
259 };
260 };
261
262 toLua = x:
263 if builtins.isString x then ''"${x}"''
264 else if builtins.isBool x then boolToString x
265 else if builtins.isInt x then toString x
266 else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }''
267 else throw "Invalid Lua value";
268
269 createSSLOptsStr = o: ''
270 ssl = {
271 cafile = "/etc/ssl/certs/ca-bundle.crt";
272 key = "${o.key}";
273 certificate = "${o.cert}";
274 ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)}
275 };
276 '';
277
278 mucOpts = { ... }: {
279 options = {
280 domain = mkOption {
281 type = types.str;
282 description = "Domain name of the MUC";
283 };
284 name = mkOption {
285 type = types.str;
286 description = "The name to return in service discovery responses for the MUC service itself";
287 default = "Prosody Chatrooms";
288 };
289 restrictRoomCreation = mkOption {
290 type = types.enum [ true false "admin" "local" ];
291 default = false;
292 description = "Restrict room creation to server admins";
293 };
294 maxHistoryMessages = mkOption {
295 type = types.int;
296 default = 20;
297 description = "Specifies a limit on what each room can be configured to keep";
298 };
299 roomLocking = mkOption {
300 type = types.bool;
301 default = true;
302 description = ''
303 Enables room locking, which means that a room must be
304 configured before it can be used. Locked rooms are invisible
305 and cannot be entered by anyone but the creator
306 '';
307 };
308 roomLockTimeout = mkOption {
309 type = types.int;
310 default = 300;
311 description = ''
312 Timout after which the room is destroyed or unlocked if not
313 configured, in seconds
314 '';
315 };
316 tombstones = mkOption {
317 type = types.bool;
318 default = true;
319 description = ''
320 When a room is destroyed, it leaves behind a tombstone which
321 prevents the room being entered or recreated. It also allows
322 anyone who was not in the room at the time it was destroyed
323 to learn about it, and to update their bookmarks. Tombstones
324 prevents the case where someone could recreate a previously
325 semi-anonymous room in order to learn the real JIDs of those
326 who often join there.
327 '';
328 };
329 tombstoneExpiry = mkOption {
330 type = types.int;
331 default = 2678400;
332 description = ''
333 This settings controls how long a tombstone is considered
334 valid. It defaults to 31 days. After this time, the room in
335 question can be created again.
336 '';
337 };
338
339 vcard_muc = mkOption {
340 type = types.bool;
341 default = true;
342 description = "Adds the ability to set vCard for Multi User Chat rooms";
343 };
344
345 # Extra parameters. Defaulting to prosody default values.
346 # Adding them explicitly to make them visible from the options
347 # documentation.
348 #
349 # See https://prosody.im/doc/modules/mod_muc for more details.
350 roomDefaultPublic = mkOption {
351 type = types.bool;
352 default = true;
353 description = "If set, the MUC rooms will be public by default.";
354 };
355 roomDefaultMembersOnly = mkOption {
356 type = types.bool;
357 default = false;
358 description = "If set, the MUC rooms will only be accessible to the members by default.";
359 };
360 roomDefaultModerated = mkOption {
361 type = types.bool;
362 default = false;
363 description = "If set, the MUC rooms will be moderated by default.";
364 };
365 roomDefaultPublicJids = mkOption {
366 type = types.bool;
367 default = false;
368 description = "If set, the MUC rooms will display the public JIDs by default.";
369 };
370 roomDefaultChangeSubject = mkOption {
371 type = types.bool;
372 default = false;
373 description = "If set, the rooms will display the public JIDs by default.";
374 };
375 roomDefaultHistoryLength = mkOption {
376 type = types.int;
377 default = 20;
378 description = "Number of history message sent to participants by default.";
379 };
380 roomDefaultLanguage = mkOption {
381 type = types.str;
382 default = "en";
383 description = "Default room language.";
384 };
385 extraConfig = mkOption {
386 type = types.lines;
387 default = "";
388 description = "Additional MUC specific configuration";
389 };
390 };
391 };
392
393 uploadHttpOpts = { ... }: {
394 options = {
395 domain = mkOption {
396 type = types.nullOr types.str;
397 description = "Domain name for the http-upload service";
398 };
399 uploadFileSizeLimit = mkOption {
400 type = types.str;
401 default = "50 * 1024 * 1024";
402 description = "Maximum file size, in bytes. Defaults to 50MB.";
403 };
404 uploadExpireAfter = mkOption {
405 type = types.str;
406 default = "60 * 60 * 24 * 7";
407 description = "Max age of a file before it gets deleted, in seconds.";
408 };
409 userQuota = mkOption {
410 type = types.nullOr types.int;
411 default = null;
412 example = 1234;
413 description = ''
414 Maximum size of all uploaded files per user, in bytes. There
415 will be no quota if this option is set to null.
416 '';
417 };
418 httpUploadPath = mkOption {
419 type = types.str;
420 description = ''
421 Directory where the uploaded files will be stored. By
422 default, uploaded files are put in a sub-directory of the
423 default Prosody storage path (usually /var/lib/prosody).
424 '';
425 default = "/var/lib/prosody";
426 };
427 };
428 };
429
430 vHostOpts = { ... }: {
431
432 options = {
433
434 # TODO: require attribute
435 domain = mkOption {
436 type = types.str;
437 description = "Domain name";
438 };
439
440 enabled = mkOption {
441 type = types.bool;
442 default = false;
443 description = "Whether to enable the virtual host";
444 };
445
446 ssl = mkOption {
447 type = types.nullOr (types.submodule sslOpts);
448 default = null;
449 description = "Paths to SSL files";
450 };
451
452 extraConfig = mkOption {
453 type = types.lines;
454 default = "";
455 description = "Additional virtual host specific configuration";
456 };
457
458 };
459
460 };
461
462in
463
464{
465
466 ###### interface
467
468 options = {
469
470 services.prosody = {
471
472 enable = mkOption {
473 type = types.bool;
474 default = false;
475 description = "Whether to enable the prosody server";
476 };
477
478 xmppComplianceSuite = mkOption {
479 type = types.bool;
480 default = true;
481 description = ''
482 The XEP-0423 defines a set of recommended XEPs to implement
483 for a server. It's generally a good idea to implement this
484 set of extensions if you want to provide your users with a
485 good XMPP experience.
486
487 This NixOS module aims to provide a "advanced server"
488 experience as per defined in the XEP-0423[1] specification.
489
490 Setting this option to true will prevent you from building a
491 NixOS configuration which won't comply with this standard.
492 You can explicitely decide to ignore this standard if you
493 know what you are doing by setting this option to false.
494
495 [1] https://xmpp.org/extensions/xep-0423.html
496 '';
497 };
498
499 package = mkOption {
500 type = types.package;
501 description = "Prosody package to use";
502 default = pkgs.prosody;
503 defaultText = "pkgs.prosody";
504 example = literalExample ''
505 pkgs.prosody.override {
506 withExtraLibs = [ pkgs.luaPackages.lpty ];
507 withCommunityModules = [ "auth_external" ];
508 };
509 '';
510 };
511
512 dataDir = mkOption {
513 type = types.path;
514 description = "Directory where Prosody stores its data";
515 default = "/var/lib/prosody";
516 };
517
518 disco_items = mkOption {
519 type = types.listOf (types.submodule discoOpts);
520 default = [];
521 description = "List of discoverable items you want to advertise.";
522 };
523
524 user = mkOption {
525 type = types.str;
526 default = "prosody";
527 description = "User account under which prosody runs.";
528 };
529
530 group = mkOption {
531 type = types.str;
532 default = "prosody";
533 description = "Group account under which prosody runs.";
534 };
535
536 allowRegistration = mkOption {
537 type = types.bool;
538 default = false;
539 description = "Allow account creation";
540 };
541
542 # HTTP server-related options
543 httpPorts = mkOption {
544 type = types.listOf types.int;
545 description = "Listening HTTP ports list for this service.";
546 default = [ 5280 ];
547 };
548
549 httpInterfaces = mkOption {
550 type = types.listOf types.str;
551 default = [ "*" "::" ];
552 description = "Interfaces on which the HTTP server will listen on.";
553 };
554
555 httpsPorts = mkOption {
556 type = types.listOf types.int;
557 description = "Listening HTTPS ports list for this service.";
558 default = [ 5281 ];
559 };
560
561 httpsInterfaces = mkOption {
562 type = types.listOf types.str;
563 default = [ "*" "::" ];
564 description = "Interfaces on which the HTTPS server will listen on.";
565 };
566
567 c2sRequireEncryption = mkOption {
568 type = types.bool;
569 default = true;
570 description = ''
571 Force clients to use encrypted connections? This option will
572 prevent clients from authenticating unless they are using encryption.
573 '';
574 };
575
576 s2sRequireEncryption = mkOption {
577 type = types.bool;
578 default = true;
579 description = ''
580 Force servers to use encrypted connections? This option will
581 prevent servers from authenticating unless they are using encryption.
582 Note that this is different from authentication.
583 '';
584 };
585
586 s2sSecureAuth = mkOption {
587 type = types.bool;
588 default = false;
589 description = ''
590 Force certificate authentication for server-to-server connections?
591 This provides ideal security, but requires servers you communicate
592 with to support encryption AND present valid, trusted certificates.
593 For more information see https://prosody.im/doc/s2s#security
594 '';
595 };
596
597 s2sInsecureDomains = mkOption {
598 type = types.listOf types.str;
599 default = [];
600 example = [ "insecure.example.com" ];
601 description = ''
602 Some servers have invalid or self-signed certificates. You can list
603 remote domains here that will not be required to authenticate using
604 certificates. They will be authenticated using DNS instead, even
605 when s2s_secure_auth is enabled.
606 '';
607 };
608
609 s2sSecureDomains = mkOption {
610 type = types.listOf types.str;
611 default = [];
612 example = [ "jabber.org" ];
613 description = ''
614 Even if you leave s2s_secure_auth disabled, you can still require valid
615 certificates for some domains by specifying a list here.
616 '';
617 };
618
619
620 modules = moduleOpts;
621
622 extraModules = mkOption {
623 type = types.listOf types.str;
624 default = [];
625 description = "Enable custom modules";
626 };
627
628 extraPluginPaths = mkOption {
629 type = types.listOf types.path;
630 default = [];
631 description = "Addtional path in which to look find plugins/modules";
632 };
633
634 uploadHttp = mkOption {
635 description = ''
636 Configures the Prosody builtin HTTP server to handle user uploads.
637 '';
638 type = types.nullOr (types.submodule uploadHttpOpts);
639 default = null;
640 example = {
641 domain = "uploads.my-xmpp-example-host.org";
642 };
643 };
644
645 muc = mkOption {
646 type = types.listOf (types.submodule mucOpts);
647 default = [ ];
648 example = [ {
649 domain = "conference.my-xmpp-example-host.org";
650 } ];
651 description = "Multi User Chat (MUC) configuration";
652 };
653
654 virtualHosts = mkOption {
655
656 description = "Define the virtual hosts";
657
658 type = with types; attrsOf (submodule vHostOpts);
659
660 example = {
661 myhost = {
662 domain = "my-xmpp-example-host.org";
663 enabled = true;
664 };
665 };
666
667 default = {
668 localhost = {
669 domain = "localhost";
670 enabled = true;
671 };
672 };
673
674 };
675
676 ssl = mkOption {
677 type = types.nullOr (types.submodule sslOpts);
678 default = null;
679 description = "Paths to SSL files";
680 };
681
682 admins = mkOption {
683 type = types.listOf types.str;
684 default = [];
685 example = [ "admin1@example.com" "admin2@example.com" ];
686 description = "List of administrators of the current host";
687 };
688
689 authentication = mkOption {
690 type = types.enum [ "internal_plain" "internal_hashed" "cyrus" "anonymous" ];
691 default = "internal_hashed";
692 example = "internal_plain";
693 description = "Authentication mechanism used for logins.";
694 };
695
696 extraConfig = mkOption {
697 type = types.lines;
698 default = "";
699 description = "Additional prosody configuration";
700 };
701
702 };
703 };
704
705
706 ###### implementation
707
708 config = mkIf cfg.enable {
709
710 assertions = let
711 genericErrMsg = ''
712
713 Having a server not XEP-0423-compliant might make your XMPP
714 experience terrible. See the NixOS manual for further
715 informations.
716
717 If you know what you're doing, you can disable this warning by
718 setting config.services.prosody.xmppComplianceSuite to false.
719 '';
720 errors = [
721 { assertion = (builtins.length cfg.muc > 0) || !cfg.xmppComplianceSuite;
722 message = ''
723 You need to setup at least a MUC domain to comply with
724 XEP-0423.
725 '' + genericErrMsg;}
726 { assertion = cfg.uploadHttp != null || !cfg.xmppComplianceSuite;
727 message = ''
728 You need to setup the uploadHttp module through
729 config.services.prosody.uploadHttp to comply with
730 XEP-0423.
731 '' + genericErrMsg;}
732 ];
733 in errors;
734
735 environment.systemPackages = [ cfg.package ];
736
737 environment.etc."prosody/prosody.cfg.lua".text =
738 let
739 httpDiscoItems = if (cfg.uploadHttp != null)
740 then [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}]
741 else [];
742 mucDiscoItems = builtins.foldl'
743 (acc: muc: [{ url = muc.domain; description = "${muc.domain} MUC endpoint";}] ++ acc)
744 []
745 cfg.muc;
746 discoItems = cfg.disco_items ++ httpDiscoItems ++ mucDiscoItems;
747 in ''
748
749 pidfile = "/run/prosody/prosody.pid"
750
751 log = "*syslog"
752
753 data_path = "${cfg.dataDir}"
754 plugin_paths = {
755 ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) }
756 }
757
758 ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) }
759
760 admins = ${toLua cfg.admins}
761
762 -- we already build with libevent, so we can just enable it for a more performant server
763 use_libevent = true
764
765 modules_enabled = {
766
767 ${ lib.concatStringsSep "\n " (lib.mapAttrsToList
768 (name: val: optionalString val "${toLua name};")
769 cfg.modules) }
770 ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)}
771 ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)}
772 };
773
774 disco_items = {
775 ${ lib.concatStringsSep "\n" (builtins.map (x: ''{ "${x.url}", "${x.description}"};'') discoItems)}
776 };
777
778 allow_registration = ${toLua cfg.allowRegistration}
779
780 c2s_require_encryption = ${toLua cfg.c2sRequireEncryption}
781
782 s2s_require_encryption = ${toLua cfg.s2sRequireEncryption}
783
784 s2s_secure_auth = ${toLua cfg.s2sSecureAuth}
785
786 s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains}
787
788 s2s_secure_domains = ${toLua cfg.s2sSecureDomains}
789
790 authentication = ${toLua cfg.authentication}
791
792 http_interfaces = ${toLua cfg.httpInterfaces}
793
794 https_interfaces = ${toLua cfg.httpsInterfaces}
795
796 http_ports = ${toLua cfg.httpPorts}
797
798 https_ports = ${toLua cfg.httpsPorts}
799
800 ${ cfg.extraConfig }
801
802 ${lib.concatMapStrings (muc: ''
803 Component ${toLua muc.domain} "muc"
804 modules_enabled = { "muc_mam"; ${optionalString muc.vcard_muc ''"vcard_muc";'' } }
805 name = ${toLua muc.name}
806 restrict_room_creation = ${toLua muc.restrictRoomCreation}
807 max_history_messages = ${toLua muc.maxHistoryMessages}
808 muc_room_locking = ${toLua muc.roomLocking}
809 muc_room_lock_timeout = ${toLua muc.roomLockTimeout}
810 muc_tombstones = ${toLua muc.tombstones}
811 muc_tombstone_expiry = ${toLua muc.tombstoneExpiry}
812 muc_room_default_public = ${toLua muc.roomDefaultPublic}
813 muc_room_default_members_only = ${toLua muc.roomDefaultMembersOnly}
814 muc_room_default_moderated = ${toLua muc.roomDefaultModerated}
815 muc_room_default_public_jids = ${toLua muc.roomDefaultPublicJids}
816 muc_room_default_change_subject = ${toLua muc.roomDefaultChangeSubject}
817 muc_room_default_history_length = ${toLua muc.roomDefaultHistoryLength}
818 muc_room_default_language = ${toLua muc.roomDefaultLanguage}
819 ${ muc.extraConfig }
820 '') cfg.muc}
821
822 ${ lib.optionalString (cfg.uploadHttp != null) ''
823 Component ${toLua cfg.uploadHttp.domain} "http_upload"
824 http_upload_file_size_limit = ${cfg.uploadHttp.uploadFileSizeLimit}
825 http_upload_expire_after = ${cfg.uploadHttp.uploadExpireAfter}
826 ${lib.optionalString (cfg.uploadHttp.userQuota != null) "http_upload_quota = ${toLua cfg.uploadHttp.userQuota}"}
827 http_upload_path = ${toLua cfg.uploadHttp.httpUploadPath}
828 ''}
829
830 ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
831 VirtualHost "${v.domain}"
832 enabled = ${boolToString v.enabled};
833 ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) }
834 ${ v.extraConfig }
835 '') cfg.virtualHosts) }
836 '';
837
838 users.users.prosody = mkIf (cfg.user == "prosody") {
839 uid = config.ids.uids.prosody;
840 description = "Prosody user";
841 createHome = true;
842 inherit (cfg) group;
843 home = "${cfg.dataDir}";
844 };
845
846 users.groups.prosody = mkIf (cfg.group == "prosody") {
847 gid = config.ids.gids.prosody;
848 };
849
850 systemd.services.prosody = {
851 description = "Prosody XMPP server";
852 after = [ "network-online.target" ];
853 wants = [ "network-online.target" ];
854 wantedBy = [ "multi-user.target" ];
855 restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
856 serviceConfig = {
857 User = cfg.user;
858 Group = cfg.group;
859 Type = "forking";
860 RuntimeDirectory = [ "prosody" ];
861 PIDFile = "/run/prosody/prosody.pid";
862 ExecStart = "${cfg.package}/bin/prosodyctl start";
863 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
864
865 MemoryDenyWriteExecute = true;
866 PrivateDevices = true;
867 PrivateMounts = true;
868 PrivateTmp = true;
869 ProtectControlGroups = true;
870 ProtectHome = true;
871 ProtectHostname = true;
872 ProtectKernelModules = true;
873 ProtectKernelTunables = true;
874 RestrictNamespaces = true;
875 RestrictRealtime = true;
876 RestrictSUIDSGID = true;
877 };
878 };
879
880 };
881 meta.doc = ./prosody.xml;
882}