projects/guix·blob javascript-team · refs

2508 lines · 89.8 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015 Andy Wingo <[email protected]>
3;;; Copyright © 2017, 2018 Clément Lassieur <[email protected]>
4;;; Copyright © 2017 Carlo Zancanaro <[email protected]>
5;;; Copyright © 2017, 2020, 2024 Tobias Geerinckx-Rice <[email protected]>
6;;; Copyright © 2019 Kristofer Buffington <[email protected]>
7;;; Copyright © 2020 Jonathan Brielmaier <[email protected]>
8;;; Copyright © 2023 Thomas Ieong <[email protected]>
9;;; Copyright © 2023 Saku Laesvuori <[email protected]>
10;;; Copyright © 2024 Juliana Sims <[email protected]>
11;;; Copyright © 2025 Felix Lechner <[email protected]>
12;;;
13;;; This file is part of GNU Guix.
14;;;
15;;; GNU Guix is free software; you can redistribute it and/or modify it
16;;; under the terms of the GNU General Public License as published by
17;;; the Free Software Foundation; either version 3 of the License, or (at
18;;; your option) any later version.
19;;;
20;;; GNU Guix is distributed in the hope that it will be useful, but
21;;; WITHOUT ANY WARRANTY; without even the implied warranty of
22;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23;;; GNU General Public License for more details.
24;;;
25;;; You should have received a copy of the GNU General Public License
26;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
27;;;
28;;; Some of the help text was taken from the default dovecot.conf files.
29
30;;; Commentary:
31;;;
32;;; This module provides service definitions for the Dovecot POP3 and IMAP
33;;; mail server.
34;;;
35;;; Code:
36
37(define-module (gnu services mail)
38 #:use-module (gnu services)
39 #:use-module (gnu services base)
40 #:use-module (gnu services configuration)
41 #:use-module (gnu services shepherd)
42 #:use-module (gnu system pam)
43 #:use-module (gnu system privilege)
44 #:use-module (gnu system shadow)
45 #:use-module (gnu packages mail)
46 #:use-module (gnu packages admin)
47 #:use-module (gnu packages dav)
48 #:use-module (gnu packages tls)
49 #:use-module (guix deprecation)
50 #:use-module ((guix diagnostics) #:select (source-properties->location))
51 #:use-module (guix modules)
52 #:use-module (guix records)
53 #:use-module (guix packages)
54 #:use-module (guix gexp)
55 #:use-module (ice-9 curried-definitions)
56 #:use-module (ice-9 match)
57 #:use-module (ice-9 format)
58 #:use-module (srfi srfi-1)
59 #:export (dovecot-service ; deprecated
60 dovecot-service-type
61 dovecot-configuration
62 opaque-dovecot-configuration
63
64 dict-configuration
65 passdb-configuration
66 userdb-configuration
67 unix-listener-configuration
68 fifo-listener-configuration
69 inet-listener-configuration
70 service-configuration
71 protocol-configuration
72 plugin-configuration
73 mailbox-configuration
74 namespace-configuration
75
76 opensmtpd-configuration
77 opensmtpd-configuration?
78 opensmtpd-service-type
79 %default-opensmtpd-config-file
80
81 mail-aliases-service-type
82
83 exim-configuration
84 exim-configuration?
85 exim-service-type
86 %default-exim-config-file
87
88 imap4d-configuration
89 imap4d-configuration?
90 imap4d-service-type
91 %default-imap4d-config-file
92
93 radicale-auth-configuration
94 radicale-auth-configuration?
95 radicale-encoding-configuration
96 radicale-encoding-configuration?
97 radicale-logging-configuration
98 radicale-logging-configuration?
99 radicale-rights-configuration
100 radicale-rights-configuration?
101 radicale-server-configuration
102 radicale-server-configuration?
103 radicale-storage-configuration
104 radicale-storage-configuration?
105 radicale-configuration
106 radicale-configuration?
107 radicale-service-type
108
109 rspamd-configuration
110 rspamd-service-type
111 %default-rspamd-account
112 %default-rspamd-config-file
113 %default-rspamd-group))
114
115(define (uglify-field-name field-name)
116 (let ((str (symbol->string field-name)))
117 (string-join (string-split (if (string-suffix? "?" str)
118 (substring str 0 (1- (string-length str)))
119 str)
120 #\-)
121 "_")))
122
123(define (serialize-field field-name val)
124 (format #t "~a=~a\n" (uglify-field-name field-name) val))
125
126(define (serialize-string field-name val)
127 (serialize-field field-name val))
128
129(define (space-separated-string-list? val)
130 (and (list? val)
131 (and-map (lambda (x)
132 (and (string? x) (not (string-index x #\space))))
133 val)))
134(define (serialize-space-separated-string-list field-name val)
135 (match val
136 (() #f)
137 (_ (serialize-field field-name (string-join val " ")))))
138
139(define (comma-separated-string-list? val)
140 (and (list? val)
141 (and-map (lambda (x)
142 (and (string? x) (not (string-index x #\,))))
143 val)))
144(define (serialize-comma-separated-string-list field-name val)
145 (serialize-field field-name (string-join val ",")))
146
147(define (file-name? val)
148 (and (string? val)
149 (string-prefix? "/" val)))
150(define (serialize-file-name field-name val)
151 (serialize-string field-name val))
152
153(define (colon-separated-file-name-list? val)
154 (and (list? val)
155 ;; Trailing slashes not needed and not
156 (and-map file-name? val)))
157(define (serialize-colon-separated-file-name-list field-name val)
158 (serialize-field field-name (string-join val ":")))
159
160(define (serialize-boolean field-name val)
161 (serialize-string field-name (if val "yes" "no")))
162
163(define (non-negative-integer? val)
164 (and (exact-integer? val) (not (negative? val))))
165(define (serialize-non-negative-integer field-name val)
166 (serialize-field field-name val))
167
168(define (hours? val) (non-negative-integer? val))
169(define (serialize-hours field-name val)
170 (serialize-field field-name (format #f "~a hours" val)))
171
172(define (free-form-fields? val)
173 (match val
174 (() #t)
175 ((((? symbol?) . (? string?)) . val) (free-form-fields? val))
176 (_ #f)))
177(define (serialize-free-form-fields field-name val)
178 (for-each (match-lambda ((k . v) (serialize-field k v))) val))
179
180(define (free-form-args? val)
181 (match val
182 (() #t)
183 ((((? symbol?) . (? string?)) . val) (free-form-args? val))
184 (_ #f)))
185(define (serialize-free-form-args field-name val)
186 (serialize-field field-name
187 (string-join
188 (map (match-lambda ((k . v) (format #f "~a=~a" k v))) val)
189 " ")))
190
191(define-configuration dict-configuration
192 (entries
193 (free-form-fields '())
194 "A list of key-value pairs that this dict should hold."))
195
196(define (serialize-dict-configuration field-name val)
197 (format #t "dict {\n")
198 (serialize-configuration val dict-configuration-fields)
199 (format #t "}\n"))
200
201(define-configuration passdb-configuration
202 (driver
203 (string "pam")
204 "The driver that the passdb should use. Valid values include
205@samp{pam}, @samp{passwd}, @samp{shadow}, @samp{bsdauth}, and
206@samp{static}.")
207 (args
208 (space-separated-string-list '())
209 "Space separated list of arguments to the passdb driver."))
210
211(define (serialize-passdb-configuration field-name val)
212 (format #t "passdb {\n")
213 (serialize-configuration val passdb-configuration-fields)
214 (format #t "}\n"))
215(define (passdb-configuration-list? val)
216 (and (list? val) (and-map passdb-configuration? val)))
217(define (serialize-passdb-configuration-list field-name val)
218 (for-each (lambda (val) (serialize-passdb-configuration field-name val)) val))
219
220(define-configuration userdb-configuration
221 (driver
222 (string "passwd")
223 "The driver that the userdb should use. Valid values include
224@samp{passwd} and @samp{static}.")
225 (args
226 (space-separated-string-list '())
227 "Space separated list of arguments to the userdb driver.")
228 (override-fields
229 (free-form-args '())
230 "Override fields from passwd."))
231
232(define (serialize-userdb-configuration field-name val)
233 (format #t "userdb {\n")
234 (serialize-configuration val userdb-configuration-fields)
235 (format #t "}\n"))
236(define (userdb-configuration-list? val)
237 (and (list? val) (and-map userdb-configuration? val)))
238(define (serialize-userdb-configuration-list field-name val)
239 (for-each (lambda (val) (serialize-userdb-configuration field-name val)) val))
240
241(define-configuration unix-listener-configuration
242 (path
243 (string (configuration-missing-field 'unix-listener 'path))
244 "Path to the file, relative to @code{base-dir} field. This is also used as
245the section name.")
246 (mode
247 (string "0600")
248 "The access mode for the socket.")
249 (user
250 (string "")
251 "The user to own the the socket.")
252 (group
253 (string "")
254 "The group to own the socket."))
255
256(define (serialize-unix-listener-configuration field-name val)
257 (format #t "unix_listener ~a {\n" (unix-listener-configuration-path val))
258 (serialize-configuration val (cdr unix-listener-configuration-fields))
259 (format #t "}\n"))
260
261(define-configuration fifo-listener-configuration
262 (path
263 (string (configuration-missing-field 'fifo-listener 'path))
264 "Path to the file, relative to @code{base-dir} field. This is also used as
265the section name.")
266 (mode
267 (string "0600")
268 "The access mode for the socket.")
269 (user
270 (string "")
271 "The user to own the the socket.")
272 (group
273 (string "")
274 "The group to own the socket."))
275
276(define (serialize-fifo-listener-configuration field-name val)
277 (format #t "fifo_listener ~a {\n" (fifo-listener-configuration-path val))
278 (serialize-configuration val (cdr fifo-listener-configuration-fields))
279 (format #t "}\n"))
280
281(define-configuration inet-listener-configuration
282 (protocol
283 (string (configuration-missing-field 'inet-listener 'protocol))
284 "The protocol to listen for.")
285 (address
286 (string "")
287 "The address on which to listen, or empty for all addresses.")
288 (port
289 (non-negative-integer
290 (configuration-missing-field 'inet-listener 'port))
291 "The port on which to listen.")
292 (ssl?
293 (boolean #t)
294 "Whether to use SSL for this service; @samp{yes}, @samp{no}, or
295@samp{required}."))
296
297(define (serialize-inet-listener-configuration field-name val)
298 (format #t "inet_listener ~a {\n" (inet-listener-configuration-protocol val))
299 (serialize-configuration val (cdr inet-listener-configuration-fields))
300 (format #t "}\n"))
301
302(define (listener-configuration? val)
303 (or (unix-listener-configuration? val)
304 (fifo-listener-configuration? val)
305 (inet-listener-configuration? val)))
306(define (serialize-listener-configuration field-name val)
307 (cond
308 ((unix-listener-configuration? val)
309 (serialize-unix-listener-configuration field-name val))
310 ((fifo-listener-configuration? val)
311 (serialize-fifo-listener-configuration field-name val))
312 ((inet-listener-configuration? val)
313 (serialize-inet-listener-configuration field-name val))
314 (else (configuration-field-error #f field-name val))))
315(define (listener-configuration-list? val)
316 (and (list? val) (and-map listener-configuration? val)))
317(define (serialize-listener-configuration-list field-name val)
318 (for-each (lambda (val)
319 (serialize-listener-configuration field-name val))
320 val))
321
322(define-configuration service-configuration
323 (kind
324 (string (configuration-missing-field 'service 'kind))
325 "The service kind. Valid values include @code{director},
326@code{imap-login}, @code{pop3-login}, @code{lmtp}, @code{imap},
327@code{pop3}, @code{auth}, @code{auth-worker}, @code{dict},
328@code{tcpwrap}, @code{quota-warning}, or anything else.")
329 (listeners
330 (listener-configuration-list '())
331 "Listeners for the service. A listener is either an
332@code{unix-listener-configuration}, a @code{fifo-listener-configuration}, or
333an @code{inet-listener-configuration}.")
334 (client-limit
335 (non-negative-integer 0)
336 "Maximum number of simultaneous client connections per process. Once this
337number of connections is received, the next incoming connection will prompt
338Dovecot to spawn another process. If set to 0, @code{default-client-limit} is
339used instead.")
340 (service-count
341 (non-negative-integer 1)
342 "Number of connections to handle before starting a new process.
343Typically the only useful values are 0 (unlimited) or 1. 1 is more
344secure, but 0 is faster. <doc/wiki/LoginProcess.txt>.")
345 (process-limit
346 (non-negative-integer 0)
347 "Maximum number of processes that can exist for this service. If set to 0,
348@code{default-process-limit} is used instead.")
349 (process-min-avail
350 (non-negative-integer 0)
351 "Number of processes to always keep waiting for more connections.")
352 ;; FIXME: Need to be able to take the default for this value from other
353 ;; parts of the config.
354 (vsz-limit
355 (non-negative-integer #e256e6)
356 "If you set @samp{service-count 0}, you probably need to grow
357this."))
358
359(define (serialize-service-configuration field-name val)
360 (format #t "service ~a {\n" (service-configuration-kind val))
361 (serialize-configuration val (cdr service-configuration-fields))
362 (format #t "}\n"))
363(define (service-configuration-list? val)
364 (and (list? val) (and-map service-configuration? val)))
365(define (serialize-service-configuration-list field-name val)
366 (for-each (lambda (val)
367 (serialize-service-configuration field-name val))
368 val))
369
370(define-configuration protocol-configuration
371 (name
372 (string (configuration-missing-field 'protocol 'name))
373 "The name of the protocol.")
374 (auth-socket-path
375 (string "/var/run/dovecot/auth-userdb")
376 "UNIX socket path to master authentication server to find users.
377This is used by imap (for shared users) and lda.")
378 (mail-plugins
379 (space-separated-string-list '("$mail_plugins"))
380 "Space separated list of plugins to load.")
381 (mail-max-userip-connections
382 (non-negative-integer 10)
383 "Maximum number of IMAP connections allowed for a user from each IP
384address. NOTE: The username is compared case-sensitively.")
385 (imap-metadata?
386 (boolean #f)
387 "Whether to enable the @code{IMAP METADATA} extension as defined in
388@uref{https://tools.ietf.org/html/rfc5464, RFC@tie{}5464}, which provides
389a means for clients to set and retrieve per-mailbox, per-user metadata
390and annotations over IMAP.
391
392If this is @samp{#t}, you must also specify a dictionary @i{via} the
393@code{mail-attribute-dict} setting.")
394 (managesieve-notify-capability
395 (space-separated-string-list '())
396 "Which NOTIFY capabilities to report to clients that first connect to
397the ManageSieve service, before authentication. These may differ from the
398capabilities offered to authenticated users. If this field is left empty,
399report what the Sieve interpreter supports by default.")
400 (managesieve-sieve-capability
401 (space-separated-string-list '())
402 "Which SIEVE capabilities to report to clients that first connect to
403the ManageSieve service, before authentication. These may differ from the
404capabilities offered to authenticated users. If this field is left empty,
405report what the Sieve interpreter supports by default."))
406
407(define (serialize-protocol-configuration field-name val)
408 (format #t "protocol ~a {\n" (protocol-configuration-name val))
409 (serialize-configuration val (cdr protocol-configuration-fields))
410 (format #t "}\n"))
411(define (protocol-configuration-list? val)
412 (and (list? val) (and-map protocol-configuration? val)))
413(define (serialize-protocol-configuration-list field-name val)
414 (serialize-field 'protocols
415 (string-join (map protocol-configuration-name val) " "))
416 (for-each (lambda (val)
417 (serialize-protocol-configuration field-name val))
418 val))
419
420(define-configuration plugin-configuration
421 (entries
422 (free-form-fields '())
423 "A list of key-value pairs that this dict should hold."))
424
425(define (serialize-plugin-configuration field-name val)
426 (format #t "plugin {\n")
427 (serialize-configuration val plugin-configuration-fields)
428 (format #t "}\n"))
429
430(define-configuration mailbox-configuration
431 (name
432 (string (error "mailbox name is required"))
433 "Name for this mailbox.")
434
435 (auto
436 (string "no")
437 "@samp{create} will automatically create this mailbox.
438@samp{subscribe} will both create and subscribe to the mailbox.")
439
440 (special-use
441 (space-separated-string-list '())
442 "List of IMAP @code{SPECIAL-USE} attributes as specified by RFC 6154.
443Valid values are @code{\\All}, @code{\\Archive}, @code{\\Drafts},
444@code{\\Flagged}, @code{\\Junk}, @code{\\Sent}, and @code{\\Trash}."))
445
446(define (serialize-mailbox-configuration field-name val)
447 (format #t "mailbox \"~a\" {\n" (mailbox-configuration-name val))
448 (serialize-configuration val (cdr mailbox-configuration-fields))
449 (format #t "}\n"))
450(define (mailbox-configuration-list? val)
451 (and (list? val) (and-map mailbox-configuration? val)))
452(define (serialize-mailbox-configuration-list field-name val)
453 (for-each (lambda (val)
454 (serialize-mailbox-configuration field-name val))
455 val))
456
457(define-configuration namespace-configuration
458 (name
459 (string (error "namespace name is required"))
460 "Name for this namespace.")
461
462 (type
463 (string "private")
464 "Namespace type: @samp{private}, @samp{shared} or @samp{public}.")
465
466 (separator
467 (string "")
468 "Hierarchy separator to use. You should use the same separator for
469all namespaces or some clients get confused. @samp{/} is usually a good
470one. The default however depends on the underlying mail storage
471format.")
472
473 (prefix
474 (string "")
475 "Prefix required to access this namespace. This needs to be
476different for all namespaces. For example @samp{Public/}.")
477
478 (location
479 (string "")
480 "Physical location of the mailbox. This is in same format as
481mail_location, which is also the default for it.")
482
483 (inbox?
484 (boolean #f)
485 "There can be only one INBOX, and this setting defines which
486namespace has it.")
487
488 (hidden?
489 (boolean #f)
490 "If namespace is hidden, it's not advertised to clients via NAMESPACE
491extension. You'll most likely also want to set @samp{list? #f}. This is mostly
492useful when converting from another server with different namespaces
493which you want to deprecate but still keep working. For example you can
494create hidden namespaces with prefixes @samp{~/mail/}, @samp{~%u/mail/}
495and @samp{mail/}.")
496
497 (list?
498 (boolean #t)
499 "Show the mailboxes under this namespace with LIST command. This
500makes the namespace visible for clients that don't support NAMESPACE
501extension. The special @code{children} value lists child mailboxes, but
502hides the namespace prefix.")
503
504 (subscriptions?
505 (boolean #t)
506 "Namespace handles its own subscriptions. If set to @code{#f}, the
507parent namespace handles them. The empty prefix should always have this
508as @code{#t}.)")
509
510 (mailboxes
511 (mailbox-configuration-list '())
512 "List of predefined mailboxes in this namespace."))
513
514(define (serialize-namespace-configuration field-name val)
515 (format #t "namespace ~a {\n" (namespace-configuration-name val))
516 (serialize-configuration val (cdr namespace-configuration-fields))
517 (format #t "}\n"))
518(define (list-of-namespace-configuration? val)
519 (and (list? val) (and-map namespace-configuration? val)))
520(define (serialize-list-of-namespace-configuration field-name val)
521 (for-each (lambda (val)
522 (serialize-namespace-configuration field-name val))
523 val))
524
525(define (package-list? val)
526 (and (list? val) (and-map package? val)))
527(define (serialize-package-list field-name val)
528 #f)
529
530(define-configuration dovecot-configuration
531 (dovecot
532 (file-like dovecot)
533 "The dovecot package.")
534
535 (extensions
536 (package-list '())
537 "Plugins and extensions to the Dovecot package. Specify a list of dovecot
538plugins that needs to be available for dovecot and its modules.")
539
540 (listen
541 (comma-separated-string-list '("*" "::"))
542 "A list of IPs or hosts where to listen in for connections. @samp{*}
543listens in all IPv4 interfaces, @samp{::} listens in all IPv6
544interfaces. If you want to specify non-default ports or anything more
545complex, customize the address and port fields of the
546@samp{inet-listener} of the specific services you are interested in.")
547
548 (dict
549 (dict-configuration (dict-configuration))
550 "Dict configuration, as created by the @code{dict-configuration}
551constructor.")
552
553 (passdbs
554 (passdb-configuration-list (list (passdb-configuration (driver "pam"))))
555 "List of passdb configurations, each one created by the
556@code{passdb-configuration} constructor.")
557
558 (userdbs
559 (userdb-configuration-list (list (userdb-configuration (driver "passwd"))))
560 "List of userdb configurations, each one created by the
561@code{userdb-configuration} constructor.")
562
563 (plugin-configuration
564 (plugin-configuration (plugin-configuration))
565 "Plug-in configuration, created by the @code{plugin-configuration}
566constructor.")
567
568 (namespaces
569 (list-of-namespace-configuration
570 (list
571 (namespace-configuration
572 (name "inbox")
573 (prefix "")
574 (inbox? #t)
575 (mailboxes
576 (list
577 (mailbox-configuration (name "Junk") (special-use '("\\Junk")))
578 (mailbox-configuration (name "Trash") (special-use '("\\Trash")))
579 (mailbox-configuration (name "Sent") (special-use '("\\Sent")))
580 (mailbox-configuration (name "Sent Messages") (special-use '("\\Sent")))
581 (mailbox-configuration (name "Drafts") (special-use '("\\Drafts"))))))))
582 "List of namespaces. Each item in the list is created by the
583@code{namespace-configuration} constructor.")
584
585 (base-dir
586 (file-name "/var/run/dovecot/")
587 "Base directory where to store runtime data.")
588
589 (login-greeting
590 (string "Dovecot ready.")
591 "Greeting message for clients.")
592
593 (login-trusted-networks
594 (space-separated-string-list '())
595 "List of trusted network ranges. Connections from these IPs are
596allowed to override their IP addresses and ports (for logging and for
597authentication checks). @samp{disable-plaintext-auth} is also ignored
598for these networks. Typically you'd specify your IMAP proxy servers
599here.")
600
601 (login-access-sockets
602 (space-separated-string-list '())
603 "List of login access check sockets (e.g. tcpwrap).")
604
605 (verbose-proctitle?
606 (boolean #f)
607 "Show more verbose process titles (in ps). Currently shows user name
608and IP address. Useful for seeing who are actually using the IMAP
609processes (e.g. shared mailboxes or if same uid is used for multiple
610accounts).")
611
612 (shutdown-clients?
613 (boolean #t)
614 "Should all processes be killed when Dovecot master process shuts down.
615Setting this to @code{#f} means that Dovecot can be upgraded without
616forcing existing client connections to close (although that could also
617be a problem if the upgrade is e.g. because of a security fix).")
618
619 (doveadm-worker-count
620 (non-negative-integer 0)
621 "If non-zero, run mail commands via this many connections to doveadm
622server, instead of running them directly in the same process.")
623
624 (doveadm-socket-path
625 (string "doveadm-server")
626 "UNIX socket or host:port used for connecting to doveadm server.")
627
628 (import-environment
629 (space-separated-string-list '("TZ"))
630 "List of environment variables that are preserved on Dovecot startup
631and passed down to all of its child processes. You can also give
632key=value pairs to always set specific settings.")
633
634;;; Authentication processes
635
636 (disable-plaintext-auth?
637 (boolean #t)
638 "Disable LOGIN command and all other plaintext authentications unless
639SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP
640matches the local IP (i.e. you're connecting from the same computer),
641the connection is considered secure and plaintext authentication is
642allowed. See also ssl=required setting.")
643
644 (auth-cache-size
645 (non-negative-integer 0)
646 "Authentication cache size (e.g. @samp{#e10e6}). 0 means it's disabled.
647Note that bsdauth, PAM and vpopmail require @samp{cache-key} to be set
648for caching to be used.")
649
650 (auth-cache-ttl
651 (string "1 hour")
652 "Time to live for cached data. After TTL expires the cached record
653is no longer used, *except* if the main database lookup returns internal
654failure. We also try to handle password changes automatically: If
655user's previous authentication was successful, but this one wasn't, the
656cache isn't used. For now this works only with plaintext
657authentication.")
658
659 (auth-cache-negative-ttl
660 (string "1 hour")
661 "TTL for negative hits (user not found, password mismatch).
6620 disables caching them completely.")
663
664 (auth-realms
665 (space-separated-string-list '())
666 "List of realms for SASL authentication mechanisms that need them.
667You can leave it empty if you don't want to support multiple realms.
668Many clients simply use the first one listed here, so keep the default
669realm first.")
670
671 (auth-default-realm
672 (string "")
673 "Default realm/domain to use if none was specified. This is used for
674both SASL realms and appending @@domain to username in plaintext
675logins.")
676
677 (auth-username-chars
678 (string
679 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@")
680 "List of allowed characters in username. If the user-given username
681contains a character not listed in here, the login automatically fails.
682This is just an extra check to make sure user can't exploit any
683potential quote escaping vulnerabilities with SQL/LDAP databases. If
684you want to allow all characters, set this value to empty.")
685
686 (auth-username-translation
687 (string "")
688 "Username character translations before it's looked up from
689databases. The value contains series of from -> to characters. For
690example @samp{#@@/@@} means that @samp{#} and @samp{/} characters are
691translated to @samp{@@}.")
692
693 (auth-username-format
694 (string "%Lu")
695 "Username formatting before it's looked up from databases. You can
696use the standard variables here, e.g. %Lu would lowercase the username,
697%n would drop away the domain if it was given, or @samp{%n-AT-%d} would
698change the @samp{@@} into @samp{-AT-}. This translation is done after
699@samp{auth-username-translation} changes.")
700
701 (auth-master-user-separator
702 (string "")
703 "If you want to allow master users to log in by specifying the master
704username within the normal username string (i.e. not using SASL
705mechanism's support for it), you can specify the separator character
706here. The format is then <username><separator><master username>.
707UW-IMAP uses @samp{*} as the separator, so that could be a good
708choice.")
709
710 (auth-anonymous-username
711 (string "anonymous")
712 "Username to use for users logging in with ANONYMOUS SASL
713mechanism.")
714
715 (auth-worker-max-count
716 (non-negative-integer 30)
717 "Maximum number of dovecot-auth worker processes. They're used to
718execute blocking passdb and userdb queries (e.g. MySQL and PAM).
719They're automatically created and destroyed as needed.")
720
721 (auth-gssapi-hostname
722 (string "")
723 "Host name to use in GSSAPI principal names. The default is to use
724the name returned by gethostname(). Use @samp{$ALL} (with quotes) to
725allow all keytab entries.")
726
727 (auth-krb5-keytab
728 (string "")
729 "Kerberos keytab to use for the GSSAPI mechanism. Will use the
730system default (usually /etc/krb5.keytab) if not specified. You may
731need to change the auth service to run as root to be able to read this
732file.")
733
734 (auth-use-winbind?
735 (boolean #f)
736 "Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon
737and @samp{ntlm-auth} helper.
738<doc/wiki/Authentication/Mechanisms/Winbind.txt>.")
739
740 (auth-winbind-helper-path
741 (file-name "/usr/bin/ntlm_auth")
742 "Path for Samba's @samp{ntlm-auth} helper binary.")
743
744 (auth-failure-delay
745 (string "2 secs")
746 "Time to delay before replying to failed authentications.")
747
748 (auth-ssl-require-client-cert?
749 (boolean #f)
750 "Require a valid SSL client certificate or the authentication
751fails.")
752
753 (auth-ssl-username-from-cert?
754 (boolean #f)
755 "Take the username from client's SSL certificate, using
756@code{X509_NAME_get_text_by_NID()} which returns the subject's DN's
757CommonName.")
758
759 (auth-mechanisms
760 (space-separated-string-list '("plain"))
761 "List of wanted authentication mechanisms. Supported mechanisms are:
762@samp{plain}, @samp{login}, @samp{digest-md5}, @samp{cram-md5},
763@samp{ntlm}, @samp{rpa}, @samp{apop}, @samp{anonymous}, @samp{gssapi},
764@samp{otp}, @samp{skey}, and @samp{gss-spnego}. NOTE: See also
765@samp{disable-plaintext-auth} setting.")
766
767 (director-servers
768 (space-separated-string-list '())
769 "List of IPs or hostnames to all director servers, including ourself.
770Ports can be specified as ip:port. The default port is the same as what
771director service's @samp{inet-listener} is using.")
772
773 (director-mail-servers
774 (space-separated-string-list '())
775 "List of IPs or hostnames to all backend mail servers. Ranges are
776allowed too, like 10.0.0.10-10.0.0.30.")
777
778 (director-user-expire
779 (string "15 min")
780 "How long to redirect users to a specific server after it no longer
781has any connections.")
782
783 (director-username-hash
784 (string "%Lu")
785 "How the username is translated before being hashed. Useful values
786include %Ln if user can log in with or without @@domain, %Ld if mailboxes
787are shared within domain.")
788
789;;; Log destination.
790
791 (log-path
792 (string "syslog")
793 "Log file to use for error messages. @samp{syslog} logs to syslog,
794@samp{/dev/stderr} logs to stderr.")
795
796 (info-log-path
797 (string "")
798 "Log file to use for informational messages. Defaults to
799@samp{log-path}.")
800
801 (debug-log-path
802 (string "")
803 "Log file to use for debug messages. Defaults to
804@samp{info-log-path}.")
805
806 (syslog-facility
807 (string "mail")
808 "Syslog facility to use if you're logging to syslog. Usually if you
809don't want to use @samp{mail}, you'll use local0..local7. Also other
810standard facilities are supported.")
811
812 (auth-verbose?
813 (boolean #f)
814 "Log unsuccessful authentication attempts and the reasons why they
815failed.")
816
817 (auth-verbose-passwords
818 (string "no")
819 "In case of password mismatches, log the attempted password. Valid
820values are no, plain and sha1. sha1 can be useful for detecting brute
821force password attempts vs. user simply trying the same password over
822and over again. You can also truncate the value to n chars by appending
823\":n\" (e.g. sha1:6).")
824
825 (auth-debug?
826 (boolean #f)
827 "Even more verbose logging for debugging purposes. Shows for example
828SQL queries.")
829
830 (auth-debug-passwords?
831 (boolean #f)
832 "In case of password mismatches, log the passwords and used scheme so
833the problem can be debugged. Enabling this also enables
834@samp{auth-debug}.")
835
836 (mail-debug?
837 (boolean #f)
838 "Enable mail process debugging. This can help you figure out why
839Dovecot isn't finding your mails.")
840
841 (verbose-ssl?
842 (boolean #f)
843 "Show protocol level SSL errors.")
844
845 (log-timestamp
846 (string "\"%b %d %H:%M:%S \"")
847 "Prefix for each line written to log file. % codes are in
848strftime(3) format.")
849
850 (login-log-format-elements
851 (space-separated-string-list
852 '("user=<%u>" "method=%m" "rip=%r" "lip=%l" "mpid=%e" "%c"))
853 "List of elements we want to log. The elements which have a
854non-empty variable value are joined together to form a comma-separated
855string.")
856
857 (login-log-format
858 (string "%$: %s")
859 "Login log format. %s contains @samp{login-log-format-elements}
860string, %$ contains the data we want to log.")
861
862 (mail-log-prefix
863 (string "\"%s(%u)<%{pid}><%{session}>: \"")
864 "Log prefix for mail processes. See doc/wiki/Variables.txt for list
865of possible variables you can use.")
866
867 (deliver-log-format
868 (string "msgid=%m: %$")
869 "Format to use for logging mail deliveries. You can use variables:
870@table @code
871@item %$
872Delivery status message (e.g. @samp{saved to INBOX})
873@item %m
874Message-ID
875@item %s
876Subject
877@item %f
878From address
879@item %p
880Physical size
881@item %w
882Virtual size.
883@end table")
884
885;;; Mailbox locations and namespaces
886
887 (mail-location
888 (string "")
889 "Location for users' mailboxes. The default is empty, which means
890that Dovecot tries to find the mailboxes automatically. This won't work
891if the user doesn't yet have any mail, so you should explicitly tell
892Dovecot the full location.
893
894If you're using mbox, giving a path to the INBOX
895file (e.g. /var/mail/%u) isn't enough. You'll also need to tell Dovecot
896where the other mailboxes are kept. This is called the \"root mail
897directory\", and it must be the first path given in the
898@samp{mail-location} setting.
899
900There are a few special variables you can use, eg.:
901
902@table @samp
903@item %u
904username
905@item %n
906user part in user@@domain, same as %u if there's no domain
907@item %d
908domain part in user@@domain, empty if there's no domain
909@item %h
910home director
911@end table
912
913See doc/wiki/Variables.txt for full list. Some examples:
914@table @samp
915@item maildir:~/Maildir
916@item mbox:~/mail:INBOX=/var/mail/%u
917@item mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%
918@end table")
919
920 (mail-uid
921 (string "")
922 "System user and group used to access mails. If you use multiple,
923userdb can override these by returning uid or gid fields. You can use
924either numbers or names. <doc/wiki/UserIds.txt>.")
925
926 (mail-gid
927 (string "")
928 "")
929
930 (mail-privileged-group
931 (string "")
932 "Group to enable temporarily for privileged operations. Currently
933this is used only with INBOX when either its initial creation or
934dotlocking fails. Typically this is set to \"mail\" to give access to
935/var/mail.")
936
937 (mail-access-groups
938 (string "")
939 "Grant access to these supplementary groups for mail processes.
940Typically these are used to set up access to shared mailboxes. Note
941that it may be dangerous to set these if users can create
942symlinks (e.g. if \"mail\" group is set here, ln -s /var/mail ~/mail/var
943could allow a user to delete others' mailboxes, or ln -s
944/secret/shared/box ~/mail/mybox would allow reading it).")
945
946 (mail-full-filesystem-access?
947 (boolean #f)
948 "Allow full file system access to clients. There's no access checks
949other than what the operating system does for the active UID/GID. It
950works with both maildir and mboxes, allowing you to prefix mailboxes
951names with e.g. /path/ or ~user/.")
952
953;;; Mail processes
954
955 (mmap-disable?
956 (boolean #f)
957 "Don't use mmap() at all. This is required if you store indexes to
958shared file systems (NFS or clustered file system).")
959
960 (dotlock-use-excl?
961 (boolean #t)
962 "Rely on @samp{O_EXCL} to work when creating dotlock files. NFS
963supports @samp{O_EXCL} since version 3, so this should be safe to use
964nowadays by default.")
965
966 (mail-fsync
967 (string "optimized")
968 "When to use fsync() or fdatasync() calls:
969@table @code
970@item optimized
971Whenever necessary to avoid losing important data
972@item always
973Useful with e.g. NFS when write()s are delayed
974@item never
975Never use it (best performance, but crashes can lose data).
976@end table")
977
978 (mail-nfs-storage?
979 (boolean #f)
980 "Mail storage exists in NFS. Set this to yes to make Dovecot flush
981NFS caches whenever needed. If you're using only a single mail server
982this isn't needed.")
983
984 (mail-nfs-index?
985 (boolean #f)
986 "Mail index files also exist in NFS. Setting this to yes requires
987@samp{mmap-disable? #t} and @samp{fsync-disable? #f}.")
988
989 (lock-method
990 (string "fcntl")
991 "Locking method for index files. Alternatives are fcntl, flock and
992dotlock. Dotlocking uses some tricks which may create more disk I/O
993than other locking methods. NFS users: flock doesn't work, remember to
994change @samp{mmap-disable}.")
995
996 (mail-temp-dir
997 (file-name "/tmp")
998 "Directory in which LDA/LMTP temporarily stores incoming mails >128
999kB.")
1000
1001 (first-valid-uid
1002 (non-negative-integer 500)
1003 "Valid UID range for users. This is mostly to make sure that users can't
1004log in as daemons or other system users. Note that denying root logins is
1005hardcoded to dovecot binary and can't be done even if @samp{first-valid-uid}
1006is set to 0.")
1007
1008 (last-valid-uid
1009 (non-negative-integer 0)
1010 "")
1011
1012 (first-valid-gid
1013 (non-negative-integer 1)
1014 "Valid GID range for users. Users having non-valid GID as primary group ID
1015aren't allowed to log in. If user belongs to supplementary groups with
1016non-valid GIDs, those groups are not set.")
1017
1018 (last-valid-gid
1019 (non-negative-integer 0)
1020 "")
1021
1022 (mail-max-keyword-length
1023 (non-negative-integer 50)
1024 "Maximum allowed length for mail keyword name. It's only forced when
1025trying to create new keywords.")
1026
1027 (valid-chroot-dirs
1028 (colon-separated-file-name-list '())
1029 "List of directories under which chrooting is allowed for mail
1030processes (i.e. /var/mail will allow chrooting to /var/mail/foo/bar
1031too). This setting doesn't affect @samp{login-chroot}
1032@samp{mail-chroot} or auth chroot settings. If this setting is empty,
1033\"/./\" in home dirs are ignored. WARNING: Never add directories here
1034which local users can modify, that may lead to root exploit. Usually
1035this should be done only if you don't allow shell access for users.
1036<doc/wiki/Chrooting.txt>.")
1037
1038 (mail-chroot
1039 (string "")
1040 "Default chroot directory for mail processes. This can be overridden
1041for specific users in user database by giving /./ in user's home
1042directory (e.g. /home/./user chroots into /home). Note that usually
1043there is no real need to do chrooting, Dovecot doesn't allow users to
1044access files outside their mail directory anyway. If your home
1045directories are prefixed with the chroot directory, append \"/.\" to
1046@samp{mail-chroot}. <doc/wiki/Chrooting.txt>.")
1047
1048 (auth-socket-path
1049 (file-name "/var/run/dovecot/auth-userdb")
1050 "UNIX socket path to master authentication server to find users.
1051This is used by imap (for shared users) and lda.")
1052
1053 (mail-plugin-dir
1054 (file-name "/usr/lib/dovecot")
1055 "Directory where to look up mail plugins.")
1056
1057 (mail-plugins
1058 (space-separated-string-list '())
1059 "List of plugins to load for all services. Plugins specific to IMAP,
1060LDA, etc. are added to this list in their own .conf files.")
1061
1062
1063 (mail-cache-min-mail-count
1064 (non-negative-integer 0)
1065 "The minimum number of mails in a mailbox before updates are done to
1066cache file. This allows optimizing Dovecot's behavior to do less disk
1067writes at the cost of more disk reads.")
1068
1069 (mailbox-idle-check-interval
1070 (string "30 secs")
1071 "When IDLE command is running, mailbox is checked once in a while to
1072see if there are any new mails or other changes. This setting defines
1073the minimum time to wait between those checks. Dovecot can also use
1074dnotify, inotify and kqueue to find out immediately when changes
1075occur.")
1076
1077 (mail-save-crlf?
1078 (boolean #f)
1079 "Save mails with CR+LF instead of plain LF. This makes sending those
1080mails take less CPU, especially with sendfile() syscall with Linux and
1081FreeBSD. But it also creates a bit more disk I/O which may just make it
1082slower. Also note that if other software reads the mboxes/maildirs,
1083they may handle the extra CRs wrong and cause problems.")
1084
1085 (maildir-stat-dirs?
1086 (boolean #f)
1087 "By default LIST command returns all entries in maildir beginning
1088with a dot. Enabling this option makes Dovecot return only entries
1089which are directories. This is done by stat()ing each entry, so it
1090causes more disk I/O.
1091 (For systems setting struct @samp{dirent->d_type} this check is free
1092and it's done always regardless of this setting).")
1093
1094 (maildir-copy-with-hardlinks?
1095 (boolean #t)
1096 "When copying a message, do it with hard links whenever possible.
1097This makes the performance much better, and it's unlikely to have any
1098side effects.")
1099
1100 (maildir-very-dirty-syncs?
1101 (boolean #f)
1102 "Assume Dovecot is the only MUA accessing Maildir: Scan cur/
1103directory only when its mtime changes unexpectedly or when we can't find
1104the mail otherwise.")
1105
1106 (mbox-read-locks
1107 (space-separated-string-list '("fcntl"))
1108 "Which locking methods to use for locking mbox. There are four
1109available:
1110
1111@table @code
1112@item dotlock
1113Create <mailbox>.lock file. This is the oldest and most NFS-safe
1114solution. If you want to use /var/mail/ like directory, the users will
1115need write access to that directory.
1116@item dotlock-try
1117Same as dotlock, but if it fails because of permissions or because there
1118isn't enough disk space, just skip it.
1119@item fcntl
1120Use this if possible. Works with NFS too if lockd is used.
1121@item flock
1122May not exist in all systems. Doesn't work with NFS.
1123@item lockf
1124May not exist in all systems. Doesn't work with NFS.
1125@end table
1126
1127You can use multiple locking methods; if you do the order they're declared
1128in is important to avoid deadlocks if other MTAs/MUAs are using multiple
1129locking methods as well. Some operating systems don't allow using some of
1130them simultaneously.")
1131
1132 (mbox-write-locks
1133 (space-separated-string-list '("dotlock" "fcntl"))
1134 "")
1135
1136 (mbox-lock-timeout
1137 (string "5 mins")
1138 "Maximum time to wait for lock (all of them) before aborting.")
1139
1140 (mbox-dotlock-change-timeout
1141 (string "2 mins")
1142 "If dotlock exists but the mailbox isn't modified in any way,
1143override the lock file after this much time.")
1144
1145 (mbox-dirty-syncs?
1146 (boolean #t)
1147 "When mbox changes unexpectedly we have to fully read it to find out
1148what changed. If the mbox is large this can take a long time. Since
1149the change is usually just a newly appended mail, it'd be faster to
1150simply read the new mails. If this setting is enabled, Dovecot does
1151this but still safely fallbacks to re-reading the whole mbox file
1152whenever something in mbox isn't how it's expected to be. The only real
1153downside to this setting is that if some other MUA changes message
1154flags, Dovecot doesn't notice it immediately. Note that a full sync is
1155done with SELECT, EXAMINE, EXPUNGE and CHECK commands.")
1156
1157 (mbox-very-dirty-syncs?
1158 (boolean #f)
1159 "Like @samp{mbox-dirty-syncs}, but don't do full syncs even with SELECT,
1160EXAMINE, EXPUNGE or CHECK commands. If this is set,
1161@samp{mbox-dirty-syncs} is ignored.")
1162
1163 (mbox-lazy-writes?
1164 (boolean #t)
1165 "Delay writing mbox headers until doing a full write sync (EXPUNGE
1166and CHECK commands and when closing the mailbox). This is especially
1167useful for POP3 where clients often delete all mails. The downside is
1168that our changes aren't immediately visible to other MUAs.")
1169
1170 (mbox-min-index-size
1171 (non-negative-integer 0)
1172 "If mbox size is smaller than this (e.g. 100k), don't write index
1173files. If an index file already exists it's still read, just not
1174updated.")
1175
1176 (mdbox-rotate-size
1177 (non-negative-integer #e10e6)
1178 "Maximum dbox file size until it's rotated.")
1179
1180 (mdbox-rotate-interval
1181 (string "1d")
1182 "Maximum dbox file age until it's rotated. Typically in days. Day
1183begins from midnight, so 1d = today, 2d = yesterday, etc. 0 = check
1184disabled.")
1185
1186 (mdbox-preallocate-space?
1187 (boolean #f)
1188 "When creating new mdbox files, immediately preallocate their size to
1189@samp{mdbox-rotate-size}. This setting currently works only in Linux
1190with some file systems (ext4, xfs).")
1191
1192 (mail-attribute-dict
1193 (string "")
1194 "The location of a dictionary used to store @code{IMAP METADATA}
1195as defined by @uref{https://tools.ietf.org/html/rfc5464, RFC@tie{}5464}.
1196
1197The IMAP METADATA commands are available only if the ``imap''
1198protocol configuration's @code{imap-metadata?} field is @samp{#t}.")
1199
1200 (mail-attachment-dir
1201 (string "")
1202 "sdbox and mdbox support saving mail attachments to external files,
1203which also allows single instance storage for them. Other backends
1204don't support this for now.
1205
1206WARNING: This feature hasn't been tested much yet. Use at your own risk.
1207
1208Directory root where to store mail attachments. Disabled, if empty.")
1209
1210 (mail-attachment-min-size
1211 (non-negative-integer #e128e3)
1212 "Attachments smaller than this aren't saved externally. It's also
1213possible to write a plugin to disable saving specific attachments
1214externally.")
1215
1216 (mail-attachment-fs
1217 (string "sis posix")
1218 "File system backend to use for saving attachments:
1219@table @code
1220@item posix
1221No SiS done by Dovecot (but this might help FS's own deduplication)
1222@item sis posix
1223SiS with immediate byte-by-byte comparison during saving
1224@item sis-queue posix
1225SiS with delayed comparison and deduplication.
1226@end table")
1227
1228 (mail-attachment-hash
1229 (string "%{sha1}")
1230 "Hash format to use in attachment filenames. You can add any text and
1231variables: @code{%@{md4@}}, @code{%@{md5@}}, @code{%@{sha1@}},
1232@code{%@{sha256@}}, @code{%@{sha512@}}, @code{%@{size@}}. Variables can be
1233truncated, e.g. @code{%@{sha256:80@}} returns only first 80 bits.")
1234
1235 (default-process-limit
1236 (non-negative-integer 100)
1237 "")
1238
1239 (default-client-limit
1240 (non-negative-integer 1000)
1241 "")
1242
1243 (default-vsz-limit
1244 (non-negative-integer #e256e6)
1245 "Default VSZ (virtual memory size) limit for service processes.
1246This is mainly intended to catch and kill processes that leak memory
1247before they eat up everything.")
1248
1249 (default-login-user
1250 (string "dovenull")
1251 "Login user is internally used by login processes. This is the most
1252untrusted user in Dovecot system. It shouldn't have access to anything
1253at all.")
1254
1255 (default-internal-user
1256 (string "dovecot")
1257 "Internal user is used by unprivileged processes. It should be
1258separate from login user, so that login processes can't disturb other
1259processes.")
1260
1261 (ssl?
1262 (string "required")
1263 "SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>.")
1264
1265 (ssl-cert
1266 (string "</etc/dovecot/default.pem")
1267 "PEM encoded X.509 SSL/TLS certificate (public key).")
1268
1269 (ssl-key
1270 (string "</etc/dovecot/private/default.pem")
1271 "PEM encoded SSL/TLS private key. The key is opened before
1272dropping root privileges, so keep the key file unreadable by anyone but
1273root.")
1274
1275 (ssl-key-password
1276 (string "")
1277 "If key file is password protected, give the password here.
1278Alternatively give it when starting dovecot with -p parameter. Since
1279this file is often world-readable, you may want to place this setting
1280instead to a different.")
1281
1282 (ssl-ca
1283 (string "")
1284 "PEM encoded trusted certificate authority. Set this only if you
1285intend to use @samp{ssl-verify-client-cert? #t}. The file should
1286contain the CA certificate(s) followed by the matching
1287CRL(s). (e.g. @samp{ssl-ca </etc/ssl/certs/ca.pem}).")
1288 (ssl-require-crl?
1289 (boolean #t)
1290 "Require that CRL check succeeds for client certificates.")
1291 (ssl-verify-client-cert?
1292 (boolean #f)
1293 "Request client to send a certificate. If you also want to require
1294it, set @samp{auth-ssl-require-client-cert? #t} in auth section.")
1295
1296 (ssl-cert-username-field
1297 (string "commonName")
1298 "Which field from certificate to use for username. commonName and
1299x500UniqueIdentifier are the usual choices. You'll also need to set
1300@samp{auth-ssl-username-from-cert? #t}.")
1301
1302 (ssl-min-protocol
1303 (string "TLSv1")
1304 "Minimum SSL protocol version to accept.")
1305
1306 (ssl-cipher-list
1307 (string "ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH")
1308 "SSL ciphers to use.")
1309
1310 (ssl-crypto-device
1311 (string "")
1312 "SSL crypto device to use, for valid values run \"openssl engine\".")
1313
1314 (postmaster-address
1315 (string "postmaster@%d")
1316 "Address to use when sending rejection mails.
1317Default is postmaster@@<your domain>. %d expands to recipient domain.")
1318
1319 (hostname
1320 (string "")
1321 "Hostname to use in various parts of sent mails (e.g. in Message-Id)
1322and in LMTP replies. Default is the system's real hostname@@domain.")
1323
1324 (quota-full-tempfail?
1325 (boolean #f)
1326 "If user is over quota, return with temporary failure instead of
1327bouncing the mail.")
1328
1329 (sendmail-path
1330 (file-name "/usr/sbin/sendmail")
1331 "Binary to use for sending mails.")
1332
1333 (submission-host
1334 (string "")
1335 "If non-empty, send mails via this SMTP host[:port] instead of
1336sendmail.")
1337
1338 (rejection-subject
1339 (string "Rejected: %s")
1340 "Subject: header to use for rejection mails. You can use the same
1341variables as for @samp{rejection-reason} below.")
1342
1343 (rejection-reason
1344 (string "Your message to <%t> was automatically rejected:%n%r")
1345 "Human readable error message for rejection mails. You can use
1346variables:
1347
1348@table @code
1349@item %n
1350CRLF
1351@item %r
1352reason
1353@item %s
1354original subject
1355@item %t
1356recipient
1357@end table")
1358
1359 (recipient-delimiter
1360 (string "+")
1361 "Delimiter character between local-part and detail in email
1362address.")
1363
1364 (lda-original-recipient-header
1365 (string "")
1366 "Header where the original recipient address (SMTP's RCPT TO:
1367address) is taken from if not available elsewhere. With dovecot-lda -a
1368parameter overrides this. A commonly used header for this is
1369X-Original-To.")
1370
1371 (lda-mailbox-autocreate?
1372 (boolean #f)
1373 "Should saving a mail to a nonexistent mailbox automatically create
1374it?.")
1375
1376 (lda-mailbox-autosubscribe?
1377 (boolean #f)
1378 "Should automatically created mailboxes be also automatically
1379subscribed?.")
1380
1381
1382 (imap-max-line-length
1383 (non-negative-integer #e64e3)
1384 "Maximum IMAP command line length. Some clients generate very long
1385command lines with huge mailboxes, so you may need to raise this if you
1386get \"Too long argument\" or \"IMAP command line too large\" errors
1387often.")
1388
1389 (imap-logout-format
1390 (string "in=%i out=%o deleted=%{deleted} expunged=%{expunged} trashed=%{trashed} hdr_count=%{fetch_hdr_count} hdr_bytes=%{fetch_hdr_bytes} body_count=%{fetch_body_count} body_bytes=%{fetch_body_bytes}")
1391 "IMAP logout format string:
1392@table @code
1393@item %i
1394total number of bytes read from client
1395@item %o
1396total number of bytes sent to client.
1397@end table
1398See @file{doc/wiki/Variables.txt} for a list of all the variables you can use.")
1399
1400 (imap-capability
1401 (string "")
1402 "Override the IMAP CAPABILITY response. If the value begins with '+',
1403add the given capabilities on top of the defaults (e.g. +XFOO XBAR).")
1404
1405 (imap-idle-notify-interval
1406 (string "2 mins")
1407 "How long to wait between \"OK Still here\" notifications when client
1408is IDLEing.")
1409
1410 (imap-id-send
1411 (string "")
1412 "ID field names and values to send to clients. Using * as the value
1413makes Dovecot use the default value. The following fields have default
1414values currently: name, version, os, os-version, support-url,
1415support-email.")
1416
1417 (imap-id-log
1418 (string "")
1419 "ID fields sent by client to log. * means everything.")
1420
1421 (imap-client-workarounds
1422 (space-separated-string-list '())
1423 "Workarounds for various client bugs:
1424
1425@table @code
1426@item delay-newmail
1427Send EXISTS/RECENT new mail notifications only when replying to NOOP and
1428CHECK commands. Some clients ignore them otherwise, for example OSX
1429Mail (<v2.1). Outlook Express breaks more badly though, without this it
1430may show user \"Message no longer in server\" errors. Note that OE6
1431still breaks even with this workaround if synchronization is set to
1432\"Headers Only\".
1433
1434@item tb-extra-mailbox-sep
1435Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and
1436adds extra @samp{/} suffixes to mailbox names. This option causes Dovecot to
1437ignore the extra @samp{/} instead of treating it as invalid mailbox name.
1438
1439@item tb-lsub-flags
1440Show \\Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox).
1441This makes Thunderbird realize they aren't selectable and show them
1442greyed out, instead of only later giving \"not selectable\" popup error.
1443@end table
1444")
1445
1446 (imap-urlauth-host
1447 (string "")
1448 "Host allowed in URLAUTH URLs sent by client. \"*\" allows all.")
1449
1450 (protocols
1451 (protocol-configuration-list
1452 (list (protocol-configuration
1453 (name "imap"))))
1454 "List of protocols we want to serve. Available protocols include
1455@samp{imap}, @samp{pop3}, and @samp{lmtp}.")
1456
1457 (services
1458 (service-configuration-list
1459 (list
1460 (service-configuration
1461 (kind "imap-login")
1462 (client-limit 0)
1463 (process-limit 0)
1464 (listeners
1465 (list
1466 (inet-listener-configuration (protocol "imap") (port 143) (ssl? #f))
1467 (inet-listener-configuration (protocol "imaps") (port 993) (ssl? #t)))))
1468 (service-configuration
1469 (kind "pop3-login")
1470 (listeners
1471 (list
1472 (inet-listener-configuration (protocol "pop3") (port 110) (ssl? #f))
1473 (inet-listener-configuration (protocol "pop3s") (port 995) (ssl? #t)))))
1474 (service-configuration
1475 (kind "lmtp")
1476 (client-limit 1)
1477 (process-limit 0)
1478 (listeners
1479 (list (unix-listener-configuration (path "lmtp") (mode "0666")))))
1480 (service-configuration
1481 (kind "imap")
1482 (client-limit 1)
1483 (process-limit 1024))
1484 (service-configuration
1485 (kind "pop3")
1486 (client-limit 1)
1487 (process-limit 1024))
1488 (service-configuration
1489 (kind "auth")
1490 (service-count 0)
1491 (client-limit 0)
1492 (process-limit 1)
1493 (listeners
1494 (list (unix-listener-configuration (path "auth-userdb")))))
1495 (service-configuration
1496 (kind "auth-worker")
1497 (client-limit 1)
1498 (process-limit 0))
1499 (service-configuration
1500 (kind "dict")
1501 (client-limit 1)
1502 (process-limit 0)
1503 (listeners (list (unix-listener-configuration (path "dict")))))))
1504 "List of services to enable. Available services include @samp{imap},
1505@samp{imap-login}, @samp{pop3}, @samp{pop3-login}, @samp{auth}, and
1506@samp{lmtp}."))
1507
1508(define-configuration opaque-dovecot-configuration
1509 (dovecot
1510 (file-like dovecot)
1511 "The dovecot package.")
1512
1513 (extensions
1514 (package-list '())
1515 "Plugins and extensions to the Dovecot package. Specify a list of dovecot
1516plugins that needs to be available for dovecot and its modules.")
1517
1518 (string
1519 (string (configuration-missing-field 'opaque-dovecot-configuration
1520 'string))
1521 "The contents of the @code{dovecot.conf} to use."))
1522
1523(define %dovecot-accounts
1524 ;; Account and group for the Dovecot daemon.
1525 (list (user-group (name "dovecot") (system? #t))
1526 (user-account
1527 (name "dovecot")
1528 (group "dovecot")
1529 (system? #t)
1530 (comment "Dovecot daemon user")
1531 (home-directory "/var/empty")
1532 (shell (file-append shadow "/sbin/nologin")))
1533
1534 (user-group (name "dovenull") (system? #t))
1535 (user-account
1536 (name "dovenull")
1537 (group "dovenull")
1538 (system? #t)
1539 (comment "Dovecot daemon login user")
1540 (home-directory "/var/empty")
1541 (shell (file-append shadow "/sbin/nologin")))))
1542
1543(define (make-dovecot-moduledir packages)
1544 "Return a computed file containing a union of Dovecot module directories from PACKAGES.
1545Each package's '/lib/dovecot' directory is combined into a single location."
1546 ;; Create a union of the set of modules and dovecot itself.
1547 (with-imported-modules '((guix build union))
1548 (computed-file
1549 "dovecot-moduledir"
1550 #~(begin
1551 (use-modules (guix build union) (srfi srfi-26))
1552
1553 (union-build #$output
1554 (map (cut string-append <>
1555 "/lib/dovecot")
1556 (list #$@packages)))))))
1557
1558(define (%dovecot-activation config)
1559 ;; Activation gexp.
1560 (let ((config-str
1561 (cond
1562 ((opaque-dovecot-configuration? config)
1563 (opaque-dovecot-configuration-string config))
1564 (else
1565 (with-output-to-string
1566 (lambda ()
1567 (serialize-configuration config
1568 dovecot-configuration-fields))))))
1569 (moduledir-directory
1570 (cond
1571 ((opaque-dovecot-configuration? config)
1572 (make-dovecot-moduledir (cons* (opaque-dovecot-configuration-dovecot config)
1573 (opaque-dovecot-configuration-extensions config))))
1574 (else
1575 (make-dovecot-moduledir (cons* (dovecot-configuration-dovecot config)
1576 (dovecot-configuration-extensions config)))))))
1577 (with-imported-modules (source-module-closure '((gnu build activation)))
1578 #~(begin
1579 (use-modules (guix build utils) (gnu build activation))
1580 (define (build-subject parameters)
1581 (string-concatenate
1582 (map (lambda (pair)
1583 (let ((k (car pair)) (v (cdr pair)))
1584 (define (escape-char str chr)
1585 (string-join (string-split str chr) (string #\\ chr)))
1586 (string-append "/" k "="
1587 (escape-char (escape-char v #\=) #\/))))
1588 (filter (lambda (pair) (cdr pair)) parameters))))
1589 (define* (create-self-signed-certificate-if-absent
1590 #:key private-key public-key (owner (getpwnam "root"))
1591 (common-name (gethostname))
1592 (organization-name "Guix")
1593 (organization-unit-name "Default Self-Signed Certificate")
1594 (subject-parameters `(("CN" . ,common-name)
1595 ("O" . ,organization-name)
1596 ("OU" . ,organization-unit-name)))
1597 (subject (build-subject subject-parameters)))
1598 ;; Note that by default, OpenSSL outputs keys in PEM format. This
1599 ;; is what we want.
1600 (unless (file-exists? private-key)
1601 (cond
1602 ((zero? (system* (string-append #$openssl "/bin/openssl")
1603 "genrsa" "-out" private-key "2048"))
1604 (chown private-key (passwd:uid owner) (passwd:gid owner))
1605 (chmod private-key #o400))
1606 (else
1607 (format (current-error-port)
1608 "Failed to create private key at ~a.\n" private-key))))
1609 (unless (file-exists? public-key)
1610 (cond
1611 ((zero? (system* (string-append #$openssl "/bin/openssl")
1612 "req" "-new" "-x509" "-key" private-key
1613 "-out" public-key "-days" "3650"
1614 "-batch" "-subj" subject))
1615 (chown public-key (passwd:uid owner) (passwd:gid owner))
1616 (chmod public-key #o444))
1617 (else
1618 (format (current-error-port)
1619 "Failed to create public key at ~a.\n" public-key)))))
1620 (let ((user (getpwnam "dovecot")))
1621 (mkdir-p/perms "/var/run/dovecot" user #o755)
1622 (mkdir-p/perms "/var/lib/dovecot" user #o755)
1623 (mkdir-p/perms "/etc/dovecot" user #o755)
1624 (copy-file #$(plain-file "dovecot.conf" config-str)
1625 "/etc/dovecot/dovecot.conf")
1626 (mkdir-p/perms "/etc/dovecot/private" user #o700)
1627 (mkdir-p "/usr/lib")
1628 (switch-symlinks "/usr/lib/dovecot" #$moduledir-directory)
1629 (create-self-signed-certificate-if-absent
1630 #:private-key "/etc/dovecot/private/default.pem"
1631 #:public-key "/etc/dovecot/default.pem"
1632 #:owner (getpwnam "root")
1633 #:common-name (format #f "Dovecot service on ~a" (gethostname))))))))
1634
1635(define (dovecot-shepherd-service config)
1636 "Return a list of <shepherd-service> for CONFIG."
1637 (let ((dovecot (if (opaque-dovecot-configuration? config)
1638 (opaque-dovecot-configuration-dovecot config)
1639 (dovecot-configuration-dovecot config))))
1640 (list (shepherd-service
1641 (documentation "Run the Dovecot POP3/IMAP mail server.")
1642 (provision '(dovecot))
1643 (requirement '(user-processes pam networking))
1644 (start #~(make-forkexec-constructor
1645 (list (string-append #$dovecot "/sbin/dovecot")
1646 "-F")))
1647 (stop #~(lambda _
1648 (invoke #$(file-append dovecot "/sbin/dovecot")
1649 "stop")
1650 #f))))))
1651
1652(define %dovecot-pam-services
1653 (list (unix-pam-service "dovecot")))
1654
1655(define dovecot-service-type
1656 (service-type (name 'dovecot)
1657 (extensions
1658 (list (service-extension shepherd-root-service-type
1659 dovecot-shepherd-service)
1660 (service-extension account-service-type
1661 (const %dovecot-accounts))
1662 (service-extension pam-root-service-type
1663 (const %dovecot-pam-services))
1664 (service-extension activation-service-type
1665 %dovecot-activation)))
1666 (description "Run Dovecot, a mail server that can run POP3,
1667IMAP, and LMTP.")
1668 (default-value (dovecot-configuration))))
1669
1670(define-deprecated (dovecot-service #:key (config (dovecot-configuration)))
1671 dovecot-service-type
1672 "Return a service that runs @command{dovecot}, a mail server that can run
1673POP3, IMAP, and LMTP. @var{config} should be a configuration object created
1674by @code{dovecot-configuration}. @var{config} may also be created by
1675@code{opaque-dovecot-configuration}, which allows specification of the
1676@code{dovecot.conf} as a string."
1677 (service dovecot-service-type config))
1678
1679;; A little helper to make it easier to document all those fields.
1680(define (generate-dovecot-documentation)
1681 (generate-documentation
1682 `((dovecot-configuration
1683 ,dovecot-configuration-fields
1684 (dict dict-configuration)
1685 (namespaces namespace-configuration)
1686 (plugin plugin-configuration)
1687 (passdbs passdb-configuration)
1688 (userdbs userdb-configuration)
1689 (services service-configuration)
1690 (protocols protocol-configuration))
1691 (dict-configuration ,dict-configuration-fields)
1692 (plugin-configuration ,plugin-configuration-fields)
1693 (passdb-configuration ,passdb-configuration-fields)
1694 (userdb-configuration ,userdb-configuration-fields)
1695 (unix-listener-configuration ,unix-listener-configuration-fields)
1696 (fifo-listener-configuration ,fifo-listener-configuration-fields)
1697 (inet-listener-configuration ,inet-listener-configuration-fields)
1698 (namespace-configuration
1699 ,namespace-configuration-fields
1700 (mailboxes mailbox-configuration))
1701 (mailbox-configuration ,mailbox-configuration-fields)
1702 (service-configuration
1703 ,service-configuration-fields
1704 (listeners unix-listener-configuration fifo-listener-configuration
1705 inet-listener-configuration))
1706 (protocol-configuration ,protocol-configuration-fields))
1707 'dovecot-configuration))
1708
1709
1710;;;
1711;;; OpenSMTPD.
1712;;;
1713
1714(define-record-type* <opensmtpd-configuration>
1715 opensmtpd-configuration make-opensmtpd-configuration
1716 opensmtpd-configuration?
1717 (package opensmtpd-configuration-package
1718 (default opensmtpd))
1719 (shepherd-requirement opensmtpd-configuration-shepherd-requirement
1720 (default '())) ; list of symbols
1721 (config-file opensmtpd-configuration-config-file
1722 (default %default-opensmtpd-config-file))
1723 (log-file opensmtpd-configuration-log-file
1724 (default "/var/log/mail.log"))
1725 (setgid-commands? opensmtpd-setgid-commands? (default #t)))
1726
1727(define %default-opensmtpd-config-file
1728 (plain-file "smtpd.conf" "
1729listen on lo
1730
1731action inbound mbox
1732match for local action inbound
1733
1734action outbound relay
1735match from local for any action outbound
1736"))
1737
1738(define (opensmtpd-shepherd-service config)
1739 (match-record config <opensmtpd-configuration>
1740 (package config-file log-file shepherd-requirement)
1741 (list (shepherd-service
1742 (provision '(smtpd))
1743 (requirement `(pam loopback ,@shepherd-requirement))
1744 (documentation "Run the OpenSMTPD daemon.")
1745 (start (let ((smtpd (file-append package "/sbin/smtpd")))
1746 #~(make-forkexec-constructor
1747 (list #$smtpd "-f" #$config-file
1748 #$@(if log-file '("-d") '()))
1749 #$@(if log-file
1750 (list #:log-file log-file)
1751 (list #:pid-file "/var/run/smtpd.pid")))))
1752 (stop #~(make-kill-destructor))))))
1753
1754(define %opensmtpd-accounts
1755 (list (user-group
1756 (name "smtpq")
1757 (system? #t))
1758 (user-account
1759 (name "smtpd")
1760 (group "nogroup")
1761 (system? #t)
1762 (comment "SMTP Daemon")
1763 (home-directory "/var/empty")
1764 (shell (file-append shadow "/sbin/nologin")))
1765 (user-account
1766 (name "smtpq")
1767 (group "smtpq")
1768 (system? #t)
1769 (comment "SMTPD Queue")
1770 (home-directory "/var/empty")
1771 (shell (file-append shadow "/sbin/nologin")))))
1772
1773(define (opensmtpd-activation config)
1774 (match-record config <opensmtpd-configuration> (package config-file)
1775 (let ((smtpd (file-append package "/sbin/smtpd")))
1776 #~(begin
1777 (use-modules (guix build utils))
1778 ;; Create mbox and spool directories.
1779 (mkdir-p "/var/mail")
1780 (mkdir-p "/var/spool/smtpd")
1781 (chmod "/var/spool/smtpd" #o711)
1782 (mkdir-p "/var/spool/mail")
1783 (chmod "/var/spool/mail" #o711)))))
1784
1785(define %opensmtpd-pam-services
1786 (list (unix-pam-service "smtpd")))
1787
1788(define (opensmtpd-privileged-programs config)
1789 (match-record config <opensmtpd-configuration> (package config-file setgid-commands?)
1790 (append
1791 (list
1792 (privileged-program
1793 (program (file-append package "/libexec/opensmtpd/lockspool"))
1794 (setuid? #t)))
1795 (if setgid-commands?
1796 (map (lambda (command)
1797 (privileged-program
1798 (program (file-append package "/" command))
1799 (setgid? #t)
1800 (group "smtpq")))
1801 (list "sbin/smtpctl"
1802
1803 ;; Also privilege the compatibility symlinks created by
1804 ;; the Guix opensmtpd package; all synonyms for smtpctl.
1805 "sbin/mailq"
1806 "sbin/makemap"
1807 "sbin/newaliases"
1808 "sbin/sendmail"
1809 "sbin/send-mail"))
1810 '()))))
1811
1812(define opensmtpd-service-type
1813 (service-type
1814 (name 'opensmtpd)
1815 (extensions
1816 (list (service-extension account-service-type
1817 (const %opensmtpd-accounts))
1818 (service-extension activation-service-type
1819 opensmtpd-activation)
1820 (service-extension pam-root-service-type
1821 (const %opensmtpd-pam-services))
1822 (service-extension profile-service-type
1823 (compose list opensmtpd-configuration-package))
1824 (service-extension shepherd-root-service-type
1825 opensmtpd-shepherd-service)
1826 (service-extension privileged-program-service-type
1827 opensmtpd-privileged-programs)))
1828 (description "Run the OpenSMTPD, a lightweight @acronym{SMTP, Simple Mail
1829Transfer Protocol} server.")))
1830
1831
1832;;;
1833;;; mail aliases.
1834;;;
1835
1836(define (mail-aliases-etc aliases)
1837 `(("aliases" ,(plain-file "aliases"
1838 ;; Ideally we'd use a format string like
1839 ;; "~:{~a: ~{~a~^,~}\n~}", but it gives a
1840 ;; warning that I can't figure out how to fix,
1841 ;; so we'll just use string-join below instead.
1842 (format #f "~:{~a: ~a\n~}"
1843 (map (match-lambda
1844 ((alias addresses ...)
1845 (list alias (string-join addresses ","))))
1846 aliases))))))
1847
1848(define mail-aliases-service-type
1849 (service-type
1850 (name 'mail-aliases)
1851 (extensions
1852 (list (service-extension etc-service-type mail-aliases-etc)))
1853 (compose concatenate)
1854 (extend append)
1855 (description "Provide a @file{/etc/aliases} file---an email alias
1856database---computed from the given alias list.")))
1857
1858
1859;;;
1860;;; Exim.
1861;;;
1862
1863(define-record-type* <exim-configuration> exim-configuration
1864 make-exim-configuration
1865 exim-configuration?
1866 (package exim-configuration-package ;file-like
1867 (default exim))
1868 (config-file exim-configuration-config-file ;file-like
1869 (default #f)))
1870
1871(define %exim-accounts
1872 (list (user-group
1873 (name "exim")
1874 (system? #t))
1875 (user-account
1876 (name "exim")
1877 (group "exim")
1878 (system? #t)
1879 (comment "Exim Daemon")
1880 (home-directory "/var/empty")
1881 (shell (file-append shadow "/sbin/nologin")))))
1882
1883(define (exim-computed-config-file package config-file)
1884 (computed-file "exim.conf"
1885 #~(call-with-output-file #$output
1886 (lambda (port)
1887 (format port "
1888exim_user = exim
1889exim_group = exim
1890.include ~a"
1891 #$(or config-file
1892 (file-append package "/etc/exim.conf")))))))
1893
1894(define exim-shepherd-service
1895 (match-lambda
1896 (($ <exim-configuration> package config-file)
1897 (list (shepherd-service
1898 (provision '(exim mta))
1899 (documentation "Run the exim daemon.")
1900 (requirement '(networking))
1901 (start #~(make-forkexec-constructor
1902 '(#$(file-append package "/bin/exim")
1903 "-bd" "-v" "-C"
1904 #$(exim-computed-config-file package config-file))))
1905 (stop #~(make-kill-destructor)))))))
1906
1907(define exim-activation
1908 (match-lambda
1909 (($ <exim-configuration> package config-file)
1910 (with-imported-modules '((guix build utils))
1911 #~(begin
1912 (use-modules (guix build utils))
1913
1914 (let ((uid (passwd:uid (getpw "exim")))
1915 (gid (group:gid (getgr "exim"))))
1916 (mkdir-p "/var/spool/exim")
1917 (chown "/var/spool/exim" uid gid))
1918
1919 (zero? (system* #$(file-append package "/bin/exim")
1920 "-bV" "-C" #$(exim-computed-config-file package config-file))))))))
1921
1922(define exim-profile
1923 (compose list exim-configuration-package))
1924
1925(define exim-service-type
1926 (service-type
1927 (name 'exim)
1928 (extensions
1929 (list (service-extension shepherd-root-service-type exim-shepherd-service)
1930 (service-extension account-service-type (const %exim-accounts))
1931 (service-extension activation-service-type exim-activation)
1932 (service-extension profile-service-type exim-profile)
1933 (service-extension mail-aliases-service-type (const '()))))
1934 (description "Run the Exim mail transfer agent (MTA).")))
1935
1936
1937;;;
1938;;; GNU Mailutils IMAP4 Daemon.
1939;;;
1940
1941(define %default-imap4d-config-file
1942 (plain-file "imap4d.conf" "server localhost {};\n"))
1943
1944(define-record-type* <imap4d-configuration>
1945 imap4d-configuration make-imap4d-configuration imap4d-configuration?
1946 (package imap4d-configuration-package
1947 (default mailutils))
1948 (config-file imap4d-configuration-config-file
1949 (default %default-imap4d-config-file)))
1950
1951(define imap4d-shepherd-service
1952 (match-lambda
1953 (($ <imap4d-configuration> package config-file)
1954 (list (shepherd-service
1955 (provision '(imap4d))
1956 (requirement '(user-processes networking syslogd))
1957 (documentation "Run the imap4d daemon.")
1958 (start (let ((imap4d (file-append package "/sbin/imap4d")))
1959 #~(make-forkexec-constructor
1960 (list #$imap4d "--daemon" "--foreground"
1961 "--config-file" #$config-file))))
1962 (stop #~(make-kill-destructor)))))))
1963
1964(define imap4d-service-type
1965 (service-type
1966 (name 'imap4d)
1967 (description
1968 "Run the GNU @command{imap4d} to serve e-mail messages through IMAP.")
1969 (extensions
1970 (list (service-extension
1971 shepherd-root-service-type imap4d-shepherd-service)))
1972 (default-value (imap4d-configuration))))
1973
1974
1975;;;
1976;;; Radicale.
1977;;;
1978
1979;; Maybe types
1980
1981(define (comma-separated-ip-list? lst)
1982 (every (lambda (s)
1983 (or (string-prefix? "localhost" s)
1984 ((@@ (gnu services vpn) ipv4-address?) s)
1985 ((@@ (gnu services vpn) ipv6-address?) s)))
1986 lst))
1987
1988(define-maybe boolean (prefix radicale-))
1989(define-maybe comma-separated-ip-list (prefix radicale-))
1990(define-maybe file-name (prefix radicale-))
1991(define-maybe non-negative-integer (prefix radicale-))
1992(define-maybe string (prefix radicale-))
1993(define-maybe symbol (prefix radicale-))
1994
1995;; Serializers and sanitizers
1996
1997(define (radicale-serialize-field field-name value)
1998 ;; XXX We quote the un-gexp form here because otherwise symbol-literals are
1999 ;; treated as variables. We can get away with this because all of our other
2000 ;; field value types are primitives by the time they get here so are printed
2001 ;; the same whether or not they are quoted.
2002 #~(format #f "~a = ~a\n" #$(uglify-field-name field-name) '#$value))
2003
2004(define (radicale-serialize-boolean field-name value?)
2005 (radicale-serialize-field field-name (if value? "True" "False")))
2006
2007(define (radicale-serialize-comma-separated-ip-list field-name value)
2008 (radicale-serialize-field field-name (string-join value ", ")))
2009
2010(define radicale-serialize-file-name radicale-serialize-field)
2011
2012(define radicale-serialize-non-negative-integer radicale-serialize-field)
2013
2014(define radicale-serialize-string radicale-serialize-field)
2015
2016(define radicale-serialize-symbol radicale-serialize-field)
2017
2018(define ((sanitize-delimited-symbols syms location field) value)
2019 (cond
2020 ((not (maybe-value-set? value))
2021 value)
2022 ((member value syms)
2023 (string->symbol (uglify-field-name value)))
2024 (else
2025 (configuration-field-error (source-properties->location location)
2026 field
2027 value))))
2028
2029;; Section configuration types
2030
2031(define-configuration radicale-auth-configuration
2032 (type
2033 maybe-symbol
2034 "The method to verify usernames and passwords. Options are @code{none},
2035@code{htpasswd}, @code{remote-user}, and @code{http-x-remote-user}.
2036
2037This value is tied to @code{htpasswd-filename} and @code{htpasswd-encryption}."
2038 (sanitizer
2039 (sanitize-delimited-symbols '(none htpasswd remote-user http-x-remote-user)
2040 (current-source-location)
2041 'type)))
2042 (htpasswd-filename
2043 maybe-file-name
2044 "Path to the htpasswd file. Use htpasswd or similar to generate this file.")
2045 (htpasswd-encryption
2046 maybe-symbol
2047 "Encryption method used in the htpasswd file. Options are @code{plain},
2048@code{bcrypt}, and @code{md5}."
2049 (sanitizer
2050 (sanitize-delimited-symbols '(plain bcrypt md5)
2051 (current-source-location)
2052 'htpasswd-encryption)))
2053 (delay
2054 maybe-non-negative-integer
2055 "Average delay after failed login attempts in seconds.")
2056 (realm
2057 maybe-string
2058 "Message displayed in the client when a password is needed.")
2059 (prefix radicale-))
2060
2061(define-configuration radicale-encoding-configuration
2062 (request
2063 maybe-symbol
2064 "Encoding for responding requests.")
2065 (stock
2066 maybe-symbol
2067 "Encoding for storing local collections.")
2068 (prefix radicale-))
2069
2070(define-configuration radicale-logging-configuration
2071 (level
2072 maybe-symbol
2073 "Set the logging level. One of @code{debug}, @code{info}, @code{warning},
2074@code{error}, or @code{critical}."
2075 (sanitizer (sanitize-delimited-symbols '(debug info warning error critical)
2076 (current-source-location)
2077 'level)))
2078 (mask-passwords?
2079 maybe-boolean
2080 "Whether to include passwords in logs.")
2081 (prefix radicale-))
2082
2083(define-configuration radicale-rights-configuration
2084 (type
2085 maybe-symbol
2086 "Backend used to check collection access rights. The recommended backend is
2087@code{owner-only}. If access to calendars and address books outside the home
2088directory of users is granted, clients won't detect these collections and will
2089not show them to the user. Choosing any other method is only useful if you
2090access calendars and address books directly via URL. Options are
2091@code{authenticate}, @code{owner-only}, @code{owner-write}, and
2092@code{from-file}."
2093 (sanitizer
2094 (sanitize-delimited-symbols '(authenticate owner-only owner-write from-file)
2095 (current-source-location)
2096 'type)))
2097 (file
2098 maybe-file-name
2099 "File for the rights backend @code{from-file}.")
2100 (prefix radicale-))
2101
2102(define-configuration radicale-server-configuration
2103 (hosts
2104 maybe-comma-separated-ip-list
2105 "List of IP addresses that the server will bind to.")
2106 (max-connections
2107 maybe-non-negative-integer
2108 "Maximum number of parallel connections. Set to 0 to disable the limit.")
2109 (max-content-length
2110 maybe-non-negative-integer
2111 "Maximum size of the request body in byetes.")
2112 (timeout
2113 maybe-non-negative-integer
2114 "Socket timeout in seconds.")
2115 (ssl?
2116 maybe-boolean
2117 "Whether to enable transport layer encryption.")
2118 (certificate
2119 maybe-file-name
2120 "Path of the SSL certificate.")
2121 (key
2122 maybe-file-name
2123 "Path to the private key for SSL. Only effective if @code{ssl?} is
2124@code{#t}.")
2125 (certificate-authority
2126 maybe-file-name
2127 "Path to CA certificate for validating client certificates. This can be used
2128to secure TCP traffic between Radicale and a reverse proxy. If you want to
2129authenticate users with client-side certificates, you also have to write an
2130authentication plugin that extracts the username from the certificate.")
2131 (prefix radicale-))
2132
2133(define-configuration radicale-storage-configuration
2134 (type
2135 maybe-symbol
2136 "Backend used to store data. Options are @code{multifilesystem} and
2137@code{multifilesystem-nolock}."
2138 (sanitizer
2139 (sanitize-delimited-symbols '(multifilesystem multifilesystem-nolock)
2140 (current-source-location)
2141 'type)))
2142 (filesystem-folder
2143 maybe-file-name
2144 "Folder for storing local collections. Created if not present.")
2145 (max-sync-token-age
2146 maybe-non-negative-integer
2147 "Delete sync-tokens that are older than the specified time in seconds.")
2148 (hook
2149 maybe-string
2150 "Command run after changes to storage.")
2151 (prefix radicale-))
2152
2153;; Helpers for using section configurations in the main configuration
2154
2155;; XXX These indirections are necessary to avoid creating semantic ambiguity
2156(define auth-config? radicale-auth-configuration?)
2157(define encoding-config? radicale-encoding-configuration?)
2158(define headers-file? file-like?)
2159(define logging-config? radicale-logging-configuration?)
2160(define rights-config? radicale-rights-configuration?)
2161(define server-config? radicale-server-configuration?)
2162(define storage-config? radicale-storage-configuration?)
2163
2164(define-maybe auth-config)
2165(define-maybe encoding-config)
2166(define-maybe headers-file)
2167(define-maybe logging-config)
2168(define-maybe rights-config)
2169(define-maybe server-config)
2170(define-maybe storage-config)
2171
2172(define ((serialize-radicale-section fields) name cfg)
2173 #~(format #f "[~a]\n~a\n" '#$name #$(serialize-configuration cfg fields)))
2174
2175(define serialize-auth-config
2176 (serialize-radicale-section radicale-auth-configuration-fields))
2177(define serialize-encoding-config
2178 (serialize-radicale-section radicale-encoding-configuration-fields))
2179(define serialize-logging-config
2180 (serialize-radicale-section radicale-logging-configuration-fields))
2181(define serialize-rights-config
2182 (serialize-radicale-section radicale-rights-configuration-fields))
2183(define serialize-server-config
2184 (serialize-radicale-section radicale-server-configuration-fields))
2185(define serialize-storage-config
2186 (serialize-radicale-section radicale-storage-configuration-fields))
2187
2188(define (serialize-radicale-configuration cfg)
2189 (mixed-text-file
2190 "radicale.conf"
2191 (serialize-configuration cfg radicale-configuration-fields)))
2192
2193(define-configuration radicale-configuration
2194 ;; Only fields whose default value does not match upstream are not maybe-types
2195 (package
2196 (file-like radicale)
2197 "Package that provides @command{radicale}.")
2198 (auth
2199 maybe-auth-config
2200 "Configuration for auth-related variables.")
2201 (encoding
2202 maybe-encoding-config
2203 "Configuration for encoding-related variables.")
2204 (headers-file
2205 maybe-headers-file
2206 "Custom HTTP headers."
2207 (serializer
2208 (lambda (field-name value)
2209 #~(begin
2210 (use-modules (ice-9 rdelim))
2211 (format #f "[headers]\n~a\n\n"
2212 (with-input-from-file #$value read-string))))))
2213 (logging
2214 maybe-logging-config
2215 "Configuration for logging-related variables.")
2216 (rights
2217 maybe-rights-config
2218 "Configuration for rights-related variables.")
2219 (server
2220 maybe-server-config
2221 "Configuration for server-related variables. Ignored if WSGI is used.")
2222 (storage
2223 maybe-storage-config
2224 "Configuration for storage-related variables.")
2225 (web-interface?
2226 maybe-boolean
2227 "Whether to use Radicale's built-in web interface."
2228 (serializer
2229 (lambda (_ use?)
2230 #~(format #f "[web]\ntype = ~a\n\n" #$(if use? "internal" "none"))))))
2231
2232(define %radicale-accounts
2233 (list (user-group
2234 (name "radicale")
2235 (system? #t))
2236 (user-account
2237 (name "radicale")
2238 (group "radicale")
2239 (system? #t)
2240 (comment "Radicale Daemon")
2241 (home-directory "/var/empty")
2242 (shell (file-append shadow "/sbin/nologin")))))
2243
2244(define (radicale-shepherd-service cfg)
2245 (define config-file
2246 (serialize-radicale-configuration cfg))
2247
2248 (list (shepherd-service
2249 (provision '(radicale))
2250 (documentation "Run the radicale daemon.")
2251 (requirement '(user-processes networking))
2252 (start #~(make-forkexec-constructor
2253 (list #$(file-append (radicale-configuration-package cfg)
2254 "/bin/radicale")
2255 "-C" #$config-file)
2256 #:user "radicale"
2257 #:group "radicale"))
2258 (stop #~(make-kill-destructor))
2259 (actions
2260 (list
2261 (shepherd-configuration-action config-file))))))
2262
2263(define radicale-activation
2264 (match-lambda
2265 (($ <radicale-configuration> _ auth-config _ _ _ _ _ storage-config _)
2266 ;; Get values for the collections directory
2267 ;; See https://radicale.org/v3.html#running-as-a-service
2268 (define filesystem-folder-val
2269 (if (maybe-value-set? storage-config)
2270 (radicale-storage-configuration-filesystem-folder storage-config)
2271 storage-config))
2272 (define collections-dir
2273 (if (maybe-value-set? filesystem-folder-val)
2274 filesystem-folder-val
2275 "/var/lib/radicale/collections"))
2276 (define collections-parent-dir (dirname collections-dir))
2277 ;; Get values for the password file directory
2278 (define auth-value-set? (maybe-value-set? auth-config))
2279 ;; If auth's type is 'none or unset, that means there is no authentication
2280 ;; and we don't need to setup files for it
2281 (define auth?
2282 (and auth-value-set?
2283 (not (eq? (radicale-auth-configuration-type auth-config) 'none))))
2284 (define password-file-val
2285 (if auth-value-set?
2286 (radicale-auth-configuration-htpasswd-filename auth-config)
2287 auth-config))
2288 (define password-file-dir
2289 (if (maybe-value-set? password-file-val)
2290 (dirname password-file-val)
2291 "/etc/radicale"))
2292 (with-imported-modules '((guix build utils))
2293 #~(begin
2294 (use-modules (guix build utils))
2295 (let ((user (getpwnam "radicale")))
2296 ;; Collections directory perms
2297 (mkdir-p/perms #$collections-dir user #o700)
2298 ;; Password file perms
2299 (when #$auth?
2300 ;; In theory, the password file and thus this directory should already
2301 ;; exist because the user has to make them by hand
2302 (mkdir-p/perms #$password-file-dir user #o700))))))))
2303
2304(define radicale-service-type
2305 (service-type
2306 (name 'radicale)
2307 (description "Run Radicale, a small CalDAV and CardDAV server.")
2308 (extensions
2309 (list (service-extension shepherd-root-service-type radicale-shepherd-service)
2310 (service-extension account-service-type (const %radicale-accounts))
2311 (service-extension activation-service-type radicale-activation)))
2312 (default-value (radicale-configuration))))
2313
2314(define (generate-radicale-documentation)
2315 (generate-documentation
2316 `((radicale-configuration
2317 ,radicale-configuration-fields
2318 (auth radicale-auth-configuration)
2319 (encoding radicale-encoding-configuration)
2320 (logging radicale-logging-configuration)
2321 (rights radicale-rights-configuration)
2322 (server radicale-server-configuration)
2323 (storage radicale-storage-configuration))
2324 (radicale-auth-configuration ,radicale-auth-configuration-fields)
2325 (radicale-encoding-configuration ,radicale-encoding-configuration-fields)
2326 (radicale-logging-configuration ,radicale-logging-configuration-fields)
2327 (radicale-rights-configuration ,radicale-rights-configuration-fields)
2328 (radicale-server-configuration ,radicale-server-configuration-fields)
2329 (radicale-storage-configuration ,radicale-storage-configuration-fields))
2330 'radicale-configuration))
2331
2332;;;
2333;;; Rspamd.
2334;;;
2335
2336(define (directory-tree? xs)
2337 (match xs
2338 ((((? string?) (? file-like?)) ...) #t)
2339 (_ #f)))
2340
2341(define-configuration/no-serialization rspamd-configuration
2342 (package
2343 (file-like rspamd)
2344 "The package that provides rspamd.")
2345 (config-file
2346 (file-like %default-rspamd-config-file)
2347 "File-like object of the configuration file to use. By default
2348all workers are enabled except fuzzy and they are binded
2349to their usual ports, e.g localhost:11334, localhost:11333 and so on")
2350 (local.d-files
2351 (directory-tree '())
2352 "Configuration files in local.d, provided as a list of two element lists where
2353the first element is the filename and the second one is a file-like object. Settings
2354in these files will be merged with the defaults.")
2355 (override.d-files
2356 (directory-tree '())
2357 "Configuration files in override.d, provided as a list of two element lists where
2358the first element is the filename and the second one is a file-like object. Settings
2359in these files will override the defaults.")
2360 (user
2361 (user-account %default-rspamd-account)
2362 "The user to run rspamd as.")
2363 (group
2364 (user-group %default-rspamd-group)
2365 "The group to run rspamd as.")
2366 (debug?
2367 (boolean #f)
2368 "Force debug output.")
2369 (insecure?
2370 (boolean #f)
2371 "Ignore running workers as privileged users.")
2372 (skip-template?
2373 (boolean #f)
2374 "Do not apply Jinja templates.")
2375 (shepherd-requirements
2376 (list-of-symbols '(user-processes loopback))
2377 "This is a list of symbols naming Shepherd services that this service
2378will depend on."))
2379
2380(define %default-rspamd-account
2381 (user-account
2382 (name "rspamd")
2383 (group "rspamd")
2384 (system? #t)
2385 (comment "Rspamd daemon")
2386 (home-directory "/var/empty")
2387 (shell (file-append shadow "/sbin/nologin"))))
2388
2389(define %default-rspamd-group
2390 (user-group
2391 (name "rspamd")
2392 (system? #t)))
2393
2394(define %default-rspamd-config-file
2395 (plain-file "rspamd.conf" "
2396.include \"$CONFDIR/common.conf\"
2397
2398options {
2399 pidfile = \"$RUNDIR/rspamd.pid\";
2400 .include \"$CONFDIR/options.inc\"
2401 .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/options.inc\"
2402 .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/options.inc\"
2403}
2404
2405logging {
2406 type = \"file\";
2407 filename = \"$LOGDIR/rspamd.log\";
2408 .include \"$CONFDIR/logging.inc\"
2409 .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/logging.inc\"
2410 .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/logging.inc\"
2411}
2412
2413worker \"normal\" {
2414 bind_socket = \"localhost:11333\";
2415 .include \"$CONFDIR/worker-normal.inc\"
2416 .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-normal.inc\"
2417 .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-normal.inc\"
2418}
2419
2420worker \"controller\" {
2421 bind_socket = \"localhost:11334\";
2422 .include \"$CONFDIR/worker-controller.inc\"
2423 .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-controller.inc\"
2424 .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-controller.inc\"
2425}
2426
2427worker \"rspamd_proxy\" {
2428 bind_socket = \"localhost:11332\";
2429 .include \"$CONFDIR/worker-proxy.inc\"
2430 .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-proxy.inc\"
2431 .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-proxy.inc\"
2432}
2433
2434# Local fuzzy storage is disabled by default
2435
2436worker \"fuzzy\" {
2437 bind_socket = \"localhost:11335\";
2438 count = -1; # Disable by default
2439 .include \"$CONFDIR/worker-fuzzy.inc\"
2440 .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-fuzzy.inc\"
2441 .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-fuzzy.inc\"
2442}
2443"))
2444
2445(define (rspamd-accounts config)
2446 (match-record config <rspamd-configuration>
2447 (user group)
2448 (list group user)))
2449
2450(define (rspamd-shepherd-service config)
2451 (match-record config <rspamd-configuration>
2452 (package config-file user group debug? insecure? skip-template?
2453 local.d-files override.d-files shepherd-requirements)
2454 (list
2455 (shepherd-service
2456 (provision '(rspamd))
2457 (documentation "Run the rspamd daemon.")
2458 (requirement shepherd-requirements)
2459 (start (let ((rspamd (file-append package "/bin/rspamd"))
2460 (local-confdir
2461 (file-union
2462 "rspamd-local-confdir"
2463 `(("local.d" ,(file-union "local.d" local.d-files))
2464 ("override.d" ,(file-union "override.d" override.d-files))))))
2465 (with-imported-modules (source-module-closure '((gnu build activation)))
2466 #~(begin
2467 (use-modules (gnu build activation)) ; for mkdir-p/perms
2468 (let ((user (getpwnam #$(user-account-name user))))
2469 (mkdir-p/perms "/var/run/rspamd" user #o755)
2470 (mkdir-p/perms "/var/log/rspamd" user #o755)
2471 (mkdir-p/perms "/var/lib/rspamd" user #o755))
2472 (make-forkexec-constructor
2473 (list #$rspamd "--config" #$config-file
2474 "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
2475 "--no-fork"
2476 #$@(if debug?
2477 '("--debug")
2478 '())
2479 #$@(if insecure?
2480 '("--insecure")
2481 '())
2482 #$@(if skip-template?
2483 '("--skip-template")
2484 '()))
2485 #:user #$(user-account-name user)
2486 #:group #$(user-group-name group))))))
2487 (stop #~(make-kill-destructor))
2488 (actions
2489 (list
2490 (shepherd-configuration-action config-file)
2491 (shepherd-signal-action
2492 'reload SIGHUP
2493 #:documentation "Reload rspamd."
2494 #:message "Service rspamd has been reloaded")
2495 (shepherd-signal-action
2496 'reopen SIGUSR1
2497 #:documentation "Reopen log files."
2498 #:message "Reopening the logs for rspamd.")))))))
2499
2500(define rspamd-service-type
2501 (service-type
2502 (name 'rspamd)
2503 (description "Run the rapid spam filtering system.")
2504 (extensions
2505 (list
2506 (service-extension shepherd-root-service-type rspamd-shepherd-service)
2507 (service-extension account-service-type rspamd-accounts)))
2508 (default-value (rspamd-configuration))))