Skip to content

Commit 88d3f02

Browse files
authored
cyrus-imapd: init at 3.8.2 (#305538)
2 parents ae25233 + cc1aeb4 commit 88d3f02

File tree

6 files changed

+707
-0
lines changed

6 files changed

+707
-0
lines changed

nixos/doc/manual/release-notes/rl-2411.section.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373

7474
## New Modules {#sec-release-24.11-new-modules}
7575

76+
- [Cyrus IMAP](https://github.com/cyrusimap/cyrus-imapd), an email, contacts and calendar server. Available as [services.cyrus-imap](#opt-services.cyrus-imap.enable) service.
77+
7678
- [TaskChampion Sync-Server](https://github.com/GothenburgBitFactory/taskchampion-sync-server), a [Taskwarrior 3](https://taskwarrior.org/docs/upgrade-3/) sync server, replacing Taskwarrior 2's sync server named [`taskserver`](https://github.com/GothenburgBitFactory/taskserver).
7779

7880
- [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr), proxy server to bypass Cloudflare protection. Available as [services.flaresolverr](#opt-services.flaresolverr.enable) service.

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@
661661
./services/logging/ulogd.nix
662662
./services/mail/automx2.nix
663663
./services/mail/clamsmtp.nix
664+
./services/mail/cyrus-imap.nix
664665
./services/mail/davmail.nix
665666
./services/mail/dkimproxy-out.nix
666667
./services/mail/dovecot.nix
Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
{
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+
;
31+
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);
50+
51+
cyrusConfig = lib.concatStringsSep "\n" (
52+
lib.mapAttrsToList (n: v: ''
53+
${n} {
54+
${mkCyrusConfig v}
55+
}
56+
'') cfg.cyrusSettings
57+
);
58+
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;
73+
in
74+
{
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";
78+
79+
listenQueue = mkOption {
80+
type = int;
81+
default = 32;
82+
description = ''
83+
Socket listen queue backlog size. See 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. master(8) will ensure that these processes are running, restarting any process which dies or forks. All listed processes will be shutdown when 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 deliver(8). This should match the path specified in 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+
};
271+
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+
};
277+
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+
};
283+
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+
};
290+
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+
};
297+
298+
sslCACert = mkOption {
299+
type = nullOr str;
300+
default = null;
301+
description = "File path which containing one or more CA certificates to use.";
302+
};
303+
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+
};
309+
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+
};
316+
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+
};
323+
324+
users.groups.cyrus = optionalAttrs (cfg.group == null) { };
325+
326+
environment.etc."imapd.conf".source = cfg.imapdConfigFile;
327+
environment.etc."cyrus.conf".source = cfg.cyrusConfigFile;
328+
329+
systemd.services.cyrus-imap = {
330+
description = "Cyrus IMAP server";
331+
332+
after = [ "network.target" ];
333+
wantedBy = [ "multi-user.target" ];
334+
restartTriggers = [
335+
"/etc/imapd.conf"
336+
"/etc/cyrus.conf"
337+
];
338+
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";
353+
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+
};
379+
}

0 commit comments

Comments
 (0)