Merge branch 'dovecot-rfc42' into 'main'
dovecot: migrate to settings option See merge request simple-nixos-mailserver/nixos-mailserver!498
This commit is contained in:
+47
-12
@@ -137,6 +137,35 @@ in
|
|||||||
description = "Message size limit enforced by Postfix.";
|
description = "Message size limit enforced by Postfix.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
quota = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to enable quota support.
|
||||||
|
|
||||||
|
When enabled, incoming mail can be rejected if a mailbox exceeds its
|
||||||
|
quota.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
perUser = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
example = "10G";
|
||||||
|
description = ''
|
||||||
|
Default quota applied to all users.
|
||||||
|
|
||||||
|
The value must use a size format like `500M`, `2G`, `10G`.
|
||||||
|
|
||||||
|
If set to `null`, no default per user quota is applied and only
|
||||||
|
explicit per user quotas apply, if set.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
accounts = mkOption {
|
accounts = mkOption {
|
||||||
type = types.attrsOf (
|
type = types.attrsOf (
|
||||||
types.submodule (
|
types.submodule (
|
||||||
@@ -260,8 +289,11 @@ in
|
|||||||
default = null;
|
default = null;
|
||||||
example = "2G";
|
example = "2G";
|
||||||
description = ''
|
description = ''
|
||||||
Per user quota rules. Accepted sizes are `xx k/M/G/T` with the
|
The quota limit for this user.
|
||||||
obvious meaning. Leave blank for the standard quota `100G`.
|
|
||||||
|
The value is must use a size format like `500M`, `2G`, `10G`.
|
||||||
|
|
||||||
|
If unset, will fall back to {option}`mailserver.quota.defaults.perUser` if set.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -654,11 +686,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
lmtpSaveToDetailMailbox = mkOption {
|
lmtpSaveToDetailMailbox = mkOption {
|
||||||
type = types.enum [
|
type = types.bool;
|
||||||
"yes"
|
default = true;
|
||||||
"no"
|
|
||||||
];
|
|
||||||
default = "yes";
|
|
||||||
description = ''
|
description = ''
|
||||||
If an email address is delimited by a "+", should it be filed into a
|
If an email address is delimited by a "+", should it be filed into a
|
||||||
mailbox matching the string after the "+"? For example,
|
mailbox matching the string after the "+"? For example,
|
||||||
@@ -882,25 +911,31 @@ in
|
|||||||
|
|
||||||
mailboxes = mkOption {
|
mailboxes = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
The mailboxes for dovecot.
|
The default mailboxes for Dovecot maildirs.
|
||||||
|
|
||||||
|
The [`special_use`] option must refer to an [RFC6154 2] attribute and lead with an escaped backslash.
|
||||||
|
|
||||||
Depending on the mail client used it might be necessary to change some mailbox's name.
|
Depending on the mail client used it might be necessary to change some mailbox's name.
|
||||||
|
|
||||||
|
[special_use]: https://doc.dovecot.org/2.3/configuration_manual/namespace/#core_setting-namespace/mailbox/special_use
|
||||||
|
[RFC6154 2]: https://datatracker.ietf.org/doc/html/rfc6154.html#section-2
|
||||||
'';
|
'';
|
||||||
default = {
|
default = {
|
||||||
Trash = {
|
Trash = {
|
||||||
auto = "no";
|
auto = "no";
|
||||||
specialUse = "Trash";
|
special_use = "\\Trash";
|
||||||
};
|
};
|
||||||
Junk = {
|
Junk = {
|
||||||
auto = "subscribe";
|
auto = "subscribe";
|
||||||
specialUse = "Junk";
|
special_use = "\\Junk";
|
||||||
};
|
};
|
||||||
Drafts = {
|
Drafts = {
|
||||||
auto = "subscribe";
|
auto = "subscribe";
|
||||||
specialUse = "Drafts";
|
special_use = "\\Drafts";
|
||||||
};
|
};
|
||||||
Sent = {
|
Sent = {
|
||||||
auto = "subscribe";
|
auto = "subscribe";
|
||||||
specialUse = "Sent";
|
special_use = "\\Sent";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+3
-3
@@ -79,11 +79,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1774935083,
|
"lastModified": 1776030597,
|
||||||
"narHash": "sha256-Mh6bLcYAcENBAZk3RoMPMFCGGMZmfaGMERE4siZOgP4=",
|
"narHash": "sha256-H2CYM/RmVqCo1iud5BhPp8Pim2d1ESGt2FDHjbmju8A=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2f4fd5e1abf9bac8c1d22750c701a7a5e6b524c6",
|
"rev": "c88e63f4caf12c731f61ce71f300680ce73c180e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
+233
-211
@@ -32,6 +32,16 @@ with (import ./common.nix {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (lib)
|
||||||
|
attrNames
|
||||||
|
concatMapStringsSep
|
||||||
|
filterAttrs
|
||||||
|
mapAttrs'
|
||||||
|
mkForce
|
||||||
|
mkIf
|
||||||
|
nameValuePair
|
||||||
|
;
|
||||||
|
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
|
|
||||||
passwdDir = "/run/dovecot2";
|
passwdDir = "/run/dovecot2";
|
||||||
@@ -39,8 +49,6 @@ let
|
|||||||
userdbFile = "${passwdDir}/userdb";
|
userdbFile = "${passwdDir}/userdb";
|
||||||
# This file contains the ldap bind password
|
# This file contains the ldap bind password
|
||||||
ldapConfFile = "${passwdDir}/dovecot-ldap.conf.ext";
|
ldapConfFile = "${passwdDir}/dovecot-ldap.conf.ext";
|
||||||
boolToYesNo = x: if x then "yes" else "no";
|
|
||||||
listToLine = lib.concatStringsSep " ";
|
|
||||||
listToMultiAttrs =
|
listToMultiAttrs =
|
||||||
keyPrefix: attrs:
|
keyPrefix: attrs:
|
||||||
lib.listToAttrs (
|
lib.listToAttrs (
|
||||||
@@ -53,14 +61,6 @@ let
|
|||||||
maildirLayoutAppendix = lib.optionalString (cfg.storage.directoryLayout == "fs") ":LAYOUT=fs";
|
maildirLayoutAppendix = lib.optionalString (cfg.storage.directoryLayout == "fs") ":LAYOUT=fs";
|
||||||
maildirUTF8FolderNames = lib.optionalString cfg.useUTF8FolderNames ":UTF-8";
|
maildirUTF8FolderNames = lib.optionalString cfg.useUTF8FolderNames ":UTF-8";
|
||||||
|
|
||||||
# https://doc.dovecot.org/2.3/configuration_manual/home_directories_for_virtual_users/#ways-to-set-up-home-directory
|
|
||||||
# Mail directory below the home directory
|
|
||||||
dovecotMaildir =
|
|
||||||
"maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}"
|
|
||||||
+ (lib.optionalString (cfg.indexDir != null) ":INDEX=${cfg.indexDir}/%{domain}/%{username}");
|
|
||||||
|
|
||||||
postfixCfg = config.services.postfix;
|
|
||||||
|
|
||||||
ldapConfig = pkgs.writeTextFile {
|
ldapConfig = pkgs.writeTextFile {
|
||||||
name = "dovecot-ldap.conf.ext.template";
|
name = "dovecot-ldap.conf.ext.template";
|
||||||
text = ''
|
text = ''
|
||||||
@@ -128,12 +128,13 @@ let
|
|||||||
lib.mapAttrsToList (
|
lib.mapAttrsToList (
|
||||||
name: _:
|
name: _:
|
||||||
if lib.elem name accountsWithPlaintextPasswordFiles then
|
if lib.elem name accountsWithPlaintextPasswordFiles then
|
||||||
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' pkgs.dovecot "doveadm"} pw)"}::::::"
|
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' config.services.dovecot2.package "doveadm"} pw)"}::::::"
|
||||||
else
|
else
|
||||||
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
||||||
) cfg.accounts
|
) cfg.accounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
|
chown dovecot2:dovecot2 ${passwdFile}
|
||||||
|
|
||||||
cat <<EOF > ${userdbFile}
|
cat <<EOF > ${userdbFile}
|
||||||
${lib.concatStringsSep "\n" (
|
${lib.concatStringsSep "\n" (
|
||||||
@@ -144,10 +145,11 @@ let
|
|||||||
) cfg.accounts
|
) cfg.accounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
|
chown dovecot2:dovecot2 ${userdbFile}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
junkMailboxes = builtins.attrNames (
|
junkMailboxes = builtins.attrNames (
|
||||||
lib.filterAttrs (_: v: v ? "specialUse" && v.specialUse == "Junk") cfg.mailboxes
|
lib.filterAttrs (_: v: v ? "special_use" && v.special_use == "\\Junk") cfg.mailboxes
|
||||||
);
|
);
|
||||||
junkMailboxNumber = builtins.length junkMailboxes;
|
junkMailboxNumber = builtins.length junkMailboxes;
|
||||||
# The assertion guarantees there is exactly one Junk mailbox.
|
# The assertion guarantees there is exactly one Junk mailbox.
|
||||||
@@ -163,30 +165,29 @@ let
|
|||||||
else
|
else
|
||||||
scope
|
scope
|
||||||
);
|
);
|
||||||
|
|
||||||
ftsPluginSettings = {
|
|
||||||
fts = "flatcurve";
|
|
||||||
fts_languages = listToLine cfg.fullTextSearch.languages;
|
|
||||||
fts_tokenizers = listToLine [
|
|
||||||
"generic"
|
|
||||||
"email-address"
|
|
||||||
];
|
|
||||||
fts_tokenizer_email_address = "maxlen=100"; # default 254 too large for Xapian
|
|
||||||
fts_flatcurve_substring_search = boolToYesNo cfg.fullTextSearch.substringSearch;
|
|
||||||
fts_filters = listToLine cfg.fullTextSearch.filters;
|
|
||||||
fts_header_excludes = listToLine cfg.fullTextSearch.headerExcludes;
|
|
||||||
fts_autoindex = boolToYesNo cfg.fullTextSearch.autoIndex;
|
|
||||||
fts_enforced = cfg.fullTextSearch.enforced;
|
|
||||||
}
|
|
||||||
// (listToMultiAttrs "fts_autoindex_exclude" cfg.fullTextSearch.autoIndexExclude);
|
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = junkMailboxNumber == 1;
|
assertion = junkMailboxNumber == 1;
|
||||||
message = "nixos-mailserver requires exactly one dovecot mailbox with the 'special use' flag set to 'Junk' (${builtins.toString junkMailboxNumber} have been found)";
|
message = "nixos-mailserver requires exactly one dovecot mailbox with the 'special_use' flag set to '\\Junk' (${builtins.toString junkMailboxNumber} have been found)";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
let
|
||||||
|
usersWithQuota = attrNames (
|
||||||
|
filterAttrs (_: account: account.quota != null) config.mailserver.loginAccounts
|
||||||
|
);
|
||||||
|
in
|
||||||
|
!cfg.quota.enable -> usersWithQuota == { };
|
||||||
|
message = ''
|
||||||
|
Without quota support enabled, per-user quotas cannot be applied to the following accounts:
|
||||||
|
|
||||||
|
${concatMapStringsSep "\n" (account: "- ${account}") quotaUsers}
|
||||||
|
|
||||||
|
Either remove per user quota settings or re-enable `mailserver.quota.enable`.
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -215,7 +216,7 @@ in
|
|||||||
# the global config and tries to open shared libraries configured in there,
|
# the global config and tries to open shared libraries configured in there,
|
||||||
# which are usually not compatible.
|
# which are usually not compatible.
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.dovecot_pigeonhole
|
pkgs.dovecot_pigeonhole_0_5
|
||||||
]
|
]
|
||||||
++ lib.optional cfg.fullTextSearch.enable pkgs.dovecot-fts-flatcurve;
|
++ lib.optional cfg.fullTextSearch.enable pkgs.dovecot-fts-flatcurve;
|
||||||
|
|
||||||
@@ -223,30 +224,9 @@ in
|
|||||||
environment.etc."dovecot/modules".source = "/run/current-system/sw/lib/dovecot/modules";
|
environment.etc."dovecot/modules".source = "/run/current-system/sw/lib/dovecot/modules";
|
||||||
|
|
||||||
services.dovecot2 = {
|
services.dovecot2 = {
|
||||||
|
package = pkgs.dovecot_2_3;
|
||||||
enable = true;
|
enable = true;
|
||||||
enableImap = cfg.enableImap || cfg.enableImapSsl;
|
enablePAM = mkForce false;
|
||||||
enablePop3 = cfg.enablePop3 || cfg.enablePop3Ssl;
|
|
||||||
enablePAM = false;
|
|
||||||
enableQuota = true;
|
|
||||||
mailGroup = cfg.storage.group;
|
|
||||||
mailUser = cfg.storage.owner;
|
|
||||||
mailLocation = dovecotMaildir;
|
|
||||||
sslServerCert = x509CertificateFile;
|
|
||||||
sslServerKey = x509PrivateKeyFile;
|
|
||||||
enableDHE = lib.mkDefault false;
|
|
||||||
enableLmtp = true;
|
|
||||||
mailPlugins.globally.enable = lib.optionals cfg.fullTextSearch.enable [
|
|
||||||
"fts"
|
|
||||||
"fts_flatcurve"
|
|
||||||
];
|
|
||||||
protocols = lib.optional cfg.enableManageSieve "sieve";
|
|
||||||
|
|
||||||
pluginSettings = {
|
|
||||||
sieve = "file:${cfg.sieveDirectory}/%{user}/scripts;active=${cfg.sieveDirectory}/%{user}/active.sieve";
|
|
||||||
sieve_default = "file:${cfg.sieveDirectory}/%{user}/default.sieve";
|
|
||||||
sieve_default_name = "default";
|
|
||||||
}
|
|
||||||
// (lib.optionalAttrs cfg.fullTextSearch.enable ftsPluginSettings);
|
|
||||||
|
|
||||||
sieve = {
|
sieve = {
|
||||||
extensions = [
|
extensions = [
|
||||||
@@ -285,100 +265,127 @@ in
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
mailboxes = cfg.mailboxes;
|
settings = {
|
||||||
|
# vmail user
|
||||||
|
mail_uid = cfg.storage.owner;
|
||||||
|
mail_gid = cfg.storage.group;
|
||||||
|
mail_access_groups = cfg.storage.group;
|
||||||
|
|
||||||
extraConfig = ''
|
# authentication
|
||||||
#Extra Config
|
auth_mechanisms = [
|
||||||
${lib.optionalString cfg.debug.dovecot ''
|
"plain"
|
||||||
mail_debug = yes
|
"login"
|
||||||
auth_debug = yes
|
];
|
||||||
verbose_ssl = yes
|
disable_plaintext_auth = true;
|
||||||
''}
|
|
||||||
|
|
||||||
${lib.optionalString (cfg.enableImap || cfg.enableImapSsl) ''
|
# backend services
|
||||||
service imap-login {
|
"service auth" = {
|
||||||
inet_listener imap {
|
"unix_listener auth" = {
|
||||||
${
|
mode = "0660";
|
||||||
if cfg.enableImap then
|
user = config.services.postfix.user;
|
||||||
''
|
group = config.services.postfix.group;
|
||||||
port = 143
|
};
|
||||||
''
|
};
|
||||||
else
|
"service indexer-worker" = mkIf (cfg.fullTextSearch.memoryLimit != null) {
|
||||||
''
|
vsz_limit = "${toString cfg.fullTextSearch.memoryLimit} MB";
|
||||||
|
};
|
||||||
|
"service lmtp" = {
|
||||||
|
"unix_listener dovecot-lmtp" = {
|
||||||
|
group = config.services.postfix.group;
|
||||||
|
mode = "0600";
|
||||||
|
user = config.services.postfix.user;
|
||||||
|
};
|
||||||
|
user = cfg.storage.owner;
|
||||||
|
vsz_limit = "${toString cfg.lmtpMemoryLimit} MB";
|
||||||
|
};
|
||||||
|
"service quota-status" = mkIf cfg.quota.enable {
|
||||||
|
executable = toString [
|
||||||
|
"${config.services.dovecot2.package}/libexec/dovecot/quota-status"
|
||||||
|
"-p"
|
||||||
|
"postfix"
|
||||||
|
];
|
||||||
|
"unix_listener quota-status" = {
|
||||||
|
user = "postfix";
|
||||||
|
};
|
||||||
|
client_limit = 1;
|
||||||
|
vsz_limit = "${toString cfg.quotaStatusMemoryLimit} MB";
|
||||||
|
};
|
||||||
|
|
||||||
|
# frontend services
|
||||||
|
"service imap-login" = mkIf (cfg.enableImap || cfg.enableImapSsl) {
|
||||||
|
"inet_listener imap" = {
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
port = 0
|
port = if cfg.enableImap then 143 else 0;
|
||||||
''
|
};
|
||||||
}
|
"inet_listener imaps" = {
|
||||||
}
|
|
||||||
inet_listener imaps {
|
|
||||||
${
|
|
||||||
if cfg.enableImapSsl then
|
|
||||||
''
|
|
||||||
port = 993
|
|
||||||
ssl = yes
|
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
port = 0
|
port = if cfg.enableImapSsl then 993 else 0;
|
||||||
''
|
ssl = true;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
}
|
"service pop3-login" = mkIf (cfg.enablePop3 || cfg.enablePop3Ssl) {
|
||||||
''}
|
"inet_listener pop3" = {
|
||||||
${lib.optionalString (cfg.enablePop3 || cfg.enablePop3Ssl) ''
|
|
||||||
service pop3-login {
|
|
||||||
inet_listener pop3 {
|
|
||||||
${
|
|
||||||
if cfg.enablePop3 then
|
|
||||||
''
|
|
||||||
port = 110
|
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
port = 0
|
port = if cfg.enablePop3 then 110 else 0;
|
||||||
''
|
};
|
||||||
}
|
"inet_listener pop3s" = {
|
||||||
}
|
|
||||||
inet_listener pop3s {
|
|
||||||
${
|
|
||||||
if cfg.enablePop3Ssl then
|
|
||||||
''
|
|
||||||
port = 995
|
|
||||||
ssl = yes
|
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
port = 0
|
port = if cfg.enablePop3Ssl then 995 else 0;
|
||||||
''
|
ssl = true;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
}
|
"service imap" = {
|
||||||
''}
|
vsz_limit = "${toString cfg.imapMemoryLimit} MB";
|
||||||
|
};
|
||||||
|
|
||||||
protocol imap {
|
# protocols
|
||||||
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
protocols = [
|
||||||
mail_plugins = $mail_plugins imap_sieve
|
"lmtp"
|
||||||
}
|
]
|
||||||
|
++ lib.optionals (cfg.enableImap || cfg.enableImapSsl) [ "imap" ]
|
||||||
|
++ lib.optionals (cfg.enablePop3 || cfg.enablePop3Ssl) [ "pop3" ]
|
||||||
|
++ lib.optionals cfg.enableManageSieve [ "sieve" ];
|
||||||
|
|
||||||
service imap {
|
"protocol lmtp" = {
|
||||||
vsz_limit = ${builtins.toString cfg.imapMemoryLimit} MB
|
mail_plugins = [
|
||||||
}
|
"$mail_plugins"
|
||||||
|
"sieve"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"protocol imap" = {
|
||||||
|
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||||
|
mail_plugins = [
|
||||||
|
"$mail_plugins"
|
||||||
|
"imap_sieve"
|
||||||
|
]
|
||||||
|
++ lib.optionals cfg.quota.enable [
|
||||||
|
"imap_quota"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"protocol pop3" = {
|
||||||
|
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||||
|
};
|
||||||
|
|
||||||
protocol pop3 {
|
# globally enabled plugins
|
||||||
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
mail_plugins = [
|
||||||
}
|
"$mail_plugins"
|
||||||
|
]
|
||||||
mail_access_groups = ${cfg.storage.group}
|
++ lib.optionals cfg.quota.enable [
|
||||||
|
"quota"
|
||||||
|
]
|
||||||
|
++ lib.optionals cfg.fullTextSearch.enable [
|
||||||
|
"fts"
|
||||||
|
"fts_flatcurve"
|
||||||
|
];
|
||||||
|
|
||||||
|
# tls settings
|
||||||
|
ssl_cert = "<${x509CertificateFile}";
|
||||||
|
ssl_key = "<${x509PrivateKeyFile}";
|
||||||
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
||||||
ssl = required
|
ssl = "required";
|
||||||
ssl_min_protocol = TLSv1.2
|
ssl_min_protocol = "TLSv1.2";
|
||||||
ssl_prefer_server_ciphers = no
|
ssl_prefer_server_ciphers = false;
|
||||||
ssl_cipher_list = ${
|
ssl_cipher_list = lib.concatStringsSep ":" [
|
||||||
lib.concatStringsSep ":" [
|
|
||||||
# TLS1.3
|
# TLS1.3
|
||||||
"TLS_AES_128_GCM_SHA256"
|
"TLS_AES_128_GCM_SHA256"
|
||||||
"TLS_CHACHA20_POLY1305_SHA256"
|
"TLS_CHACHA20_POLY1305_SHA256"
|
||||||
@@ -392,89 +399,104 @@ in
|
|||||||
"ECDHE-RSA-AES128-GCM-SHA256"
|
"ECDHE-RSA-AES128-GCM-SHA256"
|
||||||
"ECDHE-RSA-CHACHA20-POLY1305"
|
"ECDHE-RSA-CHACHA20-POLY1305"
|
||||||
"ECDHE-RSA-AES256-GCM-SHA384"
|
"ECDHE-RSA-AES256-GCM-SHA384"
|
||||||
|
];
|
||||||
|
ssl_curve_list = lib.concatStringsSep ":" [
|
||||||
|
"X25519MLKEM768"
|
||||||
|
"X25519"
|
||||||
|
"prime256v1"
|
||||||
|
"secp384r1"
|
||||||
|
];
|
||||||
|
|
||||||
|
# default mail storage
|
||||||
|
# https://doc.dovecot.org/2.3/configuration_manual/home_directories_for_virtual_users/#ways-to-set-up-home-directory
|
||||||
|
mail_location =
|
||||||
|
"maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}"
|
||||||
|
+ (lib.optionalString (cfg.indexDir != null) ":INDEX=${cfg.indexDir}/%{domain}/%{username}");
|
||||||
|
|
||||||
|
passdb = [
|
||||||
|
{
|
||||||
|
driver = "passwd-file";
|
||||||
|
args = "${passwdFile}";
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
++ lib.optionals cfg.ldap.enable [
|
||||||
|
{
|
||||||
|
driver = "ldap";
|
||||||
|
args = "${ldapConfFile}";
|
||||||
}
|
}
|
||||||
ssl_curve_list = X25519MLKEM768:X25519:prime256v1:secp384r1
|
];
|
||||||
|
userdb = [
|
||||||
|
{
|
||||||
|
driver = "passwd-file";
|
||||||
|
args = "${userdbFile}";
|
||||||
|
default_fields = [
|
||||||
|
"home=${cfg.storage.path}/%{domain}/%{username}"
|
||||||
|
"uid=${builtins.toString cfg.storage.uid}"
|
||||||
|
"gid=${builtins.toString cfg.storage.gid}"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ lib.optionals cfg.ldap.enable [
|
||||||
|
{
|
||||||
|
driver = "ldap";
|
||||||
|
args = "${ldapConfFile}";
|
||||||
|
override_fields = [
|
||||||
|
"uid=${toString cfg.storage.uid}"
|
||||||
|
"gid=${toString cfg.storage.gid}"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
service lmtp {
|
# default user mailboxes
|
||||||
unix_listener dovecot-lmtp {
|
"namespace inbox" = {
|
||||||
group = ${postfixCfg.group}
|
inbox = true;
|
||||||
mode = 0600
|
separator = ".";
|
||||||
user = ${postfixCfg.user}
|
|
||||||
}
|
|
||||||
vsz_limit = ${builtins.toString cfg.lmtpMemoryLimit} MB
|
|
||||||
}
|
}
|
||||||
|
// mapAttrs' (name: value: nameValuePair ''mailbox "${name}"'' value) cfg.mailboxes;
|
||||||
|
lda_mailbox_autosubscribe = true;
|
||||||
|
lda_mailbox_autocreate = true;
|
||||||
|
|
||||||
service quota-status {
|
# subaddressing
|
||||||
inet_listener {
|
recipient_delimiter = cfg.recipientDelimiter;
|
||||||
port = 0
|
lmtp_save_to_detail_mailbox = cfg.lmtpSaveToDetailMailbox;
|
||||||
}
|
|
||||||
unix_listener quota-status {
|
|
||||||
user = postfix
|
|
||||||
}
|
|
||||||
vsz_limit = ${builtins.toString cfg.quotaStatusMemoryLimit} MB
|
|
||||||
}
|
|
||||||
|
|
||||||
recipient_delimiter = ${cfg.recipientDelimiter}
|
plugin = {
|
||||||
lmtp_save_to_detail_mailbox = ${cfg.lmtpSaveToDetailMailbox}
|
sieve = "file:${cfg.sieveDirectory}/%{user}/scripts;active=${cfg.sieveDirectory}/%{user}/active.sieve";
|
||||||
|
sieve_default = "file:${cfg.sieveDirectory}/%{user}/default.sieve";
|
||||||
protocol lmtp {
|
sieve_default_name = "default";
|
||||||
mail_plugins = $mail_plugins sieve
|
|
||||||
}
|
}
|
||||||
|
// lib.optionalAttrs cfg.fullTextSearch.enable (
|
||||||
passdb {
|
{
|
||||||
driver = passwd-file
|
fts = "flatcurve";
|
||||||
args = ${passwdFile}
|
fts_languages = cfg.fullTextSearch.languages;
|
||||||
|
fts_tokenizers = [
|
||||||
|
"generic"
|
||||||
|
"email-address"
|
||||||
|
];
|
||||||
|
fts_tokenizer_email_address = "maxlen=100"; # default 254 too large for Xapian
|
||||||
|
fts_flatcurve_substring_search = cfg.fullTextSearch.substringSearch;
|
||||||
|
fts_filters = cfg.fullTextSearch.filters;
|
||||||
|
fts_header_excludes = cfg.fullTextSearch.headerExcludes;
|
||||||
|
fts_autoindex = cfg.fullTextSearch.autoIndex;
|
||||||
|
fts_enforced = cfg.fullTextSearch.enforced;
|
||||||
}
|
}
|
||||||
|
// (listToMultiAttrs "fts_autoindex_exclude" cfg.fullTextSearch.autoIndexExclude)
|
||||||
userdb {
|
)
|
||||||
driver = passwd-file
|
// lib.optionalAttrs cfg.quota.enable {
|
||||||
args = ${userdbFile}
|
quota_rule = mkIf (cfg.quota.defaults.perUser != null) "*:storage=${cfg.quota.defaults.perUser}";
|
||||||
default_fields = \
|
quota = "count:User quota"; # per virtual mail user quota
|
||||||
home=${cfg.storage.path}/%{domain}/%{username} \
|
quota_status_success = "DUNNO";
|
||||||
uid=${builtins.toString cfg.storage.uid} \
|
quota_status_nouser = "DUNNO";
|
||||||
gid=${builtins.toString cfg.storage.uid}
|
quota_status_overquota = "552 5.2.2 Mailbox is full";
|
||||||
|
quota_grace = "10%%";
|
||||||
|
quota_vsizes = true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
// lib.optionalAttrs cfg.debug.dovecot {
|
||||||
${lib.optionalString cfg.ldap.enable ''
|
mail_debug = true;
|
||||||
passdb {
|
auth_debug = true;
|
||||||
driver = ldap
|
verbose_ssl = true;
|
||||||
args = ${ldapConfFile}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
userdb {
|
|
||||||
driver = ldap
|
|
||||||
args = ${ldapConfFile}
|
|
||||||
override_fields = \
|
|
||||||
uid=${toString cfg.storage.uid} \
|
|
||||||
gid=${toString cfg.storage.uid}
|
|
||||||
}
|
|
||||||
''}
|
|
||||||
|
|
||||||
service auth {
|
|
||||||
unix_listener auth {
|
|
||||||
mode = 0660
|
|
||||||
user = ${postfixCfg.user}
|
|
||||||
group = ${postfixCfg.group}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auth_mechanisms = plain login
|
|
||||||
|
|
||||||
namespace inbox {
|
|
||||||
separator = ${cfg.hierarchySeparator}
|
|
||||||
inbox = yes
|
|
||||||
}
|
|
||||||
|
|
||||||
service indexer-worker {
|
|
||||||
${lib.optionalString (cfg.fullTextSearch.memoryLimit != null) ''
|
|
||||||
vsz_limit = ${toString (cfg.fullTextSearch.memoryLimit * 1024 * 1024)}
|
|
||||||
''}
|
|
||||||
}
|
|
||||||
|
|
||||||
lda_mailbox_autosubscribe = yes
|
|
||||||
lda_mailbox_autocreate = yes
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.dovecot = {
|
systemd.services.dovecot = {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ in
|
|||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
dovecot
|
config.services.dovecot2.package
|
||||||
openssh
|
openssh
|
||||||
postfix
|
postfix
|
||||||
rspamd
|
rspamd
|
||||||
|
|||||||
@@ -373,6 +373,8 @@ in
|
|||||||
# reject selected recipients
|
# reject selected recipients
|
||||||
"check_recipient_access ${mappedFile "denied_recipients"}"
|
"check_recipient_access ${mappedFile "denied_recipients"}"
|
||||||
"check_recipient_access ${mappedFile "reject_recipients"}"
|
"check_recipient_access ${mappedFile "reject_recipients"}"
|
||||||
|
]
|
||||||
|
++ lib.optionals cfg.quota.enable [
|
||||||
# quota checking
|
# quota checking
|
||||||
"check_policy_service unix:/run/dovecot2/quota-status"
|
"check_policy_service unix:/run/dovecot2/quota-status"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ groups = [
|
|||||||
"mailserver.dmarcReporting",
|
"mailserver.dmarcReporting",
|
||||||
"mailserver.tlsrpt",
|
"mailserver.tlsrpt",
|
||||||
"mailserver.fullTextSearch",
|
"mailserver.fullTextSearch",
|
||||||
|
"mailserver.quota",
|
||||||
"mailserver.redis",
|
"mailserver.redis",
|
||||||
"mailserver.ldap",
|
"mailserver.ldap",
|
||||||
"mailserver.monitoring",
|
"mailserver.monitoring",
|
||||||
|
|||||||
Reference in New Issue
Block a user