浏览代码

dovecot: migrated config to 2.4 + config splitting

DerLinkman 3 周之前
父节点
当前提交
a77e699c53

+ 85 - 64
data/Dockerfiles/dovecot/docker-entrypoint.sh

@@ -44,66 +44,83 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
 else
   QUOTA_TABLE=quota2replica
 fi
+
+cat <<EOF > /etc/dovecot/conf.d/12-mysql.conf
+# Autogenerated by mailcow - DO NOT TOUCH!
+mysql /var/run/mysqld/mysqld.sock {
+  dbname=${DBNAME}
+  user=${DBUSER}
+  password=${DBPASS}
+
+  ssl = no
+}
+EOF
+
+
 cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-quota.conf
 # Autogenerated by mailcow
-connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
-map {
-  pattern = priv/quota/storage
-  table = ${QUOTA_TABLE}
+dict_map priv/quota/storage {
+  sql_table = ${QUOTA_TABLE}
   username_field = username
-  value_field = bytes
+  value_field bytes {
+  }
 }
-map {
-  pattern = priv/quota/messages
-  table = ${QUOTA_TABLE}
+
+dict_map priv/quota/messages {
+  sql_table = ${QUOTA_TABLE}
   username_field = username
-  value_field = messages
+  value_field messages {
+  }
 }
 EOF
 
 # Create dict used for sieve pre and postfilters
 cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
 # Autogenerated by mailcow
-connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
-map {
-  pattern = priv/sieve/name/\$script_name
-  table = sieve_before
+
+dict_map priv/sieve/name/\$script_name {
+  sql_table = sieve_before
   username_field = username
-  value_field = id
-  fields {
-    script_name = \$script_name
+  value_field id {
+  }
+  key_field script_name {
+    value = \$script_name
   }
 }
-map {
-  pattern = priv/sieve/data/\$id
-  table = sieve_before
+
+dict_map priv/sieve/data/\$id {
+  sql_table = sieve_before
   username_field = username
-  value_field = script_data
-  fields {
-    id = \$id
+  key_field script_data {
+    value = \$script_data
+  }
+  value_field id {
   }
 }
+
+
 EOF
 
 cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
 # Autogenerated by mailcow
-connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
-map {
-  pattern = priv/sieve/name/\$script_name
-  table = sieve_after
+
+dict_map priv/sieve/name/\$script_name {
+  sql_table = sieve_after
   username_field = username
-  value_field = id
-  fields {
-    script_name = \$script_name
+  value_field id {
+  }
+  key_field script_name {
+    value = \$script_name
   }
 }
-map {
-  pattern = priv/sieve/data/\$id
-  table = sieve_after
+
+dict_map priv/sieve/data/\$id {
+  sql_table = sieve_after
   username_field = username
-  value_field = script_data
-  fields {
-    id = \$id
+  key_field script_name {
+    value = \$script_data
+  }
+  value_field id {
   }
 }
 EOF
@@ -116,22 +133,20 @@ fi
 
 if [[ "${SKIP_FTS}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
 echo -e "\e[33mDetecting SKIP_FTS=y... not enabling Flatcurve (FTS) then...\e[0m"
-echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
-echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap
-echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
+echo -n 'quota quota_clone acl mail_crypt mail_crypt_acl mail_log mail_compress notify lazy_expunge' > /etc/dovecot/mail_plugins
+echo -n 'quota quota_clone imap_quota imap_acl acl imap_sieve mail_crypt mail_crypt_acl mail_compress notify mail_log' > /etc/dovecot/mail_plugins_imap
+echo -n 'quota quota_clone sieve acl mail_crypt mail_crypt_acl mail_compress notify' > /etc/dovecot/mail_plugins_lmtp
 else
 echo -e "\e[32mDetecting SKIP_FTS=n... enabling Flatcurve (FTS)\e[0m"
-echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
-echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap
-echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
+echo -n 'quota quota_clone acl mail_crypt mail_crypt_acl mail_log mail_compress notify fts fts_flatcurve lazy_expunge' > /etc/dovecot/mail_plugins
+echo -n 'quota quota_clone imap_quota imap_acl acl imap_sieve mail_crypt mail_crypt_acl mail_compress notify mail_log fts fts_flatcurve' > /etc/dovecot/mail_plugins_imap
+echo -n 'quota quota_clone sieve acl mail_crypt mail_crypt_acl mail_compress fts fts_flatcurve notify' > /etc/dovecot/mail_plugins_lmtp
 fi
 chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl
 
 cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
 # Autogenerated by mailcow
-driver = mysql
-connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
-user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/${MAILDIR_SUB}:VOLATILEDIR=/var/volatile/%u:INDEX=/var/vmail_index/%u') AS mail, '%s' AS protocol, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND (active = '1' OR active = '2')
+query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%{user | domain }}/%{user | username }/Maildir:VOLATILEDIR=/var/volatile/%{user}:INDEX=/var/vmail_index/%{user}') AS mail, '%{protocol}' AS protocol, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%{user}' AND (active = '1' OR active = '2')
 iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2';
 EOF
 
@@ -162,8 +177,8 @@ for cert_dir in /etc/ssl/mail/*/ ; do
   domains=($(cat ${cert_dir}domains))
   for domain in ${domains[@]}; do
     echo 'local_name '${domain}' {' >> /etc/dovecot/sni.conf;
-    echo '  ssl_cert = <'${cert_dir}'cert.pem' >> /etc/dovecot/sni.conf;
-    echo '  ssl_key = <'${cert_dir}'key.pem' >> /etc/dovecot/sni.conf;
+    echo '  ssl_server_cert_file = '${cert_dir}'cert.pem' >> /etc/dovecot/sni.conf;
+    echo '  ssl_server_key_file = '${cert_dir}'key.pem' >> /etc/dovecot/sni.conf;
     echo '}' >> /etc/dovecot/sni.conf;
   done
 done
@@ -187,11 +202,13 @@ else
 fi
 cat <<EOF > /etc/dovecot/shared_namespace.conf
 # Autogenerated by mailcow
-namespace {
+namespace shared {
     type = shared
     separator = /
-    prefix = Shared/%%u/
-    location = maildir:%%h${MAILDIR_SUB_SHARED}:INDEX=~${MAILDIR_SUB_SHARED}/Shared/%%u
+    prefix = Shared/\$user/
+    mail_driver = maildir
+    mail_path = %{owner_home}${MAILDIR_SUB_SHARED}
+    mail_index_private_path = ~${MAILDIR_SUB_SHARED}/Shared/%{owner_user}
     subscriptions = no
     list = children
 }
@@ -201,7 +218,7 @@ EOF
 cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
 # Autogenerated by mailcow
 remote ${IPV4_NETWORK}.248 {
-  disable_plaintext_auth = no
+  auth_allow_cleartext = yes
 }
 EOF
 
@@ -212,9 +229,13 @@ echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
 echo -n ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/cron.creds
 cat <<EOF > /etc/dovecot/sogo-sso.conf
 # Autogenerated by mailcow
-passdb {
-  driver = static
-  args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
+passdb static {
+  fields {
+    allow_real_nets=${IPV4_NETWORK}.248/32
+  }
+
+  password={plain}${RAND_PASS}
+  
 }
 EOF
 
@@ -239,9 +260,9 @@ fi
 if [[ "${SKIP_FTS}" =~ ^([nN][oO]|[nN])+$ ]]; then
   echo -e "\e[94mConfiguring FTS Settings...\e[0m"
   echo -e "\e[94mSetting FTS Memory Limit (per process) to ${FTS_HEAP} MB\e[0m"
-  sed -i "s/vsz_limit\s*=\s*[0-9]*\s*MB*/vsz_limit=${FTS_HEAP} MB/" /etc/dovecot/conf.d/fts.conf
+  sed -i "s/vsz_limit\s*=\s*[0-9]*\s*MB*/vsz_limit=${FTS_HEAP} MB/" /etc/dovecot/conf.d/35-fts.conf
   echo -e "\e[94mSetting FTS Process Limit to ${FTS_PROCS}\e[0m"
-  sed -i "s/process_limit\s*=\s*[0-9]*/process_limit=${FTS_PROCS}/" /etc/dovecot/conf.d/fts.conf
+  sed -i "s/process_limit\s*=\s*[0-9]*/process_limit=${FTS_PROCS}/" /etc/dovecot/conf.d/35-fts.conf
 fi
 
 # 401 is user dovecot
@@ -253,16 +274,16 @@ else
 	chown 401 /mail_crypt/ecprivkey.pem /mail_crypt/ecpubkey.pem
 fi
 
-# Fix OpenSSL 3.X TLS1.0, 1.1 support (https://community.mailcow.email/d/4062-hi-all/20)
-if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.conf /etc/dovecot/extra.conf; then
-    sed -i '/\[openssl_init\]/a ssl_conf = ssl_configuration' /etc/ssl/openssl.cnf
+# # Fix OpenSSL 3.X TLS1.0, 1.1 support (https://community.mailcow.email/d/4062-hi-all/20)
+# if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.conf /etc/dovecot/extra.conf; then
+#     sed -i '/\[openssl_init\]/a ssl_conf = ssl_configuration' /etc/ssl/openssl.cnf
 
-    echo "[ssl_configuration]" >> /etc/ssl/openssl.cnf
-    echo "system_default = tls_system_default" >> /etc/ssl/openssl.cnf
-    echo "[tls_system_default]" >> /etc/ssl/openssl.cnf
-    echo "MinProtocol = TLSv1" >> /etc/ssl/openssl.cnf
-    echo "CipherString = DEFAULT@SECLEVEL=0" >> /etc/ssl/openssl.cnf
-fi
+#     echo "[ssl_configuration]" >> /etc/ssl/openssl.cnf
+#     echo "system_default = tls_system_default" >> /etc/ssl/openssl.cnf
+#     echo "[tls_system_default]" >> /etc/ssl/openssl.cnf
+#     echo "MinProtocol = TLSv1" >> /etc/ssl/openssl.cnf
+#     echo "CipherString = DEFAULT@SECLEVEL=0" >> /etc/ssl/openssl.cnf
+# fi
 
 # Compile sieve scripts
 sievec /var/vmail/sieve/global_sieve_before.sieve

+ 9 - 5
data/conf/dovecot/auth/passwd-verify.lua

@@ -1,4 +1,5 @@
 function auth_password_verify(request, password)
+  request.domain = request.auth_user:match("@(.+)") or nil
   if request.domain == nil then
     return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
   end
@@ -9,10 +10,10 @@ function auth_password_verify(request, password)
   https.TIMEOUT = 30
 
   local req = {
-    username = request.user,
+    username = request.auth_user,
     password = password,
-    real_rip = request.real_rip,
-    service = request.service
+    real_rip = request.remote_ip,
+    service = request.protocol
   }
   local req_json = json.encode(req)
   local res = {}
@@ -33,7 +34,6 @@ function auth_password_verify(request, password)
   -- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry,
   -- even if the TTL has expired. Useful to avoid cache eviction during backend issues.
   if c ~= 200 and c ~= 401 then
-    dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
     return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error"
   end
 
@@ -46,7 +46,7 @@ function auth_password_verify(request, password)
   end
 
   if response_json.success == true then
-    return dovecot.auth.PASSDB_RESULT_OK, ""
+    return dovecot.auth.PASSDB_RESULT_OK, { msg = "" }
   end
 
   return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
@@ -55,3 +55,7 @@ end
 function auth_passdb_lookup(req)
    return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, ""
 end
+
+function auth_passdb_get_cache_key()
+  return "%{protocol}:%{user | username}\t:%{password}"
+end

+ 3 - 0
data/conf/dovecot/conf.d/05-core.conf

@@ -0,0 +1,3 @@
+# /etc/dovecot/conf.d/05-core.conf
+# Core, single-line settings that don't fit elsewhere.
+recipient_delimiter = +

+ 14 - 0
data/conf/dovecot/conf.d/10-logging.conf

@@ -0,0 +1,14 @@
+# /etc/dovecot/conf.d/10-logging.conf
+# Logging and debug.
+#mail_debug = yes
+#auth_debug = yes
+log_debug = category=fts-flatcurve
+#log_debug = category=auth
+log_path = syslog
+log_timestamp = "%Y-%m-%d %H:%M:%S "
+login_log_format_elements = "user=<%{user}> method=%{mechanism} rip=%{remote_ip} lip=%{local_ip} mpid=%{mail_pid} %{secured} session=<%{session}>"
+
+# Mail event logging.
+mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
+mail_log_fields = uid box msgid size
+mail_log_cached_only = yes

+ 9 - 0
data/conf/dovecot/conf.d/10-mail.conf

@@ -0,0 +1,9 @@
+# /etc/dovecot/conf.d/10-mail.conf
+# Mail storage paths and core mail settings.
+mail_home = /var/vmail/%{user | domain }/%{user | username }
+mail_driver = maildir
+mail_path = ~/Maildir
+mail_index_path = /var/vmail_index/%{user}
+mail_plugins = </etc/dovecot/mail_plugins
+mail_shared_explicit_inbox = yes
+mail_prefetch_count = 30

+ 13 - 0
data/conf/dovecot/conf.d/10-ssl.conf

@@ -0,0 +1,13 @@
+# /etc/dovecot/conf.d/10-ssl.conf
+# TLS/SSL settings.
+ssl_min_protocol = TLSv1.2
+ssl_cipher_list = ALL:!ADH:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL:!eNULL:!3DES:!MD5:!PSK:!DSS:!RC4:!SEED:!IDEA:+HIGH:+MEDIUM
+ssl_options = no_ticket
+#ssl_dh_parameters_length = 2048
+
+ssl_server {
+  prefer_ciphers = server
+  dh_file = /etc/ssl/mail/dhparams.pem
+  cert_file = /etc/ssl/mail/cert.pem
+  key_file = /etc/ssl/mail/key.pem
+}

+ 3 - 0
data/conf/dovecot/conf.d/11-sql.conf

@@ -0,0 +1,3 @@
+# /etc/dovecot/conf.d/11-sql.conf
+# Default SQL driver used by SQL-based dicts/userdb.
+sql_driver = mysql

+ 8 - 0
data/conf/dovecot/conf.d/12-mysql.conf

@@ -0,0 +1,8 @@
+# Autogenerated by mailcow - DO NOT TOUCH!
+mysql /var/run/mysqld/mysqld.sock {
+  dbname=mailcow
+  user=mailcow
+  password=D8O9BIivJc7Pb2VCfpAeLbAzUOZ0
+
+  ssl = no
+}

+ 7 - 0
data/conf/dovecot/conf.d/12-storage-attachments.conf

@@ -0,0 +1,7 @@
+# /etc/dovecot/conf.d/12-storage-attachments.conf
+# External attachment storage.
+fs mail_ext_attachment {
+  fs_driver = posix
+  mail_ext_attachment_path = /var/attachments
+  mail_ext_attachment_min_size = 128k
+}

+ 10 - 0
data/conf/dovecot/conf.d/15-performance.conf

@@ -0,0 +1,10 @@
+# /etc/dovecot/conf.d/15-performance.conf
+# Performance and mailbox tuning.
+# Enable only when you do not manually touch cur/.
+maildir_very_dirty_syncs = yes
+
+# NFS examples | Only modify if using NFS!:
+#mm ap_disable = yes
+#mail_fsync = always
+#mail_nfs_index = yes
+#mail_nfs_storage = yes

+ 40 - 0
data/conf/dovecot/conf.d/20-auth.conf

@@ -0,0 +1,40 @@
+# /etc/dovecot/conf.d/20-auth.conf
+# Authentication mechanisms, master/user separation, passdb chain, auth cache.
+auth_mechanisms = plain login
+auth_allow_cleartext = yes
+auth_master_user_separator = *
+
+auth_cache_verify_password_with_worker = yes
+auth_cache_negative_ttl = 60s
+auth_cache_ttl = 300s
+auth_cache_size = 10M
+auth_verbose_passwords = sha1:6
+
+# 1) Lua password verification (blocking, return mapping).
+passdb lua {
+  driver = lua
+  lua_file = /etc/dovecot/auth/passwd-verify.lua
+  lua_settings {
+    blocking=yes
+    result_success = return-ok
+    result_failure = continue
+    result_internalfail = continue
+  }
+}
+
+# 2) Master password for master user logins.
+passdb master {
+  driver = passwd-file
+  passwd_file_path = /etc/dovecot/dovecot-master.passwd 
+  master = yes
+  skip = authenticated
+}
+
+# 3) Mandatory return layer: empty Lua (e.g. for forced reset).
+passdb empty-lua {
+  driver = lua
+  lua_file = /etc/dovecot/auth/passwd-verify.lua
+  lua_settings {
+    blocking = yes
+  }
+}

+ 11 - 0
data/conf/dovecot/conf.d/20-userdb.conf

@@ -0,0 +1,11 @@
+# /etc/dovecot/conf.d/20-userdb.conf
+# User database chain.
+userdb passwd {
+  driver = passwd-file
+  passwd_file_path = /etc/dovecot/dovecot-master.userdb
+}
+
+userdb sql {
+  !include /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
+  skip = found
+}

+ 135 - 0
data/conf/dovecot/conf.d/25-services.conf

@@ -0,0 +1,135 @@
+# /etc/dovecot/conf.d/25-services.conf
+# All service listeners and workers.
+
+# doveadm remote admin
+# Set doveadm_password in extra.conf.
+service doveadm {
+  inet_listener doveadm {
+    port = 12345
+  }
+  vsz_limit = 2048 MB
+}
+
+# dict
+service dict {
+  unix_listener dict {
+    mode = 0660
+    user = vmail
+    group = vmail
+  }
+}
+
+# log
+service log {
+  user = dovenull
+}
+
+# config socket
+service config {
+  unix_listener config {
+    user = root
+    group = vmail
+    mode = 0660
+  }
+}
+
+# auth sockets and inet
+service auth {
+  inet_listener auth-inet {
+    port = 10001
+  }
+  unix_listener auth-master {
+    mode = 0600
+    user = vmail
+  }
+  unix_listener auth-userdb {
+    mode = 0600
+    user = vmail
+  }
+  vsz_limit = 2G
+}
+
+# managesieve login
+service managesieve-login {
+  inet_listener sieve {
+    port = 4190
+  }
+  inet_listener sieve_haproxy {
+    port = 14190
+    haproxy = yes
+  }
+  service_restart_request_count = 1
+  process_min_avail = 2
+  vsz_limit = 1G
+}
+
+# imap login
+service imap-login {
+  service_restart_request_count = 1
+  process_min_avail = 2
+  process_limit = 10000
+  vsz_limit = 1G
+  user = dovenull
+  inet_listener imap_haproxy {
+    port = 10143
+    haproxy = yes
+  }
+  inet_listener imaps_haproxy {
+    port = 10993
+    ssl = yes
+    haproxy = yes
+  }
+}
+
+# pop3 login
+service pop3-login {
+  service_restart_request_count = 1
+  process_min_avail = 1
+  vsz_limit = 1G
+  inet_listener pop3_haproxy {
+    port = 10110
+    haproxy = yes
+  }
+  inet_listener pop3s_haproxy {
+    port = 10995
+    ssl = yes
+    haproxy = yes
+  }
+}
+
+# imap worker
+service imap {
+  executable = imap
+  user = vmail
+  vsz_limit = 1G
+}
+
+# managesieve worker
+service managesieve {
+  process_limit = 256
+}
+
+# lmtp
+service lmtp {
+  inet_listener lmtp-inet {
+    port = 24
+  }
+  user = vmail
+}
+
+# quota warning hook
+service quota-warning {
+  executable = script /usr/local/bin/quota_notify.py
+  user = vmail
+  unix_listener quota-warning {
+    user = vmail
+  }
+}
+
+# stats
+service stats {
+  unix_listener stats-writer {
+    mode = 0660
+    user = vmail
+  }
+}

+ 17 - 0
data/conf/dovecot/conf.d/30-protocols.conf

@@ -0,0 +1,17 @@
+# /etc/dovecot/conf.d/30-protocols.conf
+# IMAP protocol specifics.
+protocol imap {
+  mail_plugins = </etc/dovecot/mail_plugins_imap
+  imap_metadata = yes
+}
+
+# LMTP protocol specifics.
+protocol lmtp {
+  mail_plugins = </etc/dovecot/mail_plugins_lmtp
+  auth_socket_path = /var/run/dovecot/auth-master
+}
+
+# ManageSieve protocol specifics.
+protocol sieve {
+  managesieve_logout_format = bytes=%i/%o
+}

+ 45 - 0
data/conf/dovecot/conf.d/35-fts.conf

@@ -0,0 +1,45 @@
+# mailcow FTS Flatcurve Settings, change them as you like.
+
+# Maximum term length can be set via the 'maxlen' argument (maxlen is
+# specified in bytes, not number of UTF-8 characters)
+language_tokenizer_address_token_maxlen = 100
+language_tokenizer_generic_algorithm = simple
+language_tokenizer_generic_token_maxlen = 30
+
+# These are not flatcurve settings, but required for Dovecot FTS. See
+# Dovecot FTS Configuration link above for further information.
+language en {
+  default = yes
+  language_filters = lowercase snowball english-possessive stopwords
+}
+
+language de {
+  language_filters = lowercase snowball stopwords
+}
+
+language es {
+  language_filters = lowercase snowball stopwords
+}
+
+language_tokenizers = generic email-address
+
+fts_search_timeout = 300s
+
+fts_autoindex = yes
+# Tweak this setting if you only want to ensure big and frequent folders are indexed, not all.
+fts_autoindex_max_recent_msgs = 20
+fts flatcurve {
+  substring_search = no
+}
+
+### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
+
+service indexer-worker {
+  # Max amount of simultaniously running indexer jobs.
+  process_limit=1
+
+  # Max amount of RAM used by EACH indexer process.
+  vsz_limit=128 MB
+}
+
+### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###

+ 12 - 0
data/conf/dovecot/conf.d/40-acl.conf

@@ -0,0 +1,12 @@
+# /etc/dovecot/conf.d/40-acl.conf
+# ACL and shared mailboxes.
+imap_acl_allow_anyone = </etc/dovecot/acl_anyone
+
+acl_sharing_map {
+  dict file {
+    path = /var/vmail/shared-mailboxes.db
+  }
+}
+
+acl_driver = vfile
+acl_user = %{user}

+ 7 - 0
data/conf/dovecot/conf.d/40-attributes.conf

@@ -0,0 +1,7 @@
+# /etc/dovecot/conf.d/40-attributes.conf
+# User/mail attributes.
+mail_attribute {
+  dict file {
+    path = /etc/dovecot/dovecot-attributes
+  }
+}

+ 25 - 0
data/conf/dovecot/conf.d/50-quota.conf

@@ -0,0 +1,25 @@
+# /etc/dovecot/conf.d/50-quota.conf
+# Quota configuration and notifications.
+quota "User quota" {
+  driver = count
+
+  warning warn-95 {
+    quota_storage_percentage = 95
+    execute quota-warning {
+      args = 95 %{user}
+    }
+  }
+
+  warning warn-80 {
+    quota_storage_percentage = 80
+    execute quota-warning {
+      args = 80 %{user}
+    }
+  }
+}
+
+quota_clone {
+  dict proxy {
+    name = mysql_quota
+  }
+}

+ 78 - 0
data/conf/dovecot/conf.d/60-sieve-pipeline.conf

@@ -0,0 +1,78 @@
+# /etc/dovecot/conf.d/60-sieve-pipeline.conf
+# Complete Sieve pipeline: personal/global scripts, plugins, limits, training.
+
+# Personal scripts
+sieve_script personal {
+  driver = file
+  path = ~/sieve
+  active_path = ~/.dovecot.sieve
+}
+
+# Global before/after (file and dict)
+sieve_script before {
+  driver = file
+  path = /var/vmail/sieve/global_sieve_before.sieve
+}
+sieve_script before2 {
+  driver = dict
+  name = active
+  dict proxy {
+    name = sieve_before
+    sieve_script_bin_path = /var/vmail/sieve_before_bindir
+  }
+}
+sieve_script after {
+  driver = file
+  path = /var/vmail/sieve/global_sieve_after.sieve
+}
+sieve_script after2 {
+  driver = dict
+  name = active
+  dict proxy {
+    name = sieve_after
+    sieve_script_bin_path = /var/vmail/sieve_after_bindir
+  }
+}
+
+# Plugins and behavior
+sieve_plugins = sieve_imapsieve sieve_extprograms
+sieve_vacation_send_from_recipient = yes
+sieve_redirect_envelope_from = recipient
+
+# IMAPSieve training
+imapsieve_from Junk {
+  sieve_script ham {
+    type = before
+    cause = copy
+    path = /usr/lib/dovecot/sieve/report-ham.sieve
+  }
+}
+mailbox Junk {
+  sieve_script spam {
+    type = before
+    cause = copy
+    path = /usr/lib/dovecot/sieve/report-spam.sieve
+  }
+}
+
+# Extprograms and extensions
+sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
+sieve_plugins {
+  sieve_extprograms = yes
+}
+sieve_global_extensions {
+  vnd.dovecot.pipe = yes
+  vnd.dovecot.execute = yes
+}
+
+# Limits and duplicate handling
+sieve_max_script_size = 1M
+sieve_max_redirects = 100
+sieve_max_actions = 101
+sieve_quota_script_count = 0
+sieve_quota_storage_size = 0
+sieve_vacation_min_period = 5s
+sieve_vacation_max_period = 365d
+sieve_vacation_default_period = 60s
+sieve_duplicate_default_period = 1m
+sieve_duplicate_max_period = 7d

+ 6 - 0
data/conf/dovecot/conf.d/70-crypto.conf

@@ -0,0 +1,6 @@
+# /etc/dovecot/conf.d/70-crypto.conf
+# Global mail-crypt keys.
+crypt_global_private_key global {
+  crypt_private_key_file = /mail_crypt/ecprivkey.pem
+}
+crypt_global_public_key_file = /mail_crypt/ecpubkey.pem

+ 3 - 0
data/conf/dovecot/conf.d/80-compress.conf

@@ -0,0 +1,3 @@
+# /etc/dovecot/conf.d/80-compress.conf
+# Compression settings.
+mail_compress_write_method = lz4

+ 18 - 0
data/conf/dovecot/conf.d/90-dict.conf

@@ -0,0 +1,18 @@
+# /etc/dovecot/conf.d/90-dict.conf
+# Dict declarations and SQL bindings.
+dict_server {
+  dict sieveafter {
+    driver = sql
+    !include /etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
+  }
+
+  dict sievebefore {
+    driver = sql
+    !include /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
+  }
+
+  dict mysql_quota {
+    driver = sql
+    !include /etc/dovecot/sql/dovecot-dict-sql-quota.conf
+  }
+}

+ 7 - 0
data/conf/dovecot/conf.d/90-limits.conf

@@ -0,0 +1,7 @@
+# /etc/dovecot/conf.d/90-limits.conf
+# Connection and memory limits; doveadm port.
+mail_max_userip_connections = 500
+imap_max_line_length = 2 M
+default_client_limit = 10400
+default_vsz_limit = 1024 M
+doveadm_port = 12345

+ 22 - 0
data/conf/dovecot/conf.d/99-includes.conf

@@ -0,0 +1,22 @@
+# /etc/dovecot/conf.d/99-includes.conf
+# Late includes and site-specific bits.
+
+# Mailbox layout includes (if used)
+!include /etc/dovecot/dovecot.folders.conf
+
+# Optional replication
+!include_try /etc/dovecot/mail_replica.conf
+
+# Existing includes you already had
+!include_try /etc/dovecot/sni.conf
+!include_try /etc/dovecot/sogo_trusted_ip.conf
+!include_try /etc/dovecot/shared_namespace.conf
+!include_try /etc/dovecot/conf.d/fts.conf
+
+# Remote auth override
+remote 127.0.0.1 {
+  auth_allow_cleartext = yes
+}
+
+# Outbound submission target
+submission_host = postfix:588

+ 0 - 37
data/conf/dovecot/conf.d/fts.conf

@@ -1,37 +0,0 @@
-# mailcow FTS Flatcurve Settings, change them as you like.
-plugin {
-    fts_autoindex = yes
-    fts_autoindex_exclude = \Junk
-    fts_autoindex_exclude2 = \Trash
-    # Tweak this setting if you only want to ensure big and frequent folders are indexed, not all.
-    fts_autoindex_max_recent_msgs = 20
-    fts = flatcurve
-
-    # Maximum term length can be set via the 'maxlen' argument (maxlen is
-    # specified in bytes, not number of UTF-8 characters)
-    fts_tokenizer_email_address = maxlen=100
-    fts_tokenizer_generic = algorithm=simple maxlen=30
-
-    # These are not flatcurve settings, but required for Dovecot FTS. See
-    # Dovecot FTS Configuration link above for further information.
-    fts_languages = en es de
-    fts_tokenizers = generic email-address
-
-    # OPTIONAL: Recommended default FTS core configuration
-    fts_filters = normalizer-icu snowball stopwords
-    fts_filters_en = lowercase snowball english-possessive stopwords
-
-    fts_index_timeout = 300s
-}
-
-### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
-
-service indexer-worker {
-  # Max amount of simultaniously running indexer jobs.
-  process_limit=1
-
-  # Max amount of RAM used by EACH indexer process.
-  vsz_limit=128 MB
-}
-
-### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###

+ 28 - 345
data/conf/dovecot/dovecot.conf

@@ -1,351 +1,34 @@
-# --------------------------------------------------------------------------
-# Please create a file "extra.conf" for persistent overrides to dovecot.conf
-# --------------------------------------------------------------------------
-# LDAP example:
-#passdb {
-#  args = /etc/dovecot/ldap/passdb.conf
-#  driver = ldap
-#}
-
+# /etc/dovecot/dovecot.conf
+# Base file kept minimal. All real config lives under conf.d/.
 dovecot_config_version = 2.4.0
 dovecot_storage_version = 2.4.0
 
 listen = *,[::]
-
-auth_mechanisms = plain login
-#mail_debug = yes
-#auth_debug = yes
-#log_debug = category=fts-flatcurve # Activate Logging for Flatcurve FTS Searchings
-log_path = syslog
-auth_allow_cleartext = yes
-# Uncomment on NFS share
-#mmap_disable = yes
-#mail_fsync = always
-#mail_nfs_index = yes
-#mail_nfs_storage = yes
-login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
-mail_home = /var/vmail/%d/%n
-mail_driver = maildir
-mail_path = ~/
-mail_index_path = /var/vmail_index/${user}
-mail_plugins = </etc/dovecot/mail_plugins
-
-fs mail_ext_attachment {
-  fs_driver = posix
-  mail_ext_attachment_path = /var/attachments
-  mail_ext_attachment_min_size = 128k
-}
-
-
-# Significantly speeds up very large mailboxes, but is only safe to enable if
-# you do not manually modify the files in the `cur` directories in
-# mailcowdockerized_vmail-vol-1.
-# https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-performance/
-maildir_very_dirty_syncs = yes
-
-ssl_min_protocol = TLSv1.2
-
-ssl_server {
-  prefer_ciphers = server
-  dh_file = /etc/ssl/mail/dhparams.pem
-  cert_file = /etc/ssl/mail/cert.pem
-  key_file = /etc/ssl/mail/key.pem
-}
-
-ssl_cipher_list = ALL:!ADH:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL:!eNULL:!3DES:!MD5:!PSK:!DSS:!RC4:!SEED:!IDEA:+HIGH:+MEDIUM
-ssl_options = no_compression no_ticket
-
-# Dovecot 2.2
-#ssl_dh_parameters_length = 2048
-log_timestamp = "%Y-%m-%d %H:%M:%S "
-recipient_delimiter = +
-auth_master_user_separator = *
-mail_shared_explicit_inbox = yes
-mail_prefetch_count = 30
-passdb lua {
-  fields {
-    driver = lua
-    args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%s:%u:%w
-    result_success = return-ok
-    result_failure = continue
-    result_internalfail = continue
-  }
-}
-# try a master passwd
-passdb master {
-  fields {
-    driver = passwd-file
-    args = /etc/dovecot/dovecot-master.passwd
-    master = yes
-    skip = authenticated
-  }
-}
-# check for regular password - if empty (e.g. force-passwd-reset), previous pass=yes passdbs also fail
-# a return of the following passdb is mandatory
-passdb empty-lua {
-  fields {
-    driver = lua
-    args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes
-  }
-}
-# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
-service doveadm {
-  inet_listener doveadm {
-    port = 12345
-  }
-  vsz_limit=2048 MB
-}
-!include /etc/dovecot/dovecot.folders.conf
 protocols = imap sieve lmtp pop3
-service dict {
-  unix_listener dict {
-    mode = 0660
-    user = vmail
-    group = vmail
-  }
-}
-service log {
-  user = dovenull
-}
-service config {
-  unix_listener config {
-    user = root
-    group = vmail
-    mode = 0660
-  }
-}
-service auth {
-  inet_listener auth-inet {
-    port = 10001
-  }
-  unix_listener auth-master {
-    mode = 0600
-    user = vmail
-  }
-  unix_listener auth-userdb {
-    mode = 0600
-    user = vmail
-  }
-  vsz_limit = 2G
-}
-service managesieve-login {
-  inet_listener sieve {
-    port = 4190
-  }
-  inet_listener sieve_haproxy {
-    port = 14190
-    haproxy = yes
-  }
-  service_restart_request_count = 1
-  process_min_avail = 2
-  vsz_limit = 1G
-}
-service imap-login {
-  service_restart_request_count = 1
-  process_min_avail = 2
-  process_limit = 10000
-  vsz_limit = 1G
-  user = dovenull
-  inet_listener imap_haproxy {
-    port = 10143
-    haproxy = yes
-  }
-  inet_listener imaps_haproxy {
-    port = 10993
-    ssl = yes
-    haproxy = yes
-  }
-}
-service pop3-login {
-  service_restart_request_count = 1
-  process_min_avail = 1
-  vsz_limit = 1G
-  inet_listener pop3_haproxy {
-    port = 10110
-    haproxy = yes
-  }
-  inet_listener pop3s_haproxy {
-    port = 10995
-    ssl = yes
-    haproxy = yes
-  }
-}
-service imap {
-  executable = imap
-  user = vmail
-  vsz_limit = 1G
-}
-service managesieve {
-  process_limit = 256
-}
-service lmtp {
-  inet_listener lmtp-inet {
-    port = 24
-  }
-  user = vmail
-}
-
-userdb master-userdb {
-  fields {
-    driver = passwd-file
-    args = /etc/dovecot/dovecot-master.userdb
-  }
-}
-userdb sql-userdb {
-  fields {
-    driver = sql
-    args = /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
-    skip = found
-  }
-}
-protocol imap {
-  mail_plugins = </etc/dovecot/mail_plugins_imap
-  imap_metadata = yes
-}
-
-mail_attribute {
-  dict file {
-    path = /etc/dovecot/dovecot-attributes
-  }
-}
-
-protocol lmtp {
-  mail_plugins = </etc/dovecot/mail_plugins_lmtp
-  auth_socket_path = /var/run/dovecot/auth-master
-}
-protocol sieve {
-  managesieve_logout_format = bytes=%i/%o
-}
-
-# Allow "any" or "authenticated" to be used in ACLs
-imap_acl_allow_anyone = </etc/dovecot/acl_anyone
-
-
-acl_sharing_map {
-  dict file {
-    path = /var/vmail/shared-mailboxes.db
-  }
-}
-
-acl_driver = vfile
-acl_user = %u
-
-quota "User quota" {
-  dict sqlquota {
-    driver = sql
-    args = /etc/dovecot/sql/dovecot-dict-sql-quota.conf
-  }
-  quota_rule = *:storage=+100M
-  quota_rule2 = Trash:storage=+100%%
-  quota_warning = storage=95%% quota-warning 95 %u
-  quota_warning2 = storage=80%% quota-warning 80 %u
-} 
-
-sieve = /var/vmail/sieve/%u.sieve
-sieve_plugins = sieve_imapsieve sieve_extprograms
-sieve_vacation_send_from_recipient = yes
-sieve_redirect_envelope_from = recipient
-# From elsewhere to Spam folder
-imapsieve_mailbox1_name = Junk
-imapsieve_mailbox1_causes = COPY
-imapsieve_mailbox1_before = file:/usr/lib/dovecot/sieve/report-spam.sieve
-# END
-# From Spam folder to elsewhere
-imapsieve_mailbox2_name = *
-imapsieve_mailbox2_from = Junk
-imapsieve_mailbox2_causes = COPY
-imapsieve_mailbox2_before = file:/usr/lib/dovecot/sieve/report-ham.sieve
-# END
-master_user = %u
-sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
-sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
-sieve_extensions = +notify +imapflags +vacation-seconds +editheader
-sieve_max_script_size = 1M
-sieve_max_redirects = 100
-sieve_max_actions = 101
-sieve_quota_max_scripts = 0
-sieve_quota_max_storage = 0
-listescape_char = "\\"
-sieve_vacation_min_period = 5s
-sieve_vacation_max_period = 0
-sieve_vacation_default_period = 60s
-sieve_before = /var/vmail/sieve/global_sieve_before.sieve
-sieve_before2 = dict:proxy::sieve_before;name=active;bindir=/var/vmail/sieve_before_bindir
-sieve_after = dict:proxy::sieve_after;name=active;bindir=/var/vmail/sieve_after_bindir
-sieve_after2 = /var/vmail/sieve/global_sieve_after.sieve
-sieve_duplicate_default_period = 1m
-sieve_duplicate_max_period = 7d
-
-# -- Global keys
-mail_crypt_global_private_key = </mail_crypt/ecprivkey.pem
-mail_crypt_global_public_key = </mail_crypt/ecpubkey.pem
-mail_crypt_save_version = 2
-
-# Enable compression while saving, lz4 Dovecot v2.3.17+
-zlib_save = lz4
-
-mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
-mail_log_fields = uid box msgid size
-mail_log_cached_only = yes
-
-# Try set mail_replica
-!include_try /etc/dovecot/mail_replica.conf
 
-service quota-warning {
-  executable = script /usr/local/bin/quota_notify.py
-  # use some unprivileged user for executing the quota warnings
-  user = vmail
-  unix_listener quota-warning {
-    user = vmail
-  }
-}
-dict {
-  sqlquota = mysql:/etc/dovecot/sql/dovecot-dict-sql-quota.conf
-  sieve_after = mysql:/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
-  sieve_before = mysql:/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
-}
-remote 127.0.0.1 {
-  disable_plaintext_auth = no
-}
-submission_host = postfix:588
-mail_max_userip_connections = 500
-service stats {
-  unix_listener stats-writer {
-    mode = 0660
-    user = vmail
-  }
-}
-imap_max_line_length = 2 M
-auth_cache_verify_password_with_worker = yes
-auth_cache_negative_ttl = 60s
-auth_cache_ttl = 300s
-auth_cache_size = 10M
-auth_verbose_passwords = sha1:6
-service replicator {
-  process_min_avail = 1
-}
-service aggregator {
-  fifo_listener replication-notify-fifo {
-    user = vmail
-  }
-  unix_listener replication-notify {
-    user = vmail
-  }
-}
-service replicator {
-  unix_listener replicator-doveadm {
-    mode = 0666
-  }
-}
-replication_max_conns = 10
-doveadm_port = 12345
-replication_dsync_parameters = -d -l 30 -U -n INBOX
-# <Includes>
-!include_try /etc/dovecot/sni.conf
-!include_try /etc/dovecot/sogo_trusted_ip.conf
-!include_try /etc/dovecot/extra.conf
-!include_try /etc/dovecot/shared_namespace.conf
-!include_try /etc/dovecot/conf.d/fts.conf
-# </Includes>
-default_client_limit = 10400
-default_vsz_limit = 1024 M
+!include_try /etc/dovecot/conf.d/05-core.conf
+!include_try /etc/dovecot/conf.d/10-logging.conf
+!include_try /etc/dovecot/conf.d/10-mail.conf
+!include_try /etc/dovecot/conf.d/10-ssl.conf
+!include_try /etc/dovecot/conf.d/11-sql.conf
+!include_try /etc/dovecot/conf.d/12-mysql.conf
+!include_try /etc/dovecot/conf.d/12-storage-attachments.conf
+!include_try /etc/dovecot/conf.d/15-performance.conf
+!include_try /etc/dovecot/conf.d/20-auth.conf
+!include_try /etc/dovecot/conf.d/20-userdb.conf
+!include_try /etc/dovecot/conf.d/25-services.conf
+!include_try /etc/dovecot/conf.d/30-protocols.conf
+!include_try /etc/dovecot/conf.d/35-fts.conf
+!include_try /etc/dovecot/conf.d/40-acl.conf
+!include_try /etc/dovecot/conf.d/40-attributes.conf
+!include_try /etc/dovecot/conf.d/50-quota.conf
+!include_try /etc/dovecot/conf.d/60-sieve-pipeline.conf
+!include_try /etc/dovecot/conf.d/70-crypto.conf
+!include_try /etc/dovecot/conf.d/80-compress.conf
+!include_try /etc/dovecot/conf.d/80-mail-logging.conf
+!include_try /etc/dovecot/conf.d/90-limits.conf
+!include_try /etc/dovecot/conf.d/90-dict.conf
+!include_try /etc/dovecot/conf.d/99-includes.conf
+
+# Last: local overrides
+!include_try /etc/dovecot/extra.conf

+ 6 - 0
data/conf/dovecot/dovecot.folders.conf

@@ -1,9 +1,14 @@
 namespace inbox {
   inbox = yes
   separator = /
+  mailbox storage/* {
+    quota_storage_extra = 100M
+  }
   mailbox "Trash" {
     auto = subscribe
     special_use = \Trash
+    quota_storage_percentage = 100
+    fts_autoindex = no
   }
   mailbox "Deleted Messages" {
     special_use = \Trash
@@ -194,6 +199,7 @@ namespace inbox {
   mailbox "Junk" {
     auto = subscribe
     special_use = \Junk
+    fts_autoindex = no
   }
   mailbox "Junk-E-Mail" {
     special_use = \Junk