dovecot: migrate to dovecot 2.4
This commit is contained in:
+25
-30
@@ -587,6 +587,7 @@ in
|
|||||||
Full text search indexing with Xapian through the fts_flatcurve plugin.
|
Full text search indexing with Xapian through the fts_flatcurve plugin.
|
||||||
This has significant performance and disk space cost.
|
This has significant performance and disk space cost.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
memoryLimit = mkOption {
|
memoryLimit = mkOption {
|
||||||
type = types.nullOr types.int;
|
type = types.nullOr types.int;
|
||||||
default = null;
|
default = null;
|
||||||
@@ -603,31 +604,13 @@ in
|
|||||||
default = true;
|
default = true;
|
||||||
description = "Enable automatic indexing of messages as they are received or modified.";
|
description = "Enable automatic indexing of messages as they are received or modified.";
|
||||||
};
|
};
|
||||||
autoIndexExclude = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ ];
|
|
||||||
example = [
|
|
||||||
"\\Trash"
|
|
||||||
"SomeFolder"
|
|
||||||
"Other/*"
|
|
||||||
];
|
|
||||||
description = ''
|
|
||||||
Mailboxes to exclude from automatic indexing.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
enforced = mkOption {
|
fallback = mkOption {
|
||||||
type = types.enum [
|
type = types.bool;
|
||||||
"yes"
|
default = true;
|
||||||
"no"
|
|
||||||
"body"
|
|
||||||
];
|
|
||||||
default = "no";
|
|
||||||
description = ''
|
description = ''
|
||||||
Fail searches when no index is available. If set to
|
Whether to fallback to slow non-indexed search, if FTS lookup and
|
||||||
`body`, then only body searches (as opposed to
|
indexing have failed.
|
||||||
header) are affected. If set to `no`, searches may
|
|
||||||
fall back to a very slow brute force search.
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -640,9 +623,11 @@ in
|
|||||||
];
|
];
|
||||||
description = ''
|
description = ''
|
||||||
A list of languages that the full text search should detect.
|
A list of languages that the full text search should detect.
|
||||||
At least one language must be specified.
|
|
||||||
The language listed first is the default and is used when language recognition fails.
|
At least one language must be specified. The language listed first is
|
||||||
See <https://doc.dovecot.org/main/core/plugins/fts.html#fts_languages>.
|
the default and is used when language recognition fails.
|
||||||
|
|
||||||
|
See <https://doc.dovecot.org/main/core/plugins/fts.html#languages>.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -679,8 +664,8 @@ in
|
|||||||
"stopwords"
|
"stopwords"
|
||||||
];
|
];
|
||||||
description = ''
|
description = ''
|
||||||
The list of filters to apply.
|
The list of language filters to apply.
|
||||||
<https://doc.dovecot.org/main/core/plugins/fts.html#filter-configuration>.
|
See <https://doc.dovecot.org/main/core/plugins/fts.html#filter-configuration>.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -825,9 +810,9 @@ in
|
|||||||
directoryLayout = mkOption {
|
directoryLayout = mkOption {
|
||||||
type = types.enum [
|
type = types.enum [
|
||||||
"fs"
|
"fs"
|
||||||
"maildir++"
|
"Maildir++"
|
||||||
];
|
];
|
||||||
default = "maildir++";
|
default = "Maildir++";
|
||||||
description = ''
|
description = ''
|
||||||
Sets whether dovecot should organize mail in subdirectories:
|
Sets whether dovecot should organize mail in subdirectories:
|
||||||
|
|
||||||
@@ -924,10 +909,12 @@ in
|
|||||||
Trash = {
|
Trash = {
|
||||||
auto = "no";
|
auto = "no";
|
||||||
special_use = "\\Trash";
|
special_use = "\\Trash";
|
||||||
|
fts_autoindex = false;
|
||||||
};
|
};
|
||||||
Junk = {
|
Junk = {
|
||||||
auto = "subscribe";
|
auto = "subscribe";
|
||||||
special_use = "\\Junk";
|
special_use = "\\Junk";
|
||||||
|
fts_autoindex = false;
|
||||||
};
|
};
|
||||||
Drafts = {
|
Drafts = {
|
||||||
auto = "subscribe";
|
auto = "subscribe";
|
||||||
@@ -1784,5 +1771,13 @@ in
|
|||||||
(mkChangedOptionModule [ "mailserver" "useFSLayout" ] [ "mailserver" "storage" "directoryLayout" ] (
|
(mkChangedOptionModule [ "mailserver" "useFSLayout" ] [ "mailserver" "storage" "directoryLayout" ] (
|
||||||
config: if config.mailserver.useFSLayout then "fs" else "maildir++"
|
config: if config.mailserver.useFSLayout then "fs" else "maildir++"
|
||||||
))
|
))
|
||||||
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "enforced" ] ''
|
||||||
|
Whether to fallback to non-indexed search is now controlled via
|
||||||
|
`mailserver.fullTextSearch.fallback`. Missing mails are now always indexed,
|
||||||
|
since flatcurve is very fast.
|
||||||
|
'')
|
||||||
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "autoIndexExclude" ] ''
|
||||||
|
Configure `fts_autoindex` on mail directories in `mailserver.mailboxes` instead.
|
||||||
|
'')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -20,13 +20,13 @@ To enable indexing for full text search here is an example configuration.
|
|||||||
enable = true;
|
enable = true;
|
||||||
# index new email as they arrive
|
# index new email as they arrive
|
||||||
autoIndex = true;
|
autoIndex = true;
|
||||||
enforced = "body";
|
fallback = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
The ``enforced`` parameter tells dovecot to fail any body search query that cannot
|
The ``fallback`` parameter tells dovecot to fail any body search query that cannot
|
||||||
use an index. This prevents dovecot to fall back to the IO-intensive brute
|
use an index. This prevents dovecot to fall back to the IO-intensive brute
|
||||||
force search.
|
force search.
|
||||||
|
|
||||||
|
|||||||
+235
-226
@@ -39,6 +39,7 @@ let
|
|||||||
mapAttrs'
|
mapAttrs'
|
||||||
mkForce
|
mkForce
|
||||||
mkIf
|
mkIf
|
||||||
|
mkMerge
|
||||||
nameValuePair
|
nameValuePair
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -47,56 +48,6 @@ let
|
|||||||
passwdDir = "/run/dovecot2";
|
passwdDir = "/run/dovecot2";
|
||||||
passwdFile = "${passwdDir}/passwd";
|
passwdFile = "${passwdDir}/passwd";
|
||||||
userdbFile = "${passwdDir}/userdb";
|
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 =
|
genPasswdScript =
|
||||||
pkgs.writeScript "generate-password-file"
|
pkgs.writeScript "generate-password-file"
|
||||||
@@ -130,7 +81,7 @@ let
|
|||||||
if lib.elem name accountsWithPlaintextPasswordFiles then
|
if lib.elem name accountsWithPlaintextPasswordFiles then
|
||||||
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' config.services.dovecot2.package "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}:{CRYPT}${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
||||||
) cfg.accounts
|
) cfg.accounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
@@ -140,8 +91,11 @@ let
|
|||||||
${lib.concatStringsSep "\n" (
|
${lib.concatStringsSep "\n" (
|
||||||
lib.mapAttrsToList (
|
lib.mapAttrsToList (
|
||||||
name: value:
|
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}:::::::"
|
"${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
|
) cfg.accounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
@@ -213,60 +167,36 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# for sieve-test. Shelling it in on demand usually doesn't work, as it reads
|
# Dovecot modules
|
||||||
# the global config and tries to open shared libraries configured in there,
|
|
||||||
# which are usually not compatible.
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.dovecot_pigeonhole_0_5
|
pkgs.dovecot_pigeonhole
|
||||||
]
|
];
|
||||||
++ lib.optional cfg.fullTextSearch.enable pkgs.dovecot-fts-flatcurve;
|
|
||||||
|
|
||||||
# For compatibility with python imaplib
|
# For compatibility with python imaplib
|
||||||
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;
|
||||||
|
package = pkgs.dovecot; # pin over stateVersion logic in nixox 26.05
|
||||||
enablePAM = mkForce false;
|
enablePAM = mkForce false;
|
||||||
|
|
||||||
sieve = {
|
sieve.pipeBins = map lib.getExe [
|
||||||
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-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")
|
(pkgs.writeShellScriptBin "rspamd-learn-spam.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_spam")
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
imapsieve.mailbox = [
|
# https://doc.dovecot.org/2.4.3/core/settings/syntax.html
|
||||||
{
|
# https://doc.dovecot.org/2.4.3/core/settings/types.html#boolean-list
|
||||||
name = junkMailboxName;
|
settings = mkMerge [
|
||||||
causes = [
|
({
|
||||||
"COPY"
|
# https://doc.dovecot.org/main/core/summaries/settings.html#dovecot_config_version
|
||||||
"APPEND"
|
dovecot_config_version = "2.4.3";
|
||||||
];
|
# https://doc.dovecot.org/main/core/summaries/settings.html#dovecot_storage_version
|
||||||
before = ./dovecot/imap_sieve/report-spam.sieve;
|
dovecot_storage_version = "2.3.21.1";
|
||||||
}
|
|
||||||
{
|
# server identity
|
||||||
name = "*";
|
hostname = cfg.fqdn;
|
||||||
from = junkMailboxName;
|
|
||||||
causes = [ "COPY" ];
|
|
||||||
before = ./dovecot/imap_sieve/report-ham.sieve;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
# vmail user
|
# vmail user
|
||||||
mail_uid = cfg.storage.owner;
|
mail_uid = cfg.storage.owner;
|
||||||
mail_gid = cfg.storage.group;
|
mail_gid = cfg.storage.group;
|
||||||
@@ -277,64 +207,49 @@ in
|
|||||||
"plain"
|
"plain"
|
||||||
"login"
|
"login"
|
||||||
];
|
];
|
||||||
disable_plaintext_auth = true;
|
|
||||||
|
|
||||||
# hostname
|
|
||||||
hostname = cfg.fqdn;
|
|
||||||
|
|
||||||
# backend services
|
# backend services
|
||||||
|
"service anvil" = {
|
||||||
|
"unix_listener anvil" = {
|
||||||
|
mode = "0660";
|
||||||
|
group = cfg.storage.group;
|
||||||
|
};
|
||||||
|
};
|
||||||
"service auth" = {
|
"service auth" = {
|
||||||
"unix_listener auth" = {
|
"unix_listener auth" = {
|
||||||
mode = "0660";
|
|
||||||
user = config.services.postfix.user;
|
user = config.services.postfix.user;
|
||||||
group = config.services.postfix.group;
|
group = config.services.postfix.group;
|
||||||
|
mode = "0660";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"service indexer-worker" = mkIf (cfg.fullTextSearch.memoryLimit != null) {
|
|
||||||
vsz_limit = "${toString cfg.fullTextSearch.memoryLimit} MB";
|
|
||||||
};
|
|
||||||
"service lmtp" = {
|
"service lmtp" = {
|
||||||
"unix_listener dovecot-lmtp" = {
|
"unix_listener dovecot-lmtp" = {
|
||||||
|
user = config.services.postfix.user;
|
||||||
group = config.services.postfix.group;
|
group = config.services.postfix.group;
|
||||||
mode = "0600";
|
mode = "0600";
|
||||||
user = config.services.postfix.user;
|
|
||||||
};
|
};
|
||||||
user = cfg.storage.owner;
|
user = cfg.storage.owner;
|
||||||
vsz_limit = "${toString cfg.lmtpMemoryLimit} MB";
|
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
|
# frontend services
|
||||||
"service imap-login" = mkIf (cfg.enableImap || cfg.enableImapSsl) {
|
"service imap-login" = mkIf (cfg.enableImap || cfg.enableImapSsl) {
|
||||||
"inet_listener imap" = {
|
"inet_listener imap" = {
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
# https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
port = if cfg.enableImap then 143 else 0;
|
port = if cfg.enableImap then 143 else 0;
|
||||||
};
|
};
|
||||||
"inet_listener imaps" = {
|
"inet_listener imaps" = mkIf cfg.enableImapSsl {
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
port = 993;
|
||||||
port = if cfg.enableImapSsl then 993 else 0;
|
|
||||||
ssl = true;
|
ssl = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"service pop3-login" = mkIf (cfg.enablePop3 || cfg.enablePop3Ssl) {
|
"service pop3-login" = mkIf (cfg.enablePop3 || cfg.enablePop3Ssl) {
|
||||||
"inet_listener pop3" = {
|
"inet_listener pop3" = {
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
# https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
port = if cfg.enablePop3 then 110 else 0;
|
port = if cfg.enablePop3 then 110 else 0;
|
||||||
};
|
};
|
||||||
"inet_listener pop3s" = {
|
"inet_listener pop3s" = mkIf cfg.enablePop3Ssl {
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
port = 995;
|
||||||
port = if cfg.enablePop3Ssl then 995 else 0;
|
|
||||||
ssl = true;
|
ssl = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -343,52 +258,35 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
# protocols
|
# protocols
|
||||||
protocols = [
|
protocols = {
|
||||||
"lmtp"
|
lmtp = true;
|
||||||
]
|
imap = cfg.enableImap || cfg.enableImapSsl;
|
||||||
++ lib.optionals (cfg.enableImap || cfg.enableImapSsl) [ "imap" ]
|
pop3 = cfg.enablePop3 || cfg.enablePop3Ssl;
|
||||||
++ lib.optionals (cfg.enablePop3 || cfg.enablePop3Ssl) [ "pop3" ]
|
sieve = cfg.enableManageSieve;
|
||||||
++ lib.optionals cfg.enableManageSieve [ "sieve" ];
|
};
|
||||||
|
|
||||||
"protocol lmtp" = {
|
"protocol lmtp" = {
|
||||||
mail_plugins = [
|
mail_plugins = {
|
||||||
"$mail_plugins"
|
sieve = true;
|
||||||
"sieve"
|
};
|
||||||
];
|
|
||||||
};
|
};
|
||||||
"protocol imap" = {
|
"protocol imap" = {
|
||||||
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
||||||
mail_plugins = [
|
mail_plugins = {
|
||||||
"$mail_plugins"
|
imap_sieve = true;
|
||||||
"imap_sieve"
|
};
|
||||||
]
|
|
||||||
++ lib.optionals cfg.quota.enable [
|
|
||||||
"imap_quota"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
"protocol pop3" = {
|
"protocol pop3" = {
|
||||||
mail_max_userip_connections = cfg.maxConnectionsPerUser;
|
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"
|
|
||||||
];
|
|
||||||
|
|
||||||
# tls settings
|
# tls settings
|
||||||
ssl_cert = "<${x509CertificateFile}";
|
ssl_server_cert_file = x509CertificateFile;
|
||||||
ssl_key = "<${x509PrivateKeyFile}";
|
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
|
# 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 = false;
|
ssl_server_prefer_ciphers = "client";
|
||||||
ssl_cipher_list = lib.concatStringsSep ":" [
|
ssl_cipher_list = lib.concatStringsSep ":" [
|
||||||
# TLS1.3
|
# TLS1.3
|
||||||
"TLS_AES_128_GCM_SHA256"
|
"TLS_AES_128_GCM_SHA256"
|
||||||
@@ -411,46 +309,6 @@ in
|
|||||||
"secp384r1"
|
"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}";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
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}"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# default user mailboxes
|
# default user mailboxes
|
||||||
"namespace inbox" = {
|
"namespace inbox" = {
|
||||||
inbox = true;
|
inbox = true;
|
||||||
@@ -464,59 +322,210 @@ in
|
|||||||
recipient_delimiter = cfg.recipientDelimiter;
|
recipient_delimiter = cfg.recipientDelimiter;
|
||||||
lmtp_save_to_detail_mailbox = cfg.lmtpSaveToDetailMailbox;
|
lmtp_save_to_detail_mailbox = cfg.lmtpSaveToDetailMailbox;
|
||||||
|
|
||||||
plugin = {
|
# sieve filtering
|
||||||
sieve = "file:${cfg.sieveDirectory}/%{user}/scripts;active=${cfg.sieveDirectory}/%{user}/active.sieve";
|
"sieve_script spamfilter" = {
|
||||||
sieve_default = "file:${cfg.sieveDirectory}/%{user}/default.sieve";
|
# junk filter
|
||||||
sieve_default_name = "default";
|
path = pkgs.writeText "after.sieve" ''
|
||||||
|
require "fileinto";
|
||||||
|
|
||||||
|
if header :is "X-Spam" "Yes" {
|
||||||
|
fileinto "${junkMailboxName}";
|
||||||
|
stop;
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs cfg.fullTextSearch.enable (
|
'';
|
||||||
{
|
type = "after";
|
||||||
fts = "flatcurve";
|
};
|
||||||
fts_languages = cfg.fullTextSearch.languages;
|
"sieve_script default" = {
|
||||||
fts_tokenizers = [
|
# declarative
|
||||||
"generic"
|
type = "default";
|
||||||
"email-address"
|
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"
|
||||||
];
|
];
|
||||||
fts_tokenizer_email_address = "maxlen=100"; # default 254 too large for Xapian
|
path = ./dovecot/imap_sieve/report-spam.sieve;
|
||||||
fts_flatcurve_substring_search = cfg.fullTextSearch.substringSearch;
|
type = "before";
|
||||||
fts_filters = cfg.fullTextSearch.filters;
|
};
|
||||||
fts_header_excludes = cfg.fullTextSearch.headerExcludes;
|
};
|
||||||
fts_autoindex = cfg.fullTextSearch.autoIndex;
|
"imapsieve_from ${junkMailboxName}" = {
|
||||||
fts_enforced = cfg.fullTextSearch.enforced;
|
"sieve_script ham" = {
|
||||||
}
|
cause = "copy";
|
||||||
// (listToMultiAttrs "fts_autoindex_exclude" cfg.fullTextSearch.autoIndexExclude)
|
path = ./dovecot/imap_sieve/report-ham.sieve;
|
||||||
)
|
type = "before";
|
||||||
// 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
|
|
||||||
|
mailbox_list_layout = cfg.storage.directoryLayout;
|
||||||
|
mailbox_list_utf8 = cfg.useUTF8FolderNames;
|
||||||
|
mail_driver = "maildir";
|
||||||
|
mail_path = "~/mail";
|
||||||
|
|
||||||
|
# declarative users
|
||||||
|
"userdb declarative" = {
|
||||||
|
driver = "passwd-file";
|
||||||
|
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";
|
||||||
|
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";
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
|
||||||
quota_status_success = "DUNNO";
|
quota_status_success = "DUNNO";
|
||||||
quota_status_nouser = "DUNNO";
|
quota_status_nouser = "DUNNO";
|
||||||
quota_status_overquota = "552 5.2.2 Mailbox is full";
|
quota_status_overquota = "552 5.2.2 Mailbox is full";
|
||||||
quota_grace = "10%%";
|
# quota_storage_grace = "10M";
|
||||||
quota_vsizes = true;
|
|
||||||
|
"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
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs cfg.debug.dovecot {
|
# build languages from list, the first one becomes the default language
|
||||||
mail_debug = true;
|
// lib.listToAttrs (
|
||||||
auth_debug = true;
|
lib.imap0 (i: lang: {
|
||||||
verbose_ssl = true;
|
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 = {
|
systemd.services.dovecot = {
|
||||||
preStart = ''
|
preStart = ''
|
||||||
${genPasswdScript}
|
${genPasswdScript}
|
||||||
''
|
'';
|
||||||
+ (lib.optionalString cfg.ldap.enable setPwdInLdapConfFile);
|
|
||||||
reloadTriggers = lib.mkIf (!withACME) [
|
reloadTriggers = lib.mkIf (!withACME) [
|
||||||
x509CertificateFile
|
x509CertificateFile
|
||||||
x509PrivateKeyFile
|
x509PrivateKeyFile
|
||||||
];
|
];
|
||||||
|
serviceConfig = lib.optionalAttrs cfg.ldap.enable {
|
||||||
|
LoadCredential = [
|
||||||
|
"ldap-bind-pw:${cfg.ldap.bind.passwordFile}"
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.postfix.restartTriggers = [
|
systemd.services.postfix.restartTriggers = [
|
||||||
genPasswdScript
|
genPasswdScript
|
||||||
]
|
];
|
||||||
++ (lib.optional cfg.ldap.enable [ setPwdInLdapConfFile ]);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-7
@@ -81,7 +81,7 @@
|
|||||||
};
|
};
|
||||||
"lowquota@example.com" = {
|
"lowquota@example.com" = {
|
||||||
hashedPassword = "$6$u61JrAtuI0a$nGEEfTP5.eefxoScUGVG/Tl0alqla2aGax4oTd85v3j3xSmhv/02gNfSemv/aaMinlv9j/ZABosVKBrRvN5Qv0";
|
hashedPassword = "$6$u61JrAtuI0a$nGEEfTP5.eefxoScUGVG/Tl0alqla2aGax4oTd85v3j3xSmhv/02gNfSemv/aaMinlv9j/ZABosVKBrRvN5Qv0";
|
||||||
quota = "1B";
|
quota = "1K";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,13 +98,13 @@
|
|||||||
fullTextSearch = {
|
fullTextSearch = {
|
||||||
enable = true;
|
enable = true;
|
||||||
autoIndex = true;
|
autoIndex = true;
|
||||||
# special use depends on https://github.com/NixOS/nixpkgs/pull/93201
|
fallback = false;
|
||||||
autoIndexExclude = [
|
|
||||||
(if (pkgs.lib.versionAtLeast pkgs.lib.version "21") then "\\Junk" else "Junk")
|
|
||||||
];
|
|
||||||
enforced = "yes";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# by default quota can be exceeded once with this amount (default: 10M)
|
||||||
|
# this is required to make the quota subtest hard fail on the first attempt.
|
||||||
|
services.dovecot2.settings.quota_storage_grace = "0";
|
||||||
};
|
};
|
||||||
client =
|
client =
|
||||||
{ nodes, pkgs, ... }:
|
{ nodes, pkgs, ... }:
|
||||||
@@ -306,7 +306,21 @@
|
|||||||
|
|
||||||
Hello User1,
|
Hello User1,
|
||||||
|
|
||||||
how are you doing today?
|
how are you doing today? I have this exciting text for you, that helps fill
|
||||||
|
your quota.
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
|
||||||
|
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
|
||||||
|
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
|
||||||
|
no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit
|
||||||
|
amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
|
||||||
|
labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam
|
||||||
|
et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata
|
||||||
|
sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur
|
||||||
|
sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore
|
||||||
|
magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo
|
||||||
|
dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est
|
||||||
|
Lorem ipsum dolor sit amet.
|
||||||
|
|
||||||
XOXO User1
|
XOXO User1
|
||||||
'';
|
'';
|
||||||
@@ -514,6 +528,8 @@
|
|||||||
client.execute("rm ~/mail/*")
|
client.execute("rm ~/mail/*")
|
||||||
client.execute("mv ~/.fetchmailRcLowQuota ~/.fetchmailrc")
|
client.execute("mv ~/.fetchmailRcLowQuota ~/.fetchmailrc")
|
||||||
|
|
||||||
|
server.log(server.succeed("doveadm quota get -u lowquota@example.com"))
|
||||||
|
|
||||||
client.succeed(
|
client.succeed(
|
||||||
"msmtp -a test3 --tls=on --tls-certcheck=off --auth=on lowquota@example.com < /etc/root/email2 >&2"
|
"msmtp -a test3 --tls=on --tls-certcheck=off --auth=on lowquota@example.com < /etc/root/email2 >&2"
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-2
@@ -78,6 +78,8 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.services.dovecot.serviceConfig.CacheDirectory = "dovecot";
|
||||||
|
|
||||||
mailserver = {
|
mailserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
fqdn = "mail.example.com";
|
fqdn = "mail.example.com";
|
||||||
@@ -114,7 +116,7 @@ in
|
|||||||
group = "vmail";
|
group = "vmail";
|
||||||
};
|
};
|
||||||
|
|
||||||
indexDir = "/var/lib/dovecot/indices";
|
indexDir = "/var/cache/dovecot/fts";
|
||||||
|
|
||||||
enableImap = false;
|
enableImap = false;
|
||||||
};
|
};
|
||||||
@@ -219,7 +221,8 @@ in
|
|||||||
with subtest("Check dovecot maildir and index locations"):
|
with subtest("Check dovecot maildir and index locations"):
|
||||||
# If these paths change we need a migration
|
# If these paths change we need a migration
|
||||||
machine.succeed("doveadm user -f home user1@example.com | grep ${nodes.machine.mailserver.storage.path}/example.com/user1")
|
machine.succeed("doveadm user -f home user1@example.com | grep ${nodes.machine.mailserver.storage.path}/example.com/user1")
|
||||||
machine.succeed("doveadm user -f mail user1@example.com | grep 'maildir:~/mail:INDEX=${nodes.machine.mailserver.indexDir}/example.com/user1'")
|
machine.succeed("doveadm user -f mail_path user1@example.com | grep ${nodes.machine.mailserver.storage.path}/example.com/user1/mail")
|
||||||
|
machine.succeed("doveadm user -f mail_index_path user1@example.com | grep ${nodes.machine.mailserver.indexDir}/example.com/user1")
|
||||||
|
|
||||||
with subtest("mail to send only accounts is rejected"):
|
with subtest("mail to send only accounts is rejected"):
|
||||||
machine.wait_for_open_port(25)
|
machine.wait_for_open_port(25)
|
||||||
|
|||||||
+18
-9
@@ -18,7 +18,6 @@ let
|
|||||||
alicePassword = "testalice";
|
alicePassword = "testalice";
|
||||||
bobPassword = "testbob";
|
bobPassword = "testbob";
|
||||||
carolPassword = "testcarol";
|
carolPassword = "testcarol";
|
||||||
frankPassword = "testfrank";
|
|
||||||
malloryPassword = "testmallory";
|
malloryPassword = "testmallory";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -76,7 +75,8 @@ in
|
|||||||
objectClass: simpleSecurityObject
|
objectClass: simpleSecurityObject
|
||||||
objectClass: top
|
objectClass: top
|
||||||
cn: mail
|
cn: mail
|
||||||
userPassword: ${bindPassword}
|
# unsafegibberish
|
||||||
|
userPassword: {SSHA}JNr6l3s/RHo1LKRXqFsJg8sXznyRid8L
|
||||||
|
|
||||||
dn: ou=users,dc=example
|
dn: ou=users,dc=example
|
||||||
objectClass: organizationalUnit
|
objectClass: organizationalUnit
|
||||||
@@ -88,7 +88,8 @@ in
|
|||||||
uid: alice
|
uid: alice
|
||||||
sn: Foo
|
sn: Foo
|
||||||
mail: alice@example.com
|
mail: alice@example.com
|
||||||
userPassword: ${alicePassword}
|
# testalice
|
||||||
|
userPassword: {SSHA}gkJq4Dm4jfIKjxviR0WD63wMt0Ti6zMB
|
||||||
|
|
||||||
dn: cn=bob,ou=users,dc=example
|
dn: cn=bob,ou=users,dc=example
|
||||||
entryUUID: f3b4e8ea-087f-42cc-95f0-cbfd99386092
|
entryUUID: f3b4e8ea-087f-42cc-95f0-cbfd99386092
|
||||||
@@ -100,7 +101,8 @@ in
|
|||||||
sn: Bar
|
sn: Bar
|
||||||
mail: bob@example.com
|
mail: bob@example.com
|
||||||
homeDirectory: /home/bob
|
homeDirectory: /home/bob
|
||||||
userPassword: ${bobPassword}
|
# testbob
|
||||||
|
userPassword: {SSHA}qqUveZGZrDrjYFnREXLDZc//y89RppVN
|
||||||
|
|
||||||
dn: cn=carol,ou=users,dc=example
|
dn: cn=carol,ou=users,dc=example
|
||||||
entryUUID: 41240499-27e2-4fa2-be4f-4113a77661b1
|
entryUUID: 41240499-27e2-4fa2-be4f-4113a77661b1
|
||||||
@@ -108,7 +110,8 @@ in
|
|||||||
uid: carol
|
uid: carol
|
||||||
sn: Baz
|
sn: Baz
|
||||||
mail: carol@example.com
|
mail: carol@example.com
|
||||||
userPassword: ${carolPassword}
|
# testcarol
|
||||||
|
userPassword: {SSHA}69HOuP+OPWE+3+tDucFZxzXDC7p4e3ML
|
||||||
|
|
||||||
dn: cn=frank,ou=users,dc=example
|
dn: cn=frank,ou=users,dc=example
|
||||||
entryUUID: ca16f594-f6b2-418f-87d3-0d02d746461f
|
entryUUID: ca16f594-f6b2-418f-87d3-0d02d746461f
|
||||||
@@ -116,17 +119,23 @@ in
|
|||||||
uid: frank
|
uid: frank
|
||||||
sn: Moo
|
sn: Moo
|
||||||
mail: frank@example.com
|
mail: frank@example.com
|
||||||
userPassword: ${frankPassword}
|
# testfrank
|
||||||
|
userPassword: {SSHA}xqtMl8/uJ6HEFWDzLYpAE+Wq7FvKrtkm
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.services.dovecot.serviceConfig = {
|
||||||
|
CacheDirectory = "dovecot";
|
||||||
|
StateDirectory = "dovecot";
|
||||||
|
};
|
||||||
|
|
||||||
mailserver = {
|
mailserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
fqdn = "mail.example.com";
|
fqdn = "mail.example.com";
|
||||||
domains = [ "example.com" ];
|
domains = [ "example.com" ];
|
||||||
localDnsResolver = false;
|
localDnsResolver = false;
|
||||||
storage.path = "/var/lib/dovecot/vmail";
|
storage.path = "/var/lib/dovecot/vmail";
|
||||||
indexDir = "/var/lib/dovecot/indices";
|
indexDir = "/var/cache/dovecot/indices";
|
||||||
|
|
||||||
aliases = {
|
aliases = {
|
||||||
# Steal frank@example.com from LDAP user frank
|
# Steal frank@example.com from LDAP user frank
|
||||||
@@ -215,11 +224,11 @@ in
|
|||||||
machine.succeed("doveadm user -f gid bob@example.com | grep ${toString nodes.machine.mailserver.storage.uid}")
|
machine.succeed("doveadm user -f gid bob@example.com | grep ${toString nodes.machine.mailserver.storage.uid}")
|
||||||
|
|
||||||
machine.succeed("doveadm user -f home bob@example.com | grep ${nodes.machine.mailserver.storage.path}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092")
|
machine.succeed("doveadm user -f home bob@example.com | grep ${nodes.machine.mailserver.storage.path}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092")
|
||||||
machine.succeed("doveadm user -f mail bob@example.com | grep 'maildir:~/mail:INDEX=${nodes.machine.mailserver.indexDir}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092'")
|
machine.succeed("doveadm user -f mail_path bob@example.com | grep ${nodes.machine.mailserver.storage.path}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092")
|
||||||
|
machine.succeed("doveadm user -f mail_index_path bob@example.com | grep ${nodes.machine.mailserver.indexDir}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092")
|
||||||
|
|
||||||
with subtest("Files containing secrets are only readable by root"):
|
with subtest("Files containing secrets are only readable by root"):
|
||||||
machine.succeed("ls -l /run/postfix/*.cf | grep -e '-rw------- 1 root root'")
|
machine.succeed("ls -l /run/postfix/*.cf | grep -e '-rw------- 1 root root'")
|
||||||
machine.succeed("ls -l /run/dovecot2/dovecot-ldap.conf.ext | grep -e '-rw------- 1 root root'")
|
|
||||||
|
|
||||||
with subtest("Test account/mail address binding via explicit TLS"):
|
with subtest("Test account/mail address binding via explicit TLS"):
|
||||||
machine.fail(" ".join([
|
machine.fail(" ".join([
|
||||||
|
|||||||
Reference in New Issue
Block a user