orchard: 0.26.3 -> 0.26.4 (#377709)
[NixPkgs.git] / nixos / modules / services / mail / cyrus-imap.nix
blob12d63431cd732bb97c4269a24ffd64b6114aafc4
2   pkgs,
3   lib,
4   config,
5   ...
6 }:
7 let
8   cfg = config.services.cyrus-imap;
9   cyrus-imapdPkg = pkgs.cyrus-imapd;
10   inherit (lib)
11     mkEnableOption
12     mkIf
13     mkOption
14     optionalAttrs
15     optionalString
16     generators
17     mapAttrsToList
18     ;
19   inherit (lib.strings) concatStringsSep;
20   inherit (lib.types)
21     attrsOf
22     submodule
23     listOf
24     oneOf
25     str
26     int
27     bool
28     nullOr
29     path
30     ;
32   mkCyrusConfig =
33     settings:
34     let
35       mkCyrusOptionsList =
36         v:
37         mapAttrsToList (
38           p: q:
39           if (q != null) then
40             if builtins.isInt q then
41               "${p}=${builtins.toString q}"
42             else
43               "${p}=\"${if builtins.isList q then (concatStringsSep " " q) else q}\""
44           else
45             ""
46         ) v;
47       mkCyrusOptionsString = v: concatStringsSep " " (mkCyrusOptionsList v);
48     in
49     concatStringsSep "\n  " (mapAttrsToList (n: v: n + " " + (mkCyrusOptionsString v)) settings);
51   cyrusConfig = lib.concatStringsSep "\n" (
52     lib.mapAttrsToList (n: v: ''
53       ${n} {
54         ${mkCyrusConfig v}
55       }
56     '') cfg.cyrusSettings
57   );
59   imapdConfig =
60     with generators;
61     toKeyValue {
62       mkKeyValue = mkKeyValueDefault {
63         mkValueString =
64           v:
65           if builtins.isBool v then
66             if v then "yes" else "no"
67           else if builtins.isList v then
68             concatStringsSep " " v
69           else
70             mkValueStringDefault { } v;
71       } ": ";
72     } cfg.imapdSettings;
75   options.services.cyrus-imap = {
76     enable = mkEnableOption "Cyrus IMAP, an email, contacts and calendar server";
77     debug = mkEnableOption "debugging messages for the Cyrus master process";
79     listenQueue = mkOption {
80       type = int;
81       default = 32;
82       description = ''
83         Socket listen queue backlog size. See {manpage}`listen(2)` for more information about a backlog.
84         Default is 32, which may be increased if you have a very high connection rate.
85       '';
86     };
87     tmpDBDir = mkOption {
88       type = path;
89       default = "/run/cyrus/db";
90       description = ''
91         Location where DB files are stored.
92         Databases in this directory are recreated upon startup, so ideally they should live in ephemeral storage for best performance.
93       '';
94     };
95     cyrusSettings = mkOption {
96       type = submodule {
97         freeformType = attrsOf (
98           attrsOf (oneOf [
99             bool
100             int
101             (listOf str)
102           ])
103         );
104         options = {
105           START = mkOption {
106             default = {
107               recover = {
108                 cmd = [
109                   "ctl_cyrusdb"
110                   "-r"
111                 ];
112               };
113             };
114             description = ''
115               This section lists the processes to run before any SERVICES are spawned.
116               This section is typically used to initialize databases.
117               Master itself will not startup until all tasks in START have completed, so put no blocking commands here.
118             '';
119           };
120           SERVICES = mkOption {
121             default = {
122               imap = {
123                 cmd = [ "imapd" ];
124                 listen = "imap";
125                 prefork = 0;
126               };
127               pop3 = {
128                 cmd = [ "pop3d" ];
129                 listen = "pop3";
130                 prefork = 0;
131               };
132               lmtpunix = {
133                 cmd = [ "lmtpd" ];
134                 listen = "/run/cyrus/lmtp";
135                 prefork = 0;
136               };
137               notify = {
138                 cmd = [ "notifyd" ];
139                 listen = "/run/cyrus/notify";
140                 proto = "udp";
141                 prefork = 0;
142               };
143             };
144             description = ''
145               This section is the heart of the cyrus.conf file. It lists the processes that should be spawned to handle client connections made on certain Internet/UNIX sockets.
146             '';
147           };
148           EVENTS = mkOption {
149             default = {
150               tlsprune = {
151                 cmd = [ "tls_prune" ];
152                 at = 400;
153               };
154               delprune = {
155                 cmd = [
156                   "cyr_expire"
157                   "-E"
158                   "3"
159                 ];
160                 at = 400;
161               };
162               deleteprune = {
163                 cmd = [
164                   "cyr_expire"
165                   "-E"
166                   "4"
167                   "-D"
168                   "28"
169                 ];
170                 at = 430;
171               };
172               expungeprune = {
173                 cmd = [
174                   "cyr_expire"
175                   "-E"
176                   "4"
177                   "-X"
178                   "28"
179                 ];
180                 at = 445;
181               };
182               checkpoint = {
183                 cmd = [
184                   "ctl_cyrusdb"
185                   "-c"
186                 ];
187                 period = 30;
188               };
189             };
190             description = ''
191               This section lists processes that should be run at specific intervals, similar to cron jobs. This section is typically used to perform scheduled cleanup/maintenance.
192             '';
193           };
194           DAEMON = mkOption {
195             default = { };
196             description = ''
197               This section lists long running daemons to start before any SERVICES are spawned. {manpage}`master(8)` will ensure that these processes are running, restarting any process which dies or forks. All listed processes will be shutdown when {manpage}`master(8)` is exiting.
198             '';
199           };
200         };
201       };
202       description = "Cyrus configuration settings. See [cyrus.conf(5)](https://www.cyrusimap.org/imap/reference/manpages/configs/cyrus.conf.html)";
203     };
204     imapdSettings = mkOption {
205       type = submodule {
206         freeformType = attrsOf (oneOf [
207           str
208           int
209           bool
210           (listOf str)
211         ]);
212         options = {
213           configdirectory = mkOption {
214             type = path;
215             default = "/var/lib/cyrus";
216             description = ''
217               The pathname of the IMAP configuration directory.
218             '';
219           };
220           lmtpsocket = mkOption {
221             type = path;
222             default = "/run/cyrus/lmtp";
223             description = ''
224               Unix socket that lmtpd listens on, used by {manpage}`deliver(8)`. This should match the path specified in {manpage}`cyrus.conf(5)`.
225             '';
226           };
227           idlesocket = mkOption {
228             type = path;
229             default = "/run/cyrus/idle";
230             description = ''
231               Unix socket that idled listens on.
232             '';
233           };
234           notifysocket = mkOption {
235             type = path;
236             default = "/run/cyrus/notify";
237             description = ''
238               Unix domain socket that the mail notification daemon listens on.
239             '';
240           };
241         };
242       };
243       default = {
244         admins = [ "cyrus" ];
245         allowplaintext = true;
246         defaultdomain = "localhost";
247         defaultpartition = "default";
248         duplicate_db_path = "/run/cyrus/db/deliver.db";
249         hashimapspool = true;
250         httpmodules = [
251           "carddav"
252           "caldav"
253         ];
254         mboxname_lockpath = "/run/cyrus/lock";
255         partition-default = "/var/lib/cyrus/storage";
256         popminpoll = 1;
257         proc_path = "/run/cyrus/proc";
258         ptscache_db_path = "/run/cyrus/db/ptscache.db";
259         sasl_auto_transition = true;
260         sasl_pwcheck_method = [ "saslauthd" ];
261         sievedir = "/var/lib/cyrus/sieve";
262         statuscache_db_path = "/run/cyrus/db/statuscache.db";
263         syslog_prefix = "cyrus";
264         tls_client_ca_dir = "/etc/ssl/certs";
265         tls_session_timeout = 1440;
266         tls_sessions_db_path = "/run/cyrus/db/tls_sessions.db";
267         virtdomains = "on";
268       };
269       description = "IMAP configuration settings. See [imapd.conf(5)](https://www.cyrusimap.org/imap/reference/manpages/configs/imapd.conf.html)";
270     };
272     user = mkOption {
273       type = nullOr str;
274       default = null;
275       description = "Cyrus IMAP user name. If this is not set, a user named `cyrus` will be created.";
276     };
278     group = mkOption {
279       type = nullOr str;
280       default = null;
281       description = "Cyrus IMAP group name. If this is not set, a group named `cyrus` will be created.";
282     };
284     imapdConfigFile = mkOption {
285       type = nullOr path;
286       default = null;
287       description = "Path to the configuration file used for cyrus-imap.";
288       apply = v: if v != null then v else pkgs.writeText "imapd.conf" imapdConfig;
289     };
291     cyrusConfigFile = mkOption {
292       type = nullOr path;
293       default = null;
294       description = "Path to the configuration file used for Cyrus.";
295       apply = v: if v != null then v else pkgs.writeText "cyrus.conf" cyrusConfig;
296     };
298     sslCACert = mkOption {
299       type = nullOr str;
300       default = null;
301       description = "File path which containing one or more CA certificates to use.";
302     };
304     sslServerCert = mkOption {
305       type = nullOr str;
306       default = null;
307       description = "File containing the global certificate used for all services (IMAP, POP3, LMTP, Sieve)";
308     };
310     sslServerKey = mkOption {
311       type = nullOr str;
312       default = null;
313       description = "File containing the private key belonging to the global server certificate.";
314     };
315   };
317   config = mkIf cfg.enable {
318     users.users.cyrus = optionalAttrs (cfg.user == null) {
319       description = "Cyrus IMAP user";
320       isSystemUser = true;
321       group = optionalString (cfg.group == null) "cyrus";
322     };
324     users.groups.cyrus = optionalAttrs (cfg.group == null) { };
326     environment.etc."imapd.conf".source = cfg.imapdConfigFile;
327     environment.etc."cyrus.conf".source = cfg.cyrusConfigFile;
329     systemd.services.cyrus-imap = {
330       description = "Cyrus IMAP server";
332       after = [ "network.target" ];
333       wantedBy = [ "multi-user.target" ];
334       restartTriggers = [
335         "/etc/imapd.conf"
336         "/etc/cyrus.conf"
337       ];
339       startLimitIntervalSec = 60;
340       environment = {
341         CYRUS_VERBOSE = mkIf cfg.debug "1";
342         LISTENQUEUE = builtins.toString cfg.listenQueue;
343       };
344       serviceConfig = {
345         User = if (cfg.user == null) then "cyrus" else cfg.user;
346         Group = if (cfg.group == null) then "cyrus" else cfg.group;
347         Type = "simple";
348         ExecStart = "${cyrus-imapdPkg}/libexec/master -l $LISTENQUEUE -C /etc/imapd.conf -M /etc/cyrus.conf -p /run/cyrus/master.pid -D";
349         Restart = "on-failure";
350         RestartSec = "1s";
351         RuntimeDirectory = "cyrus";
352         StateDirectory = "cyrus";
354         # Hardening
355         AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
356         PrivateTmp = true;
357         PrivateDevices = true;
358         ProtectSystem = "full";
359         CapabilityBoundingSet = [ "~CAP_NET_ADMIN CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_MODULE" ];
360         MemoryDenyWriteExecute = true;
361         ProtectKernelModules = true;
362         ProtectKernelTunables = true;
363         ProtectControlGroups = true;
364         RestrictAddressFamilies = [
365           "AF_INET"
366           "AF_INET6"
367           "AF_NETLINK"
368           "AF_UNIX"
369         ];
370         RestrictNamespaces = true;
371         RestrictRealtime = true;
372       };
373       preStart = ''
374         mkdir -p '${cfg.imapdSettings.configdirectory}/socket' '${cfg.tmpDBDir}' /run/cyrus/proc /run/cyrus/lock
375       '';
376     };
377     environment.systemPackages = [ cyrus-imapdPkg ];
378   };