mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-12-15 02:03:01 +00:00
677 lines
24 KiB
Nix
677 lines
24 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
inherit (lib) attrValues concatMapStringsSep concatStrings
|
|
concatStringsSep flatten imap1 literalExpression mapAttrsToList
|
|
mkEnableOption mkIf mkOption mkRemovedOptionModule optional optionalAttrs
|
|
optionalString singleton types mkRenamedOptionModule nameValuePair
|
|
mapAttrs' listToAttrs filter;
|
|
inherit (lib.strings) match;
|
|
|
|
cfg = config.services.dovecot2;
|
|
dovecotPkg = pkgs.dovecot;
|
|
|
|
baseDir = "/run/dovecot2";
|
|
stateDir = "/var/lib/dovecot";
|
|
|
|
sieveScriptSettings = mapAttrs' (to: _: nameValuePair "sieve_${to}" "${stateDir}/sieve/${to}") cfg.sieve.scripts;
|
|
imapSieveMailboxSettings = listToAttrs (flatten (imap1 (idx: el:
|
|
singleton {
|
|
name = "imapsieve_mailbox${toString idx}_name";
|
|
value = el.name;
|
|
} ++ optional (el.from != null) {
|
|
name = "imapsieve_mailbox${toString idx}_from";
|
|
value = el.from;
|
|
} ++ optional (el.causes != []) {
|
|
name = "imapsieve_mailbox${toString idx}_causes";
|
|
value = concatStringsSep "," el.causes;
|
|
} ++ optional (el.before != null) {
|
|
name = "imapsieve_mailbox${toString idx}_before";
|
|
value = "file:${stateDir}/imapsieve/before/${baseNameOf el.before}";
|
|
} ++ optional (el.after != null) {
|
|
name = "imapsieve_mailbox${toString idx}_after";
|
|
value = "file:${stateDir}/imapsieve/after/${baseNameOf el.after}";
|
|
}
|
|
) cfg.imapsieve.mailbox));
|
|
|
|
mkExtraConfigCollisionWarning = term: ''
|
|
You referred to ${term} in `services.dovecot2.extraConfig`.
|
|
|
|
Due to gradual transition to structured configuration for plugin configuration, it is possible
|
|
this will cause your plugin configuration to be ignored.
|
|
|
|
Consider setting `services.dovecot2.pluginSettings.${term}` instead.
|
|
'';
|
|
|
|
# Those settings are automatically set based on other parts
|
|
# of this module.
|
|
automaticallySetPluginSettings = [
|
|
"sieve_plugins"
|
|
"sieve_extensions"
|
|
"sieve_global_extensions"
|
|
"sieve_pipe_bin_dir"
|
|
]
|
|
++ (builtins.attrNames sieveScriptSettings)
|
|
++ (builtins.attrNames imapSieveMailboxSettings);
|
|
|
|
# The idea is to match everything that looks like `$term =`
|
|
# but not `# $term something something`
|
|
# or `# $term = some value` because those are comments.
|
|
configContainsSetting = lines: term: (match "^[^#]*\b${term}\b.*=" lines) != null;
|
|
|
|
warnAboutExtraConfigCollisions = map mkExtraConfigCollisionWarning (filter (configContainsSetting cfg.extraConfig) automaticallySetPluginSettings);
|
|
|
|
sievePipeBinScriptDirectory = pkgs.linkFarm "sieve-pipe-bins" (map (el: {
|
|
name = builtins.unsafeDiscardStringContext (baseNameOf el);
|
|
path = el;
|
|
}) cfg.sieve.pipeBins);
|
|
|
|
dovecotConf = concatStrings [
|
|
''
|
|
base_dir = ${baseDir}
|
|
protocols = ${concatStringsSep " " cfg.protocols}
|
|
sendmail_path = /run/wrappers/bin/sendmail
|
|
# defining mail_plugins must be done before the first protocol {} filter because of https://doc.dovecot.org/configuration_manual/config_file/config_file_syntax/#variable-expansion
|
|
mail_plugins = $mail_plugins ${concatStringsSep " " cfg.mailPlugins.globally.enable}
|
|
''
|
|
|
|
(
|
|
concatStringsSep "\n" (
|
|
mapAttrsToList (
|
|
protocol: plugins: ''
|
|
protocol ${protocol} {
|
|
mail_plugins = $mail_plugins ${concatStringsSep " " plugins.enable}
|
|
}
|
|
''
|
|
) cfg.mailPlugins.perProtocol
|
|
)
|
|
)
|
|
|
|
(
|
|
if cfg.sslServerCert == null then ''
|
|
ssl = no
|
|
disable_plaintext_auth = no
|
|
'' else ''
|
|
ssl_cert = <${cfg.sslServerCert}
|
|
ssl_key = <${cfg.sslServerKey}
|
|
${optionalString (cfg.sslCACert != null) ("ssl_ca = <" + cfg.sslCACert)}
|
|
${optionalString cfg.enableDHE ''ssl_dh = <${config.security.dhparams.params.dovecot2.path}''}
|
|
disable_plaintext_auth = yes
|
|
''
|
|
)
|
|
|
|
''
|
|
default_internal_user = ${cfg.user}
|
|
default_internal_group = ${cfg.group}
|
|
${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"}
|
|
${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"}
|
|
|
|
mail_location = ${cfg.mailLocation}
|
|
|
|
maildir_copy_with_hardlinks = yes
|
|
pop3_uidl_format = %08Xv%08Xu
|
|
|
|
auth_mechanisms = plain login
|
|
|
|
service auth {
|
|
user = root
|
|
}
|
|
''
|
|
|
|
(
|
|
optionalString cfg.enablePAM ''
|
|
userdb {
|
|
driver = passwd
|
|
}
|
|
|
|
passdb {
|
|
driver = pam
|
|
args = ${optionalString cfg.showPAMFailure "failure_show_msg=yes"} dovecot2
|
|
}
|
|
''
|
|
)
|
|
|
|
(
|
|
optionalString (cfg.mailboxes != {}) ''
|
|
namespace inbox {
|
|
inbox=yes
|
|
${concatStringsSep "\n" (map mailboxConfig (attrValues cfg.mailboxes))}
|
|
}
|
|
''
|
|
)
|
|
|
|
(
|
|
optionalString cfg.enableQuota ''
|
|
service quota-status {
|
|
executable = ${dovecotPkg}/libexec/dovecot/quota-status -p postfix
|
|
inet_listener {
|
|
port = ${cfg.quotaPort}
|
|
}
|
|
client_limit = 1
|
|
}
|
|
|
|
plugin {
|
|
quota_rule = *:storage=${cfg.quotaGlobalPerUser}
|
|
quota = count:User quota # per virtual mail user quota
|
|
quota_status_success = DUNNO
|
|
quota_status_nouser = DUNNO
|
|
quota_status_overquota = "552 5.2.2 Mailbox is full"
|
|
quota_grace = 10%%
|
|
quota_vsizes = yes
|
|
}
|
|
''
|
|
)
|
|
|
|
# General plugin settings:
|
|
# - sieve is mostly generated here, refer to `pluginSettings` to follow
|
|
# the control flow.
|
|
''
|
|
plugin {
|
|
${concatStringsSep "\n" (mapAttrsToList (key: value: " ${key} = ${value}") cfg.pluginSettings)}
|
|
}
|
|
''
|
|
|
|
cfg.extraConfig
|
|
];
|
|
|
|
modulesDir = pkgs.symlinkJoin {
|
|
name = "dovecot-modules";
|
|
paths = map (pkg: "${pkg}/lib/dovecot") ([ dovecotPkg ] ++ map (module: module.override { dovecot = dovecotPkg; }) cfg.modules);
|
|
};
|
|
|
|
mailboxConfig = mailbox: ''
|
|
mailbox "${mailbox.name}" {
|
|
auto = ${toString mailbox.auto}
|
|
'' + optionalString (mailbox.autoexpunge != null) ''
|
|
autoexpunge = ${mailbox.autoexpunge}
|
|
'' + optionalString (mailbox.specialUse != null) ''
|
|
special_use = \${toString mailbox.specialUse}
|
|
'' + "}";
|
|
|
|
mailboxes = { name, ... }: {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.strMatching ''[^"]+'';
|
|
example = "Spam";
|
|
default = name;
|
|
readOnly = true;
|
|
description = lib.mdDoc "The name of the mailbox.";
|
|
};
|
|
auto = mkOption {
|
|
type = types.enum [ "no" "create" "subscribe" ];
|
|
default = "no";
|
|
example = "subscribe";
|
|
description = lib.mdDoc "Whether to automatically create or create and subscribe to the mailbox or not.";
|
|
};
|
|
specialUse = mkOption {
|
|
type = types.nullOr (types.enum [ "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash" ]);
|
|
default = null;
|
|
example = "Junk";
|
|
description = lib.mdDoc "Null if no special use flag is set. Other than that every use flag mentioned in the RFC is valid.";
|
|
};
|
|
autoexpunge = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
example = "60d";
|
|
description = lib.mdDoc ''
|
|
To automatically remove all email from the mailbox which is older than the
|
|
specified time.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
in
|
|
{
|
|
imports = [
|
|
(mkRemovedOptionModule [ "services" "dovecot2" "package" ] "")
|
|
(mkRenamedOptionModule [ "services" "dovecot2" "sieveScripts" ] [ "services" "dovecot2" "sieve" "scripts" ])
|
|
];
|
|
|
|
options.services.dovecot2 = {
|
|
enable = mkEnableOption (lib.mdDoc "the dovecot 2.x POP3/IMAP server");
|
|
|
|
enablePop3 = mkEnableOption (lib.mdDoc "starting the POP3 listener (when Dovecot is enabled)");
|
|
|
|
enableImap = mkEnableOption (lib.mdDoc "starting the IMAP listener (when Dovecot is enabled)") // { default = true; };
|
|
|
|
enableLmtp = mkEnableOption (lib.mdDoc "starting the LMTP listener (when Dovecot is enabled)");
|
|
|
|
protocols = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = lib.mdDoc "Additional listeners to start when Dovecot is enabled.";
|
|
};
|
|
|
|
user = mkOption {
|
|
type = types.str;
|
|
default = "dovecot2";
|
|
description = lib.mdDoc "Dovecot user name.";
|
|
};
|
|
|
|
group = mkOption {
|
|
type = types.str;
|
|
default = "dovecot2";
|
|
description = lib.mdDoc "Dovecot group name.";
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
example = "mail_debug = yes";
|
|
description = lib.mdDoc "Additional entries to put verbatim into Dovecot's config file.";
|
|
};
|
|
|
|
mailPlugins =
|
|
let
|
|
plugins = hint: types.submodule {
|
|
options = {
|
|
enable = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = lib.mdDoc "mail plugins to enable as a list of strings to append to the ${hint} `$mail_plugins` configuration variable";
|
|
};
|
|
};
|
|
};
|
|
in
|
|
mkOption {
|
|
type = with types; submodule {
|
|
options = {
|
|
globally = mkOption {
|
|
description = lib.mdDoc "Additional entries to add to the mail_plugins variable for all protocols";
|
|
type = plugins "top-level";
|
|
example = { enable = [ "virtual" ]; };
|
|
default = { enable = []; };
|
|
};
|
|
perProtocol = mkOption {
|
|
description = lib.mdDoc "Additional entries to add to the mail_plugins variable, per protocol";
|
|
type = attrsOf (plugins "corresponding per-protocol");
|
|
default = {};
|
|
example = { imap = [ "imap_acl" ]; };
|
|
};
|
|
};
|
|
};
|
|
description = lib.mdDoc "Additional entries to add to the mail_plugins variable, globally and per protocol";
|
|
example = {
|
|
globally.enable = [ "acl" ];
|
|
perProtocol.imap.enable = [ "imap_acl" ];
|
|
};
|
|
default = { globally.enable = []; perProtocol = {}; };
|
|
};
|
|
|
|
configFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
description = lib.mdDoc "Config file used for the whole dovecot configuration.";
|
|
apply = v: if v != null then v else pkgs.writeText "dovecot.conf" dovecotConf;
|
|
};
|
|
|
|
mailLocation = mkOption {
|
|
type = types.str;
|
|
default = "maildir:/var/spool/mail/%u"; /* Same as inbox, as postfix */
|
|
example = "maildir:~/mail:INBOX=/var/spool/mail/%u";
|
|
description = lib.mdDoc ''
|
|
Location that dovecot will use for mail folders. Dovecot mail_location option.
|
|
'';
|
|
};
|
|
|
|
mailUser = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc "Default user to store mail for virtual users.";
|
|
};
|
|
|
|
mailGroup = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc "Default group to store mail for virtual users.";
|
|
};
|
|
|
|
createMailUser = mkEnableOption (lib.mdDoc ''automatically creating the user
|
|
given in {option}`services.dovecot.user` and the group
|
|
given in {option}`services.dovecot.group`.'') // { default = true; };
|
|
|
|
modules = mkOption {
|
|
type = types.listOf types.package;
|
|
default = [];
|
|
example = literalExpression "[ pkgs.dovecot_pigeonhole ]";
|
|
description = lib.mdDoc ''
|
|
Symlinks the contents of lib/dovecot of every given package into
|
|
/etc/dovecot/modules. This will make the given modules available
|
|
if a dovecot package with the module_dir patch applied is being used.
|
|
'';
|
|
};
|
|
|
|
sslCACert = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc "Path to the server's CA certificate key.";
|
|
};
|
|
|
|
sslServerCert = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc "Path to the server's public key.";
|
|
};
|
|
|
|
sslServerKey = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc "Path to the server's private key.";
|
|
};
|
|
|
|
enablePAM = mkEnableOption (lib.mdDoc "creating a own Dovecot PAM service and configure PAM user logins") // { default = true; };
|
|
|
|
enableDHE = mkEnableOption (lib.mdDoc "ssl_dh and generation of primes for the key exchange") // { default = true; };
|
|
|
|
showPAMFailure = mkEnableOption (lib.mdDoc "showing the PAM failure message on authentication error (useful for OTPW)");
|
|
|
|
mailboxes = mkOption {
|
|
type = with types; coercedTo
|
|
(listOf unspecified)
|
|
(list: listToAttrs (map (entry: { name = entry.name; value = removeAttrs entry ["name"]; }) list))
|
|
(attrsOf (submodule mailboxes));
|
|
default = {};
|
|
example = literalExpression ''
|
|
{
|
|
Spam = { specialUse = "Junk"; auto = "create"; };
|
|
}
|
|
'';
|
|
description = lib.mdDoc "Configure mailboxes and auto create or subscribe them.";
|
|
};
|
|
|
|
enableQuota = mkEnableOption (lib.mdDoc "the dovecot quota service");
|
|
|
|
quotaPort = mkOption {
|
|
type = types.str;
|
|
default = "12340";
|
|
description = lib.mdDoc ''
|
|
The Port the dovecot quota service binds to.
|
|
If using postfix, add check_policy_service inet:localhost:12340 to your smtpd_recipient_restrictions in your postfix config.
|
|
'';
|
|
};
|
|
quotaGlobalPerUser = mkOption {
|
|
type = types.str;
|
|
default = "100G";
|
|
example = "10G";
|
|
description = lib.mdDoc "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %.";
|
|
};
|
|
|
|
|
|
pluginSettings = mkOption {
|
|
# types.str does not coerce from packages, like `sievePipeBinScriptDirectory`.
|
|
type = types.attrsOf (types.oneOf [ types.str types.package ]);
|
|
default = {};
|
|
example = literalExpression ''
|
|
{
|
|
sieve = "file:~/sieve;active=~/.dovecot.sieve";
|
|
}
|
|
'';
|
|
description = ''
|
|
Plugin settings for dovecot in general, e.g. `sieve`, `sieve_default`, etc.
|
|
|
|
Some of the other knobs of this module will influence by default the plugin settings, but you
|
|
can still override any plugin settings.
|
|
|
|
If you override a plugin setting, its value is cleared and you have to copy over the defaults.
|
|
'';
|
|
};
|
|
|
|
imapsieve.mailbox = mkOption {
|
|
default = [];
|
|
description = "Configure Sieve filtering rules on IMAP actions";
|
|
type = types.listOf (types.submodule ({ config, ... }: {
|
|
options = {
|
|
name = mkOption {
|
|
description = ''
|
|
This setting configures the name of a mailbox for which administrator scripts are configured.
|
|
|
|
The settings defined hereafter with matching sequence numbers apply to the mailbox named by this setting.
|
|
|
|
This setting supports wildcards with a syntax compatible with the IMAP LIST command, meaning that this setting can apply to multiple or even all ("*") mailboxes.
|
|
'';
|
|
example = "Junk";
|
|
type = types.str;
|
|
};
|
|
|
|
from = mkOption {
|
|
default = null;
|
|
description = ''
|
|
Only execute the administrator Sieve scripts for the mailbox configured with services.dovecot2.imapsieve.mailbox.<name>.name when the message originates from the indicated mailbox.
|
|
|
|
This setting supports wildcards with a syntax compatible with the IMAP LIST command, meaning that this setting can apply to multiple or even all ("*") mailboxes.
|
|
'';
|
|
example = "*";
|
|
type = types.nullOr types.str;
|
|
};
|
|
|
|
causes = mkOption {
|
|
default = [ ];
|
|
description = ''
|
|
Only execute the administrator Sieve scripts for the mailbox configured with services.dovecot2.imapsieve.mailbox.<name>.name when one of the listed IMAPSIEVE causes apply.
|
|
|
|
This has no effect on the user script, which is always executed no matter the cause.
|
|
'';
|
|
example = [ "COPY" "APPEND" ];
|
|
type = types.listOf (types.enum [ "APPEND" "COPY" "FLAG" ]);
|
|
};
|
|
|
|
before = mkOption {
|
|
default = null;
|
|
description = ''
|
|
When an IMAP event of interest occurs, this sieve script is executed before any user script respectively.
|
|
|
|
This setting each specify the location of a single sieve script. The semantics of this setting is similar to sieve_before: the specified scripts form a sequence together with the user script in which the next script is only executed when an (implicit) keep action is executed.
|
|
'';
|
|
example = literalExpression "./report-spam.sieve";
|
|
type = types.nullOr types.path;
|
|
};
|
|
|
|
after = mkOption {
|
|
default = null;
|
|
description = ''
|
|
When an IMAP event of interest occurs, this sieve script is executed after any user script respectively.
|
|
|
|
This setting each specify the location of a single sieve script. The semantics of this setting is similar to sieve_after: the specified scripts form a sequence together with the user script in which the next script is only executed when an (implicit) keep action is executed.
|
|
'';
|
|
example = literalExpression "./report-spam.sieve";
|
|
type = types.nullOr types.path;
|
|
};
|
|
};
|
|
}));
|
|
};
|
|
|
|
sieve = {
|
|
plugins = mkOption {
|
|
default = [];
|
|
example = [ "sieve_extprograms" ];
|
|
description = "Sieve plugins to load";
|
|
type = types.listOf types.str;
|
|
};
|
|
|
|
extensions = mkOption {
|
|
default = [];
|
|
description = "Sieve extensions for use in user scripts";
|
|
example = [ "notify" "imapflags" "vnd.dovecot.filter" ];
|
|
type = types.listOf types.str;
|
|
};
|
|
|
|
globalExtensions = mkOption {
|
|
default = [];
|
|
example = [ "vnd.dovecot.environment" ];
|
|
description = "Sieve extensions for use in global scripts";
|
|
type = types.listOf types.str;
|
|
};
|
|
|
|
scripts = mkOption {
|
|
type = types.attrsOf types.path;
|
|
default = {};
|
|
description = lib.mdDoc "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc.";
|
|
};
|
|
|
|
pipeBins = mkOption {
|
|
default = [];
|
|
example = literalExpression ''
|
|
map lib.getExe [
|
|
(pkgs.writeShellScriptBin "learn-ham.sh" "exec ''${pkgs.rspamd}/bin/rspamc learn_ham")
|
|
(pkgs.writeShellScriptBin "learn-spam.sh" "exec ''${pkgs.rspamd}/bin/rspamc learn_spam")
|
|
]
|
|
'';
|
|
description = "Programs available for use by the vnd.dovecot.pipe extension";
|
|
type = types.listOf types.path;
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
security.pam.services.dovecot2 = mkIf cfg.enablePAM {};
|
|
|
|
security.dhparams = mkIf (cfg.sslServerCert != null && cfg.enableDHE) {
|
|
enable = true;
|
|
params.dovecot2 = {};
|
|
};
|
|
|
|
services.dovecot2 = {
|
|
protocols =
|
|
optional cfg.enableImap "imap"
|
|
++ optional cfg.enablePop3 "pop3"
|
|
++ optional cfg.enableLmtp "lmtp";
|
|
|
|
mailPlugins = mkIf cfg.enableQuota {
|
|
globally.enable = [ "quota" ];
|
|
perProtocol.imap.enable = [ "imap_quota" ];
|
|
};
|
|
|
|
sieve.plugins =
|
|
optional (cfg.imapsieve.mailbox != []) "sieve_imapsieve"
|
|
++ optional (cfg.sieve.pipeBins != []) "sieve_extprograms";
|
|
|
|
sieve.globalExtensions = optional (cfg.sieve.pipeBins != []) "vnd.dovecot.pipe";
|
|
|
|
pluginSettings = lib.mapAttrs (n: lib.mkDefault) ({
|
|
sieve_plugins = concatStringsSep " " cfg.sieve.plugins;
|
|
sieve_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions);
|
|
sieve_global_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions);
|
|
sieve_pipe_bin_dir = sievePipeBinScriptDirectory;
|
|
} // sieveScriptSettings // imapSieveMailboxSettings);
|
|
};
|
|
|
|
users.users = {
|
|
dovenull =
|
|
{
|
|
uid = config.ids.uids.dovenull2;
|
|
description = "Dovecot user for untrusted logins";
|
|
group = "dovenull";
|
|
};
|
|
} // optionalAttrs (cfg.user == "dovecot2") {
|
|
dovecot2 =
|
|
{
|
|
uid = config.ids.uids.dovecot2;
|
|
description = "Dovecot user";
|
|
group = cfg.group;
|
|
};
|
|
} // optionalAttrs (cfg.createMailUser && cfg.mailUser != null) {
|
|
${cfg.mailUser} =
|
|
{ description = "Virtual Mail User"; isSystemUser = true; } // optionalAttrs (cfg.mailGroup != null)
|
|
{ group = cfg.mailGroup; };
|
|
};
|
|
|
|
users.groups = {
|
|
dovenull.gid = config.ids.gids.dovenull2;
|
|
} // optionalAttrs (cfg.group == "dovecot2") {
|
|
dovecot2.gid = config.ids.gids.dovecot2;
|
|
} // optionalAttrs (cfg.createMailUser && cfg.mailGroup != null) {
|
|
${cfg.mailGroup} = {};
|
|
};
|
|
|
|
environment.etc."dovecot/modules".source = modulesDir;
|
|
environment.etc."dovecot/dovecot.conf".source = cfg.configFile;
|
|
|
|
systemd.services.dovecot2 = {
|
|
description = "Dovecot IMAP/POP3 server";
|
|
|
|
after = [ "network.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
restartTriggers = [ cfg.configFile modulesDir ];
|
|
|
|
startLimitIntervalSec = 60; # 1 min
|
|
serviceConfig = {
|
|
Type = "notify";
|
|
ExecStart = "${dovecotPkg}/sbin/dovecot -F";
|
|
ExecReload = "${dovecotPkg}/sbin/doveadm reload";
|
|
Restart = "on-failure";
|
|
RestartSec = "1s";
|
|
RuntimeDirectory = [ "dovecot2" ];
|
|
};
|
|
|
|
# When copying sieve scripts preserve the original time stamp
|
|
# (should be 0) so that the compiled sieve script is newer than
|
|
# the source file and Dovecot won't try to compile it.
|
|
preStart = ''
|
|
rm -rf ${stateDir}/sieve ${stateDir}/imapsieve
|
|
'' + optionalString (cfg.sieve.scripts != {}) ''
|
|
mkdir -p ${stateDir}/sieve
|
|
${concatStringsSep "\n" (
|
|
mapAttrsToList (
|
|
to: from: ''
|
|
if [ -d '${from}' ]; then
|
|
mkdir '${stateDir}/sieve/${to}'
|
|
cp -p "${from}/"*.sieve '${stateDir}/sieve/${to}'
|
|
else
|
|
cp -p '${from}' '${stateDir}/sieve/${to}'
|
|
fi
|
|
${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}'
|
|
''
|
|
) cfg.sieve.scripts
|
|
)}
|
|
chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve'
|
|
''
|
|
+ optionalString (cfg.imapsieve.mailbox != []) ''
|
|
mkdir -p ${stateDir}/imapsieve/{before,after}
|
|
|
|
${
|
|
concatMapStringsSep "\n"
|
|
(el:
|
|
optionalString (el.before != null) ''
|
|
cp -p ${el.before} ${stateDir}/imapsieve/before/${baseNameOf el.before}
|
|
${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/before/${baseNameOf el.before}'
|
|
''
|
|
+ optionalString (el.after != null) ''
|
|
cp -p ${el.after} ${stateDir}/imapsieve/after/${baseNameOf el.after}
|
|
${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/after/${baseNameOf el.after}'
|
|
''
|
|
)
|
|
cfg.imapsieve.mailbox
|
|
}
|
|
|
|
${
|
|
optionalString (cfg.mailUser != null && cfg.mailGroup != null)
|
|
"chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/imapsieve'"
|
|
}
|
|
'';
|
|
};
|
|
|
|
environment.systemPackages = [ dovecotPkg ];
|
|
|
|
warnings = warnAboutExtraConfigCollisions;
|
|
|
|
assertions = [
|
|
{
|
|
assertion = (cfg.sslServerCert == null) == (cfg.sslServerKey == null)
|
|
&& (cfg.sslCACert != null -> !(cfg.sslServerCert == null || cfg.sslServerKey == null));
|
|
message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto";
|
|
}
|
|
{
|
|
assertion = cfg.showPAMFailure -> cfg.enablePAM;
|
|
message = "dovecot is configured with showPAMFailure while enablePAM is disabled";
|
|
}
|
|
{
|
|
assertion = cfg.sieve.scripts != {} -> (cfg.mailUser != null && cfg.mailGroup != null);
|
|
message = "dovecot requires mailUser and mailGroup to be set when `sieve.scripts` is set";
|
|
}
|
|
];
|
|
|
|
};
|
|
|
|
meta.maintainers = [ lib.maintainers.dblsaiko ];
|
|
}
|