From a77e699c534dd9d7683cb20c75de09d4a84f737f Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Thu, 14 Aug 2025 11:11:35 +0200 Subject: [PATCH] dovecot: migrated config to 2.4 + config splitting --- data/Dockerfiles/dovecot/docker-entrypoint.sh | 149 ++++--- data/conf/dovecot/auth/passwd-verify.lua | 14 +- data/conf/dovecot/conf.d/05-core.conf | 3 + data/conf/dovecot/conf.d/10-logging.conf | 14 + data/conf/dovecot/conf.d/10-mail.conf | 9 + data/conf/dovecot/conf.d/10-ssl.conf | 13 + data/conf/dovecot/conf.d/11-sql.conf | 3 + data/conf/dovecot/conf.d/12-mysql.conf | 8 + .../conf.d/12-storage-attachments.conf | 7 + data/conf/dovecot/conf.d/15-performance.conf | 10 + data/conf/dovecot/conf.d/20-auth.conf | 40 ++ data/conf/dovecot/conf.d/20-userdb.conf | 11 + data/conf/dovecot/conf.d/25-services.conf | 135 +++++++ data/conf/dovecot/conf.d/30-protocols.conf | 17 + data/conf/dovecot/conf.d/35-fts.conf | 45 +++ data/conf/dovecot/conf.d/40-acl.conf | 12 + data/conf/dovecot/conf.d/40-attributes.conf | 7 + data/conf/dovecot/conf.d/50-quota.conf | 25 ++ .../dovecot/conf.d/60-sieve-pipeline.conf | 78 ++++ data/conf/dovecot/conf.d/70-crypto.conf | 6 + data/conf/dovecot/conf.d/80-compress.conf | 3 + data/conf/dovecot/conf.d/90-dict.conf | 18 + data/conf/dovecot/conf.d/90-limits.conf | 7 + data/conf/dovecot/conf.d/99-includes.conf | 22 ++ data/conf/dovecot/conf.d/fts.conf | 37 -- data/conf/dovecot/dovecot.conf | 371 ++---------------- data/conf/dovecot/dovecot.folders.conf | 6 + 27 files changed, 620 insertions(+), 450 deletions(-) create mode 100644 data/conf/dovecot/conf.d/05-core.conf create mode 100644 data/conf/dovecot/conf.d/10-logging.conf create mode 100644 data/conf/dovecot/conf.d/10-mail.conf create mode 100644 data/conf/dovecot/conf.d/10-ssl.conf create mode 100644 data/conf/dovecot/conf.d/11-sql.conf create mode 100644 data/conf/dovecot/conf.d/12-mysql.conf create mode 100644 data/conf/dovecot/conf.d/12-storage-attachments.conf create mode 100644 data/conf/dovecot/conf.d/15-performance.conf create mode 100644 data/conf/dovecot/conf.d/20-auth.conf create mode 100644 data/conf/dovecot/conf.d/20-userdb.conf create mode 100644 data/conf/dovecot/conf.d/25-services.conf create mode 100644 data/conf/dovecot/conf.d/30-protocols.conf create mode 100644 data/conf/dovecot/conf.d/35-fts.conf create mode 100644 data/conf/dovecot/conf.d/40-acl.conf create mode 100644 data/conf/dovecot/conf.d/40-attributes.conf create mode 100644 data/conf/dovecot/conf.d/50-quota.conf create mode 100644 data/conf/dovecot/conf.d/60-sieve-pipeline.conf create mode 100644 data/conf/dovecot/conf.d/70-crypto.conf create mode 100644 data/conf/dovecot/conf.d/80-compress.conf create mode 100644 data/conf/dovecot/conf.d/90-dict.conf create mode 100644 data/conf/dovecot/conf.d/90-limits.conf create mode 100644 data/conf/dovecot/conf.d/99-includes.conf delete mode 100644 data/conf/dovecot/conf.d/fts.conf diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 4854bd6f3..5c2c07cde 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -44,66 +44,83 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then else QUOTA_TABLE=quota2replica fi + +cat < /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 < /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 < /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 < /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 < /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 < /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 < /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 < /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 diff --git a/data/conf/dovecot/auth/passwd-verify.lua b/data/conf/dovecot/auth/passwd-verify.lua index ea847932d..3fe43b6b4 100644 --- a/data/conf/dovecot/auth/passwd-verify.lua +++ b/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 \ No newline at end of file diff --git a/data/conf/dovecot/conf.d/05-core.conf b/data/conf/dovecot/conf.d/05-core.conf new file mode 100644 index 000000000..e934e9724 --- /dev/null +++ b/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 = + \ No newline at end of file diff --git a/data/conf/dovecot/conf.d/10-logging.conf b/data/conf/dovecot/conf.d/10-logging.conf new file mode 100644 index 000000000..f3211ecb7 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/data/conf/dovecot/conf.d/10-mail.conf b/data/conf/dovecot/conf.d/10-mail.conf new file mode 100644 index 000000000..4d658449c --- /dev/null +++ b/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 = -!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 -# -default_client_limit = 10400 -default_vsz_limit = 1024 M +# Last: local overrides +!include_try /etc/dovecot/extra.conf \ No newline at end of file diff --git a/data/conf/dovecot/dovecot.folders.conf b/data/conf/dovecot/dovecot.folders.conf index 8de8daec6..4d73d14f3 100644 --- a/data/conf/dovecot/dovecot.folders.conf +++ b/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