From bd96c1161e9f9b32bb0b3aaec4b930980a56945f Mon Sep 17 00:00:00 2001 From: Vincent Ducamps Date: Sat, 30 Sep 2023 13:20:03 +0200 Subject: [PATCH] feat: Allow changing the Dovecot vmail UID/GID via ENV (#3550) Some deployment scenarios are not compatible with `5000:5000` static vmail user with `/var/mail`. This feature allows adjusting the defaults to a UID / GID that is compatible. Signed-off-by: vincent Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- docs/content/config/environment.md | 12 ++++ mailserver.env | 6 ++ target/scripts/helpers/accounts.sh | 4 +- target/scripts/helpers/utils.sh | 4 +- target/scripts/start-mailserver.sh | 1 + target/scripts/startup/setup.d/vmail-id.sh | 12 ++++ target/scripts/startup/variables-stack.sh | 2 + test/tests/serial/vmail-id.bats | 75 ++++++++++++++++++++++ 8 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 target/scripts/startup/setup.d/vmail-id.sh create mode 100644 test/tests/serial/vmail-id.bats diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index e5aba889..678ec965 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -33,6 +33,18 @@ Here you can adjust the [log-level for Supervisor](http://supervisord.org/loggin The log-level will show everything in its class and above. +##### DMS_VMAIL_UID + +Default: 5000 + +The User ID assigned to the static vmail user for `/var/mail` (_Mail storage managed by Dovecot_). + +##### DMS_VMAIL_GID + +Default: 5000 + +The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage managed by Dovecot_). + ##### ONE_DIR - 0 => state in default directories. diff --git a/mailserver.env b/mailserver.env index 632f6edf..1a57ceca 100644 --- a/mailserver.env +++ b/mailserver.env @@ -34,6 +34,12 @@ SUPERVISOR_LOGLEVEL= # 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes ONE_DIR=1 +# Support for deployment where these defaults are not compatible (eg: some NAS appliances): +# /var/mail vmail User ID (default: 5000) +DMS_VMAIL_UID= +# /var/mail vmail Group ID (default: 5000) +DMS_VMAIL_GID= + # **empty** => use FILE # LDAP => use LDAP authentication # OIDC => use OIDC authentication (not yet implemented) diff --git a/target/scripts/helpers/accounts.sh b/target/scripts/helpers/accounts.sh index 82b626c1..7499a2ec 100644 --- a/target/scripts/helpers/accounts.sh +++ b/target/scripts/helpers/accounts.sh @@ -70,7 +70,7 @@ function _create_accounts() { # Dovecot's userdb has the following format # user:password:uid:gid:(gecos):home:(shell):extra_fields - DOVECOT_USERDB_LINE="${LOGIN}:${PASS}:5000:5000::/var/mail/${DOMAIN}/${USER}/home::${USER_ATTRIBUTES}" + DOVECOT_USERDB_LINE="${LOGIN}:${PASS}:${DMS_VMAIL_UID}:${DMS_VMAIL_GID}::/var/mail/${DOMAIN}/${USER}/home::${USER_ATTRIBUTES}" if grep -qF "${DOVECOT_USERDB_LINE}" "${DOVECOT_USERDB_FILE}"; then _log 'warn' "Login '${LOGIN}' will not be added to '${DOVECOT_USERDB_FILE}' twice" else @@ -141,7 +141,7 @@ function _create_dovecot_alias_dummy_accounts() { fi fi - DOVECOT_USERDB_LINE="${ALIAS}:${REAL_ACC[1]}:5000:5000::/var/mail/${REAL_DOMAINNAME}/${REAL_USERNAME}::${REAL_ACC[2]:-}" + DOVECOT_USERDB_LINE="${ALIAS}:${REAL_ACC[1]}:${DMS_VMAIL_UID}:${DMS_VMAIL_GID}::/var/mail/${REAL_DOMAINNAME}/${REAL_USERNAME}::${REAL_ACC[2]:-}" if grep -qi "^${ALIAS}:" "${DOVECOT_USERDB_FILE}"; then _log 'warn' "Alias '${ALIAS}' will not be added to '${DOVECOT_USERDB_FILE}' twice" else diff --git a/target/scripts/helpers/utils.sh b/target/scripts/helpers/utils.sh index c74fd7e8..e44f0aff 100644 --- a/target/scripts/helpers/utils.sh +++ b/target/scripts/helpers/utils.sh @@ -39,9 +39,9 @@ function _get_dms_env_value() { # /var/mail folders (used during startup and change detection handling). function _chown_var_mail_if_necessary() { # fix permissions, but skip this if 3 levels deep the user id is already set - if find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | read -r; then + if find /var/mail -maxdepth 3 -a \( \! -user "${DMS_VMAIL_UID}" -o \! -group "${DMS_VMAIL_GID}" \) | read -r; then _log 'trace' 'Fixing /var/mail permissions' - chown -R 5000:5000 /var/mail || return 1 + chown -R "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" /var/mail || return 1 fi } diff --git a/target/scripts/start-mailserver.sh b/target/scripts/start-mailserver.sh index e41ae71e..f0f385f3 100755 --- a/target/scripts/start-mailserver.sh +++ b/target/scripts/start-mailserver.sh @@ -39,6 +39,7 @@ function _register_functions() { # ? >> Setup + _register_setup_function '_setup_vmail_id' _register_setup_function '_setup_logs_general' _register_setup_function '_setup_timezone' diff --git a/target/scripts/startup/setup.d/vmail-id.sh b/target/scripts/startup/setup.d/vmail-id.sh new file mode 100644 index 00000000..2b692169 --- /dev/null +++ b/target/scripts/startup/setup.d/vmail-id.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +function _setup_vmail_id() { + if [[ "${DMS_VMAIL_UID}" != "5000" ]]; then + _log 'debug' "Setting 'docker' UID to ${DMS_VMAIL_UID}" + usermod --uid "${DMS_VMAIL_UID}" docker + fi + if [[ "${DMS_VMAIL_GID}" != "5000" ]]; then + _log 'debug' "Setting 'docker' GID to ${DMS_VMAIL_GID}" + groupmod --gid "${DMS_VMAIL_GID}" docker + fi +} diff --git a/target/scripts/startup/variables-stack.sh b/target/scripts/startup/variables-stack.sh index b18a61dc..3b575f50 100644 --- a/target/scripts/startup/variables-stack.sh +++ b/target/scripts/startup/variables-stack.sh @@ -46,6 +46,8 @@ function __environment_variables_general_setup() { VARS[POSTMASTER_ADDRESS]="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}" VARS[REPORT_RECIPIENT]="${REPORT_RECIPIENT:=${POSTMASTER_ADDRESS}}" VARS[REPORT_SENDER]="${REPORT_SENDER:=mailserver-report@${HOSTNAME}}" + VARS[DMS_VMAIL_UID]="${DMS_VMAIL_UID:=5000}" + VARS[DMS_VMAIL_GID]="${DMS_VMAIL_GID:=5000}" _log 'trace' 'Setting anti-spam & anti-virus environment variables' diff --git a/test/tests/serial/vmail-id.bats b/test/tests/serial/vmail-id.bats new file mode 100644 index 00000000..b44670b2 --- /dev/null +++ b/test/tests/serial/vmail-id.bats @@ -0,0 +1,75 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +BATS_TEST_NAME_PREFIX='[ENV] (DMS_VMAIL_UID + DMS_VMAIL_GID) ' +CONTAINER_NAME='dms-test_env-change-vmail-id' + +function setup_file() { + _init_with_defaults + + local CUSTOM_SETUP_ARGUMENTS=( + --env PERMIT_DOCKER=container + --env DMS_VMAIL_UID=9042 + --env DMS_VMAIL_GID=9042 + ) + + _common_container_setup 'CUSTOM_SETUP_ARGUMENTS' + _wait_for_smtp_port_in_container +} + +function teardown_file() { _default_teardown ; } + +@test 'should successfully deliver mail' { + _send_email 'email-templates/existing-user1' + _wait_for_empty_mail_queue_in_container + + # Should be successfully sent (received) by Postfix: + _run_in_container grep 'to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 + + # Verify successful delivery via Dovecot to `/var/mail` account by searching for the subject: + _repeat_in_container_until_success_or_timeout 20 "${CONTAINER_NAME}" grep -R \ + 'Subject: Test Message existing-user1.txt' \ + '/var/mail/localhost.localdomain/user1/new/' + assert_success + _should_output_number_of_lines 1 +} + +# TODO: Migrate to test/helper/common.bash +# This test case is shared with tests.bats, but provides context on errors + some minor edits +# TODO: Could improve in future with keywords from https://github.com/docker-mailserver/docker-mailserver/pull/3550#issuecomment-1738509088 +# Potentially via a helper that allows an optional fixed number of errors to be present if they were intentional +@test '/var/log/mail/mail.log is error free' { + # Postfix: https://serverfault.com/questions/934703/postfix-451-4-3-0-temporary-lookup-failure + _run_in_container grep 'non-null host address bits in' /var/log/mail/mail.log + assert_failure + + # Postfix delivery failure: https://github.com/docker-mailserver/docker-mailserver/issues/230 + _run_in_container grep 'mail system configuration error' /var/log/mail/mail.log + assert_failure + + # Unknown error source: https://github.com/docker-mailserver/docker-mailserver/pull/85 + _run_in_container grep -i ': error:' /var/log/mail/mail.log + assert_failure + + # Unknown error source: https://github.com/docker-mailserver/docker-mailserver/pull/320 + _run_in_container grep -i 'not writable' /var/log/mail/mail.log + assert_failure + _run_in_container grep -i 'permission denied' /var/log/mail/mail.log + assert_failure + + # Amavis: https://forum.howtoforge.com/threads/postfix-smtp-error-caused-by-clamav-cant-connect-to-a-unix-socket-var-run-clamav-clamd-ctl.81002/ + _run_in_container grep -i '(!)connect' /var/log/mail/mail.log + assert_failure + + # Postfix: https://github.com/docker-mailserver/docker-mailserver/pull/2597 + _run_in_container grep -i 'using backwards-compatible default setting' /var/log/mail/mail.log + assert_failure + + # Postgrey: https://github.com/docker-mailserver/docker-mailserver/pull/612#discussion_r117635774 + _run_in_container grep -i 'connect to 127.0.0.1:10023: Connection refused' /var/log/mail/mail.log + assert_failure +} +