dovecot: migrate to dovecot 2.4
This commit is contained in:
+318
-309
@@ -39,6 +39,7 @@ let
|
||||
mapAttrs'
|
||||
mkForce
|
||||
mkIf
|
||||
mkMerge
|
||||
nameValuePair
|
||||
;
|
||||
|
||||
@@ -47,56 +48,6 @@ let
|
||||
passwdDir = "/run/dovecot2";
|
||||
passwdFile = "${passwdDir}/passwd";
|
||||
userdbFile = "${passwdDir}/userdb";
|
||||
# This file contains the ldap bind password
|
||||
ldapConfFile = "${passwdDir}/dovecot-ldap.conf.ext";
|
||||
listToMultiAttrs =
|
||||
keyPrefix: attrs:
|
||||
lib.listToAttrs (
|
||||
lib.imap1 (n: x: {
|
||||
name = "${keyPrefix}${if n == 1 then "" else toString n}";
|
||||
value = x;
|
||||
}) attrs
|
||||
);
|
||||
|
||||
maildirLayoutAppendix = lib.optionalString (cfg.storage.directoryLayout == "fs") ":LAYOUT=fs";
|
||||
maildirUTF8FolderNames = lib.optionalString cfg.useUTF8FolderNames ":UTF-8";
|
||||
|
||||
ldapConfig = pkgs.writeTextFile {
|
||||
name = "dovecot-ldap.conf.ext.template";
|
||||
text = ''
|
||||
ldap_version = 3
|
||||
uris = ${lib.concatStringsSep " " cfg.ldap.uris}
|
||||
${lib.optionalString cfg.ldap.startTls ''
|
||||
tls = yes
|
||||
''}
|
||||
tls_require_cert = hard
|
||||
tls_ca_cert_file = ${cfg.ldap.caFile}
|
||||
dn = ${cfg.ldap.bind.dn}
|
||||
sasl_bind = no
|
||||
auth_bind = yes
|
||||
base = ${cfg.ldap.base}
|
||||
scope = ${mkLdapSearchScope cfg.ldap.scope}
|
||||
user_attrs = \
|
||||
=home=${cfg.storage.path}/ldap/%{ldap:${cfg.ldap.attributes.uuid}}, \
|
||||
=mail=maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}${
|
||||
lib.optionalString (
|
||||
cfg.indexDir != null
|
||||
) ":INDEX=${cfg.indexDir}/ldap/%{ldap:${cfg.ldap.attributes.uuid}}"
|
||||
}
|
||||
user_filter = ${cfg.ldap.dovecot.userFilter}
|
||||
pass_attrs = ${cfg.ldap.attributes.password}=password
|
||||
pass_filter = ${cfg.ldap.dovecot.passFilter}
|
||||
'';
|
||||
};
|
||||
|
||||
setPwdInLdapConfFile = appendLdapBindPwd {
|
||||
name = "ldap-conf-file";
|
||||
file = ldapConfig;
|
||||
prefix = ''dnpass = "'';
|
||||
suffix = ''"'';
|
||||
passwordFile = cfg.ldap.bind.passwordFile;
|
||||
destination = ldapConfFile;
|
||||
};
|
||||
|
||||
genPasswdScript =
|
||||
pkgs.writeScript "generate-password-file"
|
||||
@@ -130,7 +81,7 @@ let
|
||||
if lib.elem name accountsWithPlaintextPasswordFiles then
|
||||
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' config.services.dovecot2.package "doveadm"} pw)"}::::::"
|
||||
else
|
||||
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
||||
"${name}:{CRYPT}${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
||||
) cfg.accounts
|
||||
)}
|
||||
EOF
|
||||
@@ -140,8 +91,11 @@ let
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (
|
||||
name: value:
|
||||
# https://doc.dovecot.org/2.4.3/core/config/auth/databases/passwd_file.html
|
||||
# https://doc.dovecot.org/2.4.3/core/plugins/quota.html#per-user-quota
|
||||
# https://dovecot.org/mailman3/archives/list/dovecot@dovecot.org/thread/67DBLLW4L5QBTEYRKGA26POFZ52ZR7ZO/#67DBLLW4L5QBTEYRKGA26POFZ52ZR7ZO
|
||||
"${name}:::::::"
|
||||
+ lib.optionalString (value.quota != null) "userdb_quota_rule=*:storage=${value.quota}"
|
||||
+ lib.optionalString (value.quota != null) "userdb_quota/user/storage_size=${value.quota}"
|
||||
) cfg.accounts
|
||||
)}
|
||||
EOF
|
||||
@@ -213,310 +167,365 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
# for sieve-test. Shelling it in on demand usually doesn't work, as it reads
|
||||
# the global config and tries to open shared libraries configured in there,
|
||||
# which are usually not compatible.
|
||||
# Dovecot modules
|
||||
environment.systemPackages = [
|
||||
pkgs.dovecot_pigeonhole_0_5
|
||||
]
|
||||
++ lib.optional cfg.fullTextSearch.enable pkgs.dovecot-fts-flatcurve;
|
||||
pkgs.dovecot_pigeonhole
|
||||
];
|
||||
|
||||
# For compatibility with python imaplib
|
||||
environment.etc."dovecot/modules".source = "/run/current-system/sw/lib/dovecot/modules";
|
||||
|
||||
services.dovecot2 = {
|
||||
package = pkgs.dovecot_2_3;
|
||||
enable = true;
|
||||
package = pkgs.dovecot; # pin over stateVersion logic in nixox 26.05
|
||||
enablePAM = mkForce false;
|
||||
|
||||
sieve = {
|
||||
extensions = [
|
||||
"fileinto"
|
||||
];
|
||||
|
||||
scripts.after = builtins.toFile "spam.sieve" ''
|
||||
require "fileinto";
|
||||
|
||||
if header :is "X-Spam" "Yes" {
|
||||
fileinto "${junkMailboxName}";
|
||||
stop;
|
||||
}
|
||||
'';
|
||||
|
||||
pipeBins = map lib.getExe [
|
||||
(pkgs.writeShellScriptBin "rspamd-learn-ham.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_ham")
|
||||
(pkgs.writeShellScriptBin "rspamd-learn-spam.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_spam")
|
||||
];
|
||||
};
|
||||
|
||||
imapsieve.mailbox = [
|
||||
{
|
||||
name = junkMailboxName;
|
||||
causes = [
|
||||
"COPY"
|
||||
"APPEND"
|
||||
];
|
||||
before = ./dovecot/imap_sieve/report-spam.sieve;
|
||||
}
|
||||
{
|
||||
name = "*";
|
||||
from = junkMailboxName;
|
||||
causes = [ "COPY" ];
|
||||
before = ./dovecot/imap_sieve/report-ham.sieve;
|
||||
}
|
||||
sieve.pipeBins = map lib.getExe [
|
||||
(pkgs.writeShellScriptBin "rspamd-learn-ham.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_ham")
|
||||
(pkgs.writeShellScriptBin "rspamd-learn-spam.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_spam")
|
||||
];
|
||||
|
||||
settings = {
|
||||
# vmail user
|
||||
mail_uid = cfg.storage.owner;
|
||||
mail_gid = cfg.storage.group;
|
||||
mail_access_groups = cfg.storage.group;
|
||||
# https://doc.dovecot.org/2.4.3/core/settings/syntax.html
|
||||
# https://doc.dovecot.org/2.4.3/core/settings/types.html#boolean-list
|
||||
settings = mkMerge [
|
||||
({
|
||||
# https://doc.dovecot.org/main/core/summaries/settings.html#dovecot_config_version
|
||||
dovecot_config_version = "2.4.3";
|
||||
# https://doc.dovecot.org/main/core/summaries/settings.html#dovecot_storage_version
|
||||
dovecot_storage_version = "2.3.21.1";
|
||||
|
||||
# authentication
|
||||
auth_mechanisms = [
|
||||
"plain"
|
||||
"login"
|
||||
];
|
||||
disable_plaintext_auth = true;
|
||||
# server identity
|
||||
hostname = cfg.fqdn;
|
||||
|
||||
# hostname
|
||||
hostname = cfg.fqdn;
|
||||
# vmail user
|
||||
mail_uid = cfg.storage.owner;
|
||||
mail_gid = cfg.storage.group;
|
||||
mail_access_groups = cfg.storage.group;
|
||||
|
||||
# backend services
|
||||
"service auth" = {
|
||||
"unix_listener auth" = {
|
||||
mode = "0660";
|
||||
user = config.services.postfix.user;
|
||||
group = config.services.postfix.group;
|
||||
};
|
||||
};
|
||||
"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"
|
||||
# authentication
|
||||
auth_mechanisms = [
|
||||
"plain"
|
||||
"login"
|
||||
];
|
||||
"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
|
||||
port = if cfg.enableImap then 143 else 0;
|
||||
# backend services
|
||||
"service anvil" = {
|
||||
"unix_listener anvil" = {
|
||||
mode = "0660";
|
||||
group = cfg.storage.group;
|
||||
};
|
||||
};
|
||||
"inet_listener imaps" = {
|
||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||
port = if cfg.enableImapSsl then 993 else 0;
|
||||
ssl = true;
|
||||
"service auth" = {
|
||||
"unix_listener auth" = {
|
||||
user = config.services.postfix.user;
|
||||
group = config.services.postfix.group;
|
||||
mode = "0660";
|
||||
};
|
||||
};
|
||||
};
|
||||
"service pop3-login" = mkIf (cfg.enablePop3 || cfg.enablePop3Ssl) {
|
||||
"inet_listener pop3" = {
|
||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||
port = if cfg.enablePop3 then 110 else 0;
|
||||
"service lmtp" = {
|
||||
"unix_listener dovecot-lmtp" = {
|
||||
user = config.services.postfix.user;
|
||||
group = config.services.postfix.group;
|
||||
mode = "0600";
|
||||
};
|
||||
user = cfg.storage.owner;
|
||||
vsz_limit = "${toString cfg.lmtpMemoryLimit} MB";
|
||||
};
|
||||
"inet_listener pop3s" = {
|
||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||
port = if cfg.enablePop3Ssl then 995 else 0;
|
||||
ssl = true;
|
||||
};
|
||||
};
|
||||
"service imap" = {
|
||||
vsz_limit = "${toString cfg.imapMemoryLimit} MB";
|
||||
};
|
||||
|
||||
# protocols
|
||||
protocols = [
|
||||
"lmtp"
|
||||
]
|
||||
++ lib.optionals (cfg.enableImap || cfg.enableImapSsl) [ "imap" ]
|
||||
++ lib.optionals (cfg.enablePop3 || cfg.enablePop3Ssl) [ "pop3" ]
|
||||
++ lib.optionals cfg.enableManageSieve [ "sieve" ];
|
||||
# frontend services
|
||||
"service imap-login" = mkIf (cfg.enableImap || cfg.enableImapSsl) {
|
||||
"inet_listener imap" = {
|
||||
# https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||
port = if cfg.enableImap then 143 else 0;
|
||||
};
|
||||
"inet_listener imaps" = mkIf cfg.enableImapSsl {
|
||||
port = 993;
|
||||
ssl = true;
|
||||
};
|
||||
};
|
||||
"service pop3-login" = mkIf (cfg.enablePop3 || cfg.enablePop3Ssl) {
|
||||
"inet_listener pop3" = {
|
||||
# https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||
port = if cfg.enablePop3 then 110 else 0;
|
||||
};
|
||||
"inet_listener pop3s" = mkIf cfg.enablePop3Ssl {
|
||||
port = 995;
|
||||
ssl = true;
|
||||
};
|
||||
};
|
||||
"service imap" = {
|
||||
vsz_limit = "${toString cfg.imapMemoryLimit} MB";
|
||||
};
|
||||
|
||||
"protocol lmtp" = {
|
||||
mail_plugins = [
|
||||
"$mail_plugins"
|
||||
"sieve"
|
||||
# protocols
|
||||
protocols = {
|
||||
lmtp = true;
|
||||
imap = cfg.enableImap || cfg.enableImapSsl;
|
||||
pop3 = cfg.enablePop3 || cfg.enablePop3Ssl;
|
||||
sieve = cfg.enableManageSieve;
|
||||
};
|
||||
|
||||
"protocol lmtp" = {
|
||||
mail_plugins = {
|
||||
sieve = true;
|
||||
};
|
||||
};
|
||||
"protocol imap" = {
|
||||
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||
mail_plugins = {
|
||||
imap_sieve = true;
|
||||
};
|
||||
};
|
||||
"protocol pop3" = {
|
||||
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||
};
|
||||
|
||||
# tls settings
|
||||
ssl_server_cert_file = x509CertificateFile;
|
||||
ssl_server_key_file = x509PrivateKeyFile;
|
||||
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
||||
ssl = "required";
|
||||
ssl_min_protocol = "TLSv1.2";
|
||||
ssl_server_prefer_ciphers = "client";
|
||||
ssl_cipher_list = lib.concatStringsSep ":" [
|
||||
# TLS1.3
|
||||
"TLS_AES_128_GCM_SHA256"
|
||||
"TLS_CHACHA20_POLY1305_SHA256"
|
||||
"TLS_AES_256_GCM_SHA384"
|
||||
# TLS1.2
|
||||
# EC key material
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256"
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305"
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384"
|
||||
# RSA key material
|
||||
"ECDHE-RSA-AES128-GCM-SHA256"
|
||||
"ECDHE-RSA-CHACHA20-POLY1305"
|
||||
"ECDHE-RSA-AES256-GCM-SHA384"
|
||||
];
|
||||
};
|
||||
"protocol imap" = {
|
||||
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||
mail_plugins = [
|
||||
"$mail_plugins"
|
||||
"imap_sieve"
|
||||
]
|
||||
++ lib.optionals cfg.quota.enable [
|
||||
"imap_quota"
|
||||
ssl_curve_list = lib.concatStringsSep ":" [
|
||||
"X25519MLKEM768"
|
||||
"X25519"
|
||||
"prime256v1"
|
||||
"secp384r1"
|
||||
];
|
||||
};
|
||||
"protocol pop3" = {
|
||||
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||
};
|
||||
|
||||
# globally enabled plugins
|
||||
mail_plugins = [
|
||||
"$mail_plugins"
|
||||
]
|
||||
++ lib.optionals cfg.quota.enable [
|
||||
"quota"
|
||||
]
|
||||
++ lib.optionals cfg.fullTextSearch.enable [
|
||||
"fts"
|
||||
"fts_flatcurve"
|
||||
];
|
||||
# default user mailboxes
|
||||
"namespace inbox" = {
|
||||
inbox = true;
|
||||
separator = cfg.hierarchySeparator;
|
||||
}
|
||||
// mapAttrs' (name: value: nameValuePair ''mailbox "${name}"'' value) cfg.mailboxes;
|
||||
lda_mailbox_autosubscribe = true;
|
||||
lda_mailbox_autocreate = true;
|
||||
|
||||
# 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
|
||||
ssl = "required";
|
||||
ssl_min_protocol = "TLSv1.2";
|
||||
ssl_prefer_server_ciphers = false;
|
||||
ssl_cipher_list = lib.concatStringsSep ":" [
|
||||
# TLS1.3
|
||||
"TLS_AES_128_GCM_SHA256"
|
||||
"TLS_CHACHA20_POLY1305_SHA256"
|
||||
"TLS_AES_256_GCM_SHA384"
|
||||
# TLS1.2
|
||||
# EC key material
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256"
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305"
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384"
|
||||
# RSA key material
|
||||
"ECDHE-RSA-AES128-GCM-SHA256"
|
||||
"ECDHE-RSA-CHACHA20-POLY1305"
|
||||
"ECDHE-RSA-AES256-GCM-SHA384"
|
||||
];
|
||||
ssl_curve_list = lib.concatStringsSep ":" [
|
||||
"X25519MLKEM768"
|
||||
"X25519"
|
||||
"prime256v1"
|
||||
"secp384r1"
|
||||
];
|
||||
# subaddressing
|
||||
recipient_delimiter = cfg.recipientDelimiter;
|
||||
lmtp_save_to_detail_mailbox = cfg.lmtpSaveToDetailMailbox;
|
||||
|
||||
# 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}");
|
||||
# sieve filtering
|
||||
"sieve_script spamfilter" = {
|
||||
# junk filter
|
||||
path = pkgs.writeText "after.sieve" ''
|
||||
require "fileinto";
|
||||
|
||||
passdb = [
|
||||
{
|
||||
if header :is "X-Spam" "Yes" {
|
||||
fileinto "${junkMailboxName}";
|
||||
stop;
|
||||
}
|
||||
'';
|
||||
type = "after";
|
||||
};
|
||||
"sieve_script default" = {
|
||||
# declarative
|
||||
type = "default";
|
||||
path = "${cfg.sieveDirectory}/%{user}/default.sieve";
|
||||
};
|
||||
|
||||
"sieve_script personal" = {
|
||||
# managesieve
|
||||
type = "personal";
|
||||
active_path = "${cfg.sieveDirectory}/%{user}/active.sieve";
|
||||
path = "${cfg.sieveDirectory}/%{user}/scripts";
|
||||
};
|
||||
|
||||
sieve_extensions = {
|
||||
fileinto = true;
|
||||
};
|
||||
sieve_global_extensions = {
|
||||
"vnd.dovecot.pipe" = true;
|
||||
};
|
||||
sieve_plugins = {
|
||||
sieve_imapsieve = true;
|
||||
sieve_extprograms = true;
|
||||
};
|
||||
|
||||
# imapsieve (spam/ham learning)
|
||||
"mailbox ${junkMailboxName}" = {
|
||||
"sieve_script spam" = {
|
||||
cause = [
|
||||
"APPEND"
|
||||
"COPY"
|
||||
];
|
||||
path = ./dovecot/imap_sieve/report-spam.sieve;
|
||||
type = "before";
|
||||
};
|
||||
};
|
||||
"imapsieve_from ${junkMailboxName}" = {
|
||||
"sieve_script ham" = {
|
||||
cause = "copy";
|
||||
path = ./dovecot/imap_sieve/report-ham.sieve;
|
||||
type = "before";
|
||||
};
|
||||
};
|
||||
|
||||
mailbox_list_layout = cfg.storage.directoryLayout;
|
||||
mailbox_list_utf8 = cfg.useUTF8FolderNames;
|
||||
mail_driver = "maildir";
|
||||
mail_path = "~/mail";
|
||||
|
||||
# declarative users
|
||||
"userdb declarative" = {
|
||||
driver = "passwd-file";
|
||||
args = "${passwdFile}";
|
||||
}
|
||||
]
|
||||
++ lib.optionals cfg.ldap.enable [
|
||||
{
|
||||
driver = "ldap";
|
||||
args = "${ldapConfFile}";
|
||||
}
|
||||
];
|
||||
userdb = [
|
||||
{
|
||||
passwd_file_path = userdbFile;
|
||||
fields = {
|
||||
home = "${cfg.storage.path}/%{user | domain}/%{user | username}";
|
||||
inherit (cfg.storage) uid gid;
|
||||
mail_index_path = "${
|
||||
if cfg.indexDir != null then cfg.indexDir else cfg.storage.path
|
||||
}/%{user | domain }/%{user | username}";
|
||||
};
|
||||
};
|
||||
"passdb declarative" = {
|
||||
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 [
|
||||
{
|
||||
passwd_file_path = passwdFile;
|
||||
};
|
||||
|
||||
})
|
||||
(mkIf cfg.ldap.enable {
|
||||
# ldap users
|
||||
ssl_client_ca_file = cfg.ldap.caFile;
|
||||
ssl_client_require_valid_cert = true;
|
||||
|
||||
ldap_version = 3;
|
||||
ldap_uris = cfg.ldap.uris;
|
||||
ldap_starttls = cfg.ldap.startTls;
|
||||
ldap_auth_dn = cfg.ldap.bind.dn;
|
||||
ldap_auth_dn_password = "</run/credentials/dovecot.service/ldap-bind-pw";
|
||||
ldap_base = cfg.ldap.base;
|
||||
ldap_scope = mkLdapSearchScope cfg.ldap.scope;
|
||||
|
||||
"userdb ldap" = {
|
||||
driver = "ldap";
|
||||
args = "${ldapConfFile}";
|
||||
override_fields = [
|
||||
"uid=${toString cfg.storage.uid}"
|
||||
"gid=${toString cfg.storage.gid}"
|
||||
filter = cfg.ldap.dovecot.userFilter;
|
||||
fields = {
|
||||
home = "${cfg.storage.path}/ldap/%{ldap:${cfg.ldap.attributes.uuid}}";
|
||||
inherit (cfg.storage) uid gid;
|
||||
mail_index_path = "${
|
||||
if cfg.indexDir != null then cfg.indexDir else cfg.storage.path
|
||||
}/ldap/%{ldap:${cfg.ldap.attributes.uuid}}";
|
||||
};
|
||||
ldap_connection_group = "ldap-userdb-conn";
|
||||
};
|
||||
"passdb ldap" = {
|
||||
driver = "ldap";
|
||||
filter = cfg.ldap.dovecot.passFilter;
|
||||
fields = {
|
||||
password = "%{ldap:userPassword}";
|
||||
};
|
||||
ldap_connection_group = "ldap-passdb-conn";
|
||||
};
|
||||
})
|
||||
(mkIf cfg.quota.enable {
|
||||
mail_plugins.quota = true;
|
||||
|
||||
"protocol imap".mail_plugins.imap_quota = true;
|
||||
|
||||
"service quota-status" = {
|
||||
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";
|
||||
};
|
||||
|
||||
# default user mailboxes
|
||||
"namespace inbox" = {
|
||||
inbox = true;
|
||||
separator = cfg.hierarchySeparator;
|
||||
}
|
||||
// mapAttrs' (name: value: nameValuePair ''mailbox "${name}"'' value) cfg.mailboxes;
|
||||
lda_mailbox_autosubscribe = true;
|
||||
lda_mailbox_autocreate = true;
|
||||
|
||||
# subaddressing
|
||||
recipient_delimiter = cfg.recipientDelimiter;
|
||||
lmtp_save_to_detail_mailbox = cfg.lmtpSaveToDetailMailbox;
|
||||
|
||||
plugin = {
|
||||
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 (
|
||||
{
|
||||
fts = "flatcurve";
|
||||
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)
|
||||
)
|
||||
// lib.optionalAttrs cfg.quota.enable {
|
||||
quota_rule = mkIf (cfg.quota.defaults.perUser != null) "*:storage=${cfg.quota.defaults.perUser}";
|
||||
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 = true;
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs cfg.debug.dovecot {
|
||||
mail_debug = true;
|
||||
auth_debug = true;
|
||||
verbose_ssl = true;
|
||||
};
|
||||
# quota_storage_grace = "10M";
|
||||
|
||||
"quota user" = {
|
||||
driver = "count";
|
||||
storage_size = mkIf (cfg.quota.defaults.perUser != null) cfg.quota.defaults.perUser;
|
||||
};
|
||||
})
|
||||
(mkIf cfg.fullTextSearch.enable (
|
||||
{
|
||||
mail_plugins = {
|
||||
fts = true;
|
||||
fts_flatcurve = true;
|
||||
};
|
||||
|
||||
"service indexer-worker" = mkIf (cfg.fullTextSearch.memoryLimit != null) {
|
||||
vsz_limit = "${toString cfg.fullTextSearch.memoryLimit} MB";
|
||||
};
|
||||
|
||||
fts_autoindex = cfg.fullTextSearch.autoIndex;
|
||||
fts_driver = "flatcurve";
|
||||
fts_search_add_missing = "yes";
|
||||
fts_search_read_fallback = cfg.fullTextSearch.fallback;
|
||||
fts_header_excludes = lib.genAttrs cfg.fullTextSearch.headerExcludes (_: true);
|
||||
|
||||
"fts flatcurve" = {
|
||||
flatcurve_substring_search = cfg.fullTextSearch.substringSearch;
|
||||
};
|
||||
|
||||
# languages
|
||||
language_filters = lib.genAttrs cfg.fullTextSearch.filters (_: true);
|
||||
language_tokenizer_address_token_maxlen = 100; # default 250 too large for Xapian
|
||||
}
|
||||
# build languages from list, the first one becomes the default language
|
||||
// lib.listToAttrs (
|
||||
lib.imap0 (i: lang: {
|
||||
name = "language ${lang}";
|
||||
value = (if i == 0 then { default = true; } else { }) // {
|
||||
language_tokenizers = [
|
||||
"generic"
|
||||
"email-address"
|
||||
];
|
||||
};
|
||||
}) cfg.fullTextSearch.languages
|
||||
)
|
||||
))
|
||||
(mkIf cfg.debug.dovecot {
|
||||
mail_debug = true;
|
||||
# https://doc.dovecot.org/2.4.3/core/config/events/filter.html#common-unified-filter-language
|
||||
log_debug = "category=ssl OR category=auth";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
systemd.services.dovecot = {
|
||||
preStart = ''
|
||||
${genPasswdScript}
|
||||
''
|
||||
+ (lib.optionalString cfg.ldap.enable setPwdInLdapConfFile);
|
||||
'';
|
||||
reloadTriggers = lib.mkIf (!withACME) [
|
||||
x509CertificateFile
|
||||
x509PrivateKeyFile
|
||||
];
|
||||
serviceConfig = lib.optionalAttrs cfg.ldap.enable {
|
||||
LoadCredential = [
|
||||
"ldap-bind-pw:${cfg.ldap.bind.passwordFile}"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.postfix.restartTriggers = [
|
||||
genPasswdScript
|
||||
]
|
||||
++ (lib.optional cfg.ldap.enable [ setPwdInLdapConfFile ]);
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user