]> Git — Sourcephile - sourcephile-nix.git/blob - install/logical/friot/dovecot.nix
dovecot: clean pop3 config.
[sourcephile-nix.git] / install / logical / friot / dovecot.nix
1 {pkgs, lib, config, system, ...}:
2 let inherit (builtins) toString toFile;
3 inherit (lib) types;
4 inherit (pkgs.lib) unlines;
5 inherit (config.services) dovecot2 postfix x509 openldap;
6 when = x: y: if x == null then "" else y;
7 extSep = postfix.recipientDelimiter;
8 dirSep = extSep;
9 libDir = "/var/lib/dovecot";
10 mailDir = "${libDir}/mail";
11 sieveDir = "${libDir}/sieve";
12 authDir = "${libDir}/auth";
13 authUser = dovecot2.mailUser; # TODO: php_roundcube
14 authGroup = dovecot2.mailGroup; # TODO: php_roundcube
15 escapeGroup = lib.stringAsChars (c: if "a"<=c && c<="z"
16 || "0"<=c && c<="9"
17 || c=="-"
18 then c else "_");
19 etc_dovecot = [
20 { target = "dovecot/${config.networking.domain}/dovecot-ldap.conf";
21 source = pkgs.writeText "dovecot-ldap.conf" ''
22 ${lib.optionalString dovecot2.debug ''
23 debug_level = 1
24 ''}
25
26 # LDAP database
27 uris = ldapi://
28 base = ou=posix,${openldap.domainSuffix}
29 scope = subtree
30 deref = never
31 blocking = no
32 # NOTE: sufficient for small systems and uses less resources.
33
34 # LDAP auth
35 sasl_bind = yes
36 sasl_mech = EXTERNAL
37 #dn = cn=admin,${openldap.domainSuffix}
38 #dnpass = useless with sasl_mech=EXTERNAL
39 auth_bind = no
40 #auth_bind_userdn = cn=%n,ou=accounts,ou=posix,dc=${openldap.domainSuffix}
41
42 # dovecot passdb query
43 pass_filter = (&(objectClass=posixAccount)(uid=%n))
44 pass_attrs = userPassword=password,\
45 uidNumber=userdb_uid,\
46 gidNumber=userdb_gid,\
47 mailGroupMember=mail_access_groups,\
48 =user=%n@%d
49 #homeDirectory=userdb_home
50 #quotaBytes=userdb_quota_rule=*:bytes=%{ldap:quotaBytes}
51 # TODO: userdb_quota_rule=*:storage=
52 # TODO: userdb_mail_access_groups
53 # DOC: http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
54 default_pass_scheme = CRYPT
55
56 # dovecot userdb query
57 user_filter = (&(objectClass=posixAccount)(uid=%n))
58 #user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(mailEnabled=TRUE))
59 # # DOC: http://wiki2.dovecot.org/Variables
60 #user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid
61 #user_attrs = mailHomeDirectory=home,\
62 # mailStorageDirectory=mail,\
63 # mailUidNumber=uid,\
64 # mailGidNumber=gid,\
65 # mailQuota=quota_rule=*:bytes=%$
66 # # DOC: http://wiki2.dovecot.org/UserDatabase/ExtraFields
67
68 # doveadm user query
69 iterate_attrs = =user=%{ldap:uid}@${config.networking.domain}
70 iterate_filter = (objectClass=posixAccount)
71 '';
72 }
73 ];
74 dovecot-virtual = pkgs.writeTextFile {
75 name = "dovecot-virtual";
76 destination = "/pop3/INBOX/dovecot-virtual";
77 text = ''
78 all
79 all+*
80 all
81 '';
82 };
83 in
84 {
85 imports = [
86 dovecot/autoconfig.nix
87 ];
88 config = {
89 systemd.services.dovecot2.after = [ "postfix.service" ];
90 systemd.services.dovecot2.restartTriggers =
91 map (f: f.source) etc_dovecot;
92 environment.etc = etc_dovecot;
93 #services.postfix.mapFiles."transport-dovecot" =
94 # toFile "transport-dovecot"
95 # (unlines
96 # (lib.mapAttrsToList
97 # (dom: {...}: "${transportSubDomain}.${dom} lmtp:unix:private/dovecot-lmtp")
98 # dovecot2.domains));
99 #users.extraUsers = [
100 # { name = "dovecot";
101 # uid = config.ids.uids.dovecot2;
102 # description = "Dovecot user";
103 # group = dovecot2.group;
104 # }
105 #];
106 users.extraGroups =
107 lib.mapAttrs
108 (domain: {...}:
109 { name = escapeGroup "${dovecot2.mailGroup}-${domain}";
110 })
111 dovecot2.domains;
112 systemd.services.dovecot2.preStart =
113 let sieveList =
114 pkgs.writeText "list.sieve" ''
115 require
116 [ "date"
117 , "fileinto"
118 , "mailbox"
119 , "variables"
120 ];
121
122 if currentdate :matches "year" "*" { set "year" "''${1}"; }
123 if currentdate :matches "month" "*" { set "month" "''${1}"; }
124
125 if exists "List-ID" {
126 if header :matches "List-ID" "*<*.*.*.*>*" {
127 set "list" "''${2}";
128 set "domain" "''${4}";
129 }
130 elsif header :matches "List-ID" "*<*.*.*>*" {
131 set "list" "''${2}";
132 set "domain" "''${3}";
133 }
134 fileinto :create "Listes+''${domain}+''${list}+''${year}+''${month}";
135 stop;
136 }
137 '';
138 sieveSpam =
139 pkgs.writeText "spam.sieve" ''
140 require
141 [ "imap4flags"
142 ];
143
144 if header :contains "X-Spam-Level" "***" {
145 addflag "Junk";
146 }
147 '';
148 sieveExtension =
149 pkgs.writeText "extension.sieve" ''
150 require
151 [ "envelope"
152 , "fileinto"
153 , "mailbox"
154 , "subaddress"
155 , "variables"
156 ];
157 if envelope :matches :detail "TO" "*" {
158 set "extension" "''${1}";
159 }
160 if not string :is "''${extension}" "" {
161 fileinto :create "Plus+''${extension}";
162 stop;
163 }
164 '';
165 installSieve = ''
166 # SEE: http://wiki2.dovecot.org/SharedMailboxes/Permissions
167 # NOTE: the sticky bit is to allow the acl.db{.lock,} done by dovecot
168 install -D -d -m 2771 \
169 -o ${dovecot2.mailUser} \
170 -g ${dovecot2.mailGroup} \
171 ${mailDir}
172
173 # Install global sieves
174 install -D -d -m 0755 \
175 -o root \
176 -g root \
177 ${sieveDir} \
178 ${sieveDir}/after.d \
179 ${sieveDir}/before.d \
180 ${sieveDir}/global.d
181 ln -fns ${sieveList} ${sieveDir}/global.d/list.sieve
182 ln -fns ${sieveExtension} ${sieveDir}/global.d/extension.sieve
183 ln -fns ${sieveSpam} ${sieveDir}/global.d/spam.sieve
184 for f in ${sieveDir}/*/*.sieve
185 do ${pkgs.dovecot_pigeonhole}/bin/sievec $f
186 done
187 '';
188 installDomains = ''
189 # Install domains
190 new_uid=5000
191 ''
192 + unlines (lib.mapAttrsToList (domain: {accounts, ...}:
193 let domainGroup = escapeGroup "${dovecot2.mailGroup}-${domain}"; in
194 ''
195 install -D -d -m 1770 \
196 -o ${dovecot2.mailUser} \
197 -g ${domainGroup} \
198 ${mailDir}/${domain} \
199 ${libDir}/control/${domain} \
200 ${libDir}/index/${domain}
201 install -D -d -m 1770 \
202 -o ${dovecot2.mailUser} \
203 -g ${authGroup} \
204 ${libDir}/auth \
205 ${libDir}/auth/${domain}
206 '') dovecot2.domains);
207 in
208 unlines [
209 installSieve
210 installDomains
211 ];
212 services.dovecot2 = {
213 enable = true;
214 debug = true;
215 mailUser = "dovemail";
216 mailGroup = "dovemail";
217 modules = [
218 #pkgs.dovecot_antispam
219 pkgs.dovecot_pigeonhole
220 ];
221 configFile = toString (pkgs.writeText "dovecot.conf" ''
222 passdb {
223 driver = ldap
224 args = /etc/dovecot/${config.networking.domain}/dovecot-ldap.conf
225 default_fields =
226 override_fields =
227 }
228 userdb {
229 driver = prefetch
230 }
231 userdb {
232 # NOTE: this userdb is only used by lda.
233 driver = ldap
234 args = /etc/dovecot/${config.networking.domain}/dovecot-ldap.conf
235 default_fields =
236 override_fields =
237 skip = found
238 }
239 #passdb {
240 # driver = passwd-file
241 # args = scheme=crypt username_format=%n ${authDir}/%d/passwd
242 #}
243 #userdb {
244 # # NOTE: this userdb is only used by lda.
245 # driver = passwd-file
246 # args = username_format=%n ${authDir}/%d/passwd
247 # #default_fields = home=${mailDir}/%d/%n
248 # skip = found
249 #}
250 mail_home = ${mailDir}/%d/%n
251 # NOTE: if needed, may be overrided by userdb_mail
252 mail_location = maildir:${mailDir}/%d/%n/Maildir:LAYOUT=fs:INDEX=${libDir}/index/%d/%n:CONTROL=${libDir}/control/%d/%n
253 # NOTE: if needed, may be overrided by userdb_mail
254 # NOTE: INDEX and CONTROL are on a partition without quota, as explain in the doc.
255 # SEE: http://wiki2.dovecot.org/Quota/FS
256 auth_mechanisms = plain login
257 auth_ssl_require_client_cert = no
258 # NOTE: postfix does not supply a client cert.
259 auth_ssl_username_from_cert = yes
260 auth_verbose = yes
261 auth_username_format = %Lu
262 # NOTE: lowercase the username, help with LDAP?
263 ${lib.optionalString dovecot2.debug ''
264 auth_debug = yes
265 mail_debug = yes
266 verbose_ssl = yes
267 ''}
268 default_internal_user = ${dovecot2.user}
269 default_internal_group = ${dovecot2.group}
270 disable_plaintext_auth = yes
271 first_valid_uid = 1000
272 lda_mailbox_autocreate = yes
273 lda_mailbox_autosubscribe = yes
274 listen = *
275 log_timestamp = "%Y-%m-%d %H:%M:%S "
276 #maildir_copy_with_hardlinks = yes
277 namespace inbox {
278 # NOTE: here because protocol sieve {namespace inbox{}} does not seem to work.
279 inbox = yes
280 location =
281 list = yes
282 prefix =
283 separator = ${dirSep}
284 }
285 namespace {
286 #list = children
287 list = yes
288 location = maildir:${mailDir}/%d/%n/Maildir:LAYOUT=fs:INDEX=${libDir}/index/%d/%n/Shared/%n:CONTROL=${libDir}/control/%d/%n/Shared/%n
289 # FIXME: %d not working
290 prefix = Partages+%%n+
291 separator = ${dirSep}
292 subscriptions = yes
293 type = shared
294 }
295 mail_plugins = $mail_plugins acl quota virtual
296 #mail_uid = ${dovecot2.mailUser}
297 #mail_gid = ${dovecot2.mailGroup}
298 # NOTE: each user has a dedicated (uid,gid) pair
299 #mail_privileged_group = mail
300 #mail_access_groups =
301 plugin {
302 acl = vfile:/etc/dovecot/acl/global.d
303 acl_anyone = allow
304 acl_shared_dict = file:${mailDir}/%d/acl.db
305 # NOTE: to let users LIST mailboxes shared by other users,
306 # Dovecot needs a shared mailbox dictionary.
307 # FIXME: %d not working with userdb ldap
308 ##antispam_allow_append_to_spam = yes
309 # # NOTE: pour offlineimap
310 #antispam_backend = pipe
311 ##antispam_crm_args = -u;${mailDir}/%d/.crm114;/usr/share/crm114/mailfilter.crm
312 #antispam_crm_args = -u;${mailDir}/crm114;/usr/share/crm114/mailfilter.crm
313 #antispam_crm_binary = /usr/bin/crm
314 #antispam_debug_target = syslog
315 ##antispam_crm_env = HOME=%h;USER=%u
316 #antispam_ham_keywords = NonJunk
317 #antispam_pipe_program = /usr/bin/crm
318 #antispam_pipe_program_args = -u;${mailDir}/crm114;/usr/share/crm114/mailfilter.crm;--stats_only;--force
319 #antispam_pipe_program_notspam_arg = --learnnonspam
320 #antispam_pipe_program_spam_arg = --learnspam
321 #antispam_pipe_program_unlearn_spam_args = --unlearn;--learnspam
322 #antispam_pipe_program_unlearn_notspam_args = --unlearn;--learnnonspam
323 #antispam_pipe_tmpdir = ${mailDir}/crm114/tmp
324 #antispam_signature = X-CRM114-CacheID
325 #antispam_signature_missing = move
326 #antispam_spam = Junk
327 #antispam_spam_keywords = Junk
328 #antispam_trash = Trash
329 #antispam_unsure = Unsure
330 #antispam_verbose_debug = 0
331 quota = maildir:User quota
332 quota_rule = *:storage=256M
333 quota_rule2 = Trash:storage=+64M
334 quota_max_mail_size = 20M
335 #quota_exceeded_message = </path/to/quota_exceeded_message.txt
336 quota_warning = storage=95%% quota-warning 95 %u
337 quota_warning2 = storage=80%% quota-warning 80 %u
338 quota_warning3 = -storage=100%% quota-warning below %u
339 # NOTE: user is no longer over quota
340 recipient_delimiter = ${extSep}
341 sieve = file:${mailDir}/%d/%n/sieve;active=${mailDir}/%d/%n/sieve/main.sieve
342 #sieve_default = file:${mailDir}/%u/default.sieve
343 #sieve_default_name = default
344 sieve_after = ${sieveDir}/after.d/
345 sieve_before = ${sieveDir}/before.d/
346 sieve_dir = ${mailDir}/%d/%n/sieve/
347 #sieve_extensions = +spamtest +spamtestplus
348 sieve_global_dir = ${sieveDir}/global.d/
349 sieve_max_script_size = 1M
350 sieve_quota_max_scripts = 0
351 sieve_quota_max_storage = 10M
352 sieve_spamtest_max_value = 10
353 sieve_spamtest_status_header = X-Spam-Score
354 sieve_spamtest_status_type = strlen
355 sieve_user_log = /var/log/dovecot/%d/sieve.%n.log
356 }
357 service quota-warning {
358 executable = script ${
359 pkgs.writeScript "quota-warning" ''
360 #!/bin/sh -eu
361 PERCENT=$1
362 USER=$2
363 cat << EOF | ${pkgs.dovecot}/libexec/dovecot/dovecot-lda -d $USER -o
364 "plugin/quota=maildir:User quota:noenforcing"
365 From: postmaster@${config.networking.domain}
366 Subject: [WARNING] your mailbox is now $PERCENT% full.
367
368 Please remove some mails to make room for new ones.
369 EOF
370 ''
371 }
372 # use some unprivileged user for executing the quota warnings
373 user = ${dovecot2.user}
374 unix_listener quota-warning {
375 }
376 }
377 protocol imap {
378 #mail_max_userip_connections = 10
379 mail_plugins = $mail_plugins imap_acl imap_quota # antispam
380 namespace inbox {
381 inbox = yes
382 location =
383 list = yes
384 mailbox Drafts {
385 special_use = \Drafts
386 }
387 mailbox Junk {
388 special_use = \Junk
389 }
390 mailbox Sent {
391 special_use = \Sent
392 }
393 mailbox "Sent Messages" {
394 special_use = \Sent
395 }
396 mailbox Trash {
397 special_use = \Trash
398 }
399 prefix =
400 separator = ${dirSep}
401 }
402 }
403 protocol lda {
404 auth_socket_path = /var/run/dovecot/auth-userdb
405 hostname = ${config.networking.domain}
406 info_log_path =
407 log_path =
408 mail_plugins = $mail_plugins sieve
409 namespace inbox {
410 inbox = yes
411 location =
412 list = yes
413 prefix =
414 separator = ${dirSep}
415 }
416 postmaster_address = postmaster${extSep}dovecot${extSep}lda@${config.networking.domain}
417 syslog_facility = mail
418 }
419 protocol lmtp {
420 #info_log_path = /tmp/dovecot-lmtp.log
421 mail_plugins = $mail_plugins sieve
422 namespace inbox {
423 inbox = yes
424 location =
425 list = yes
426 prefix =
427 separator = ${dirSep}
428 }
429 postmaster_address = postmaster${extSep}dovecot${extSep}lmtp@${config.networking.domain}
430 }
431 protocol pop3 {
432 #mail_max_userip_connections = 10
433 namespace all {
434 # NOTE: used by ${dovecot-virtual}/pop3/INBOX/dovecot-virtual
435 hidden = yes
436 list = no
437 location =
438 prefix = all+
439 separator = ${dirSep}
440 }
441 # Virtual namespace for the virtual INBOX.
442 # Use a global directory for dovecot-virtual files.
443 namespace inbox {
444 inbox = yes
445 hidden = yes
446 list = no
447 location = virtual:${dovecot-virtual}/pop3:INDEX=${libDir}/index/%d/%n/POP3:LAYOUT=fs
448 prefix = pop3+
449 separator = ${dirSep}
450 }
451 pop3_client_workarounds =
452 pop3_fast_size_lookups = yes
453 pop3_lock_session = yes
454 pop3_no_flag_updates = yes
455 # Use GUIDs to avoid accidental POP3 UIDL changes instead of IMAP UIDs.
456 pop3_uidl_format = %g
457 }
458 protocol sieve {
459 #mail_max_userip_connections = 10
460 #managesieve_implementation_string = Dovecot Pigeonhole
461 managesieve_max_compile_errors = 5
462 #managesieve_max_line_length = 65536
463 #managesieve_notify_capability = mailto
464 #managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date ihave
465 }
466 protocols = imap lmtp pop3 sieve
467 service lmtp {
468 #executable = lmtp -L
469 process_min_avail = 2
470 unix_listener /var/lib/postfix/queue/private/dovecot-lmtp {
471 user = ${postfix.user}
472 group = ${postfix.group}
473 mode = 0600
474 }
475 #user = mail
476 }
477 service auth {
478 user = root
479 # FIXME: may be user=dovecot-auth with LDAP?
480 unix_listener auth-userdb {
481 user = ${dovecot2.user}
482 group = ${dovecot2.group}
483 mode = 0660
484 }
485 unix_listener /var/lib/postfix/queue/private/auth {
486 user = ${postfix.user}
487 group = ${postfix.group}
488 mode = 0660
489 }
490 }
491 service imap {
492 # Most of the memory goes to mmap()ing files.
493 # You may need to increase this limit if you have huge mailboxes.
494 #vsz_limit =
495 process_limit = 1024
496 }
497 service imap-login {
498 #inet_listener imap {
499 # address = 127.0.0.1
500 # port = 143
501 # ssl = no
502 #}
503 inet_listener imaps {
504 port = 993
505 ssl = yes
506 }
507 }
508 service pop3 {
509 process_limit = 1024
510 }
511 service pop3-login {
512 inet_listener pop3s {
513 port = 995
514 ssl = yes
515 }
516 }
517 ssl = required
518 ssl_dh = <${x509.dir}/dh.pem
519 ssl_cipher_list = HIGH:!LOW:!SSLv2:!EXP:!aNULL
520 ssl_cert = <${x509.cert}
521 ssl_key = <${x509.key}
522 #ssl_ca = <''${caPath}
523 #ssl_verify_client_cert = yes
524 '');
525 #${lib.concatMapStringsSep "\n"
526 # (dom: ''
527 # local_name mail.${dom} {
528 # #ssl_ca = <''${caPath}
529 # ssl_cert = <${x509.cert dom}
530 # ssl_key = <${x509.key dom}
531 # }
532 # '')
533 # dovecot2.domains
534 #}
535 };
536 };
537 }