From ab55343d8ee34ccdf562804b2c098a9938b026c8 Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Mon, 22 Aug 2022 08:31:32 +0200 Subject: [PATCH] scripts: rework environment variables setup (#2716) * outsourcing env variable setup This commit contains major parts of the work of refactoring the setup and usage of environment variables. It outsources the setup into its own script and provides dedicated functions to be executed at a later point in time. A **new** env variable was added: `USER_PROVISIONG` which provides a better way of defining which method / protocol to use when it comes to setting up users. This way, the `ENABLE_LDAP` variable is deprecated, but all of this is backwards compatible due to a "compatibility layer", a function provided by the new variables script. This is not a breaking change. It mostly refators internal scripts. The only change facing the user-side is the deprecation of `ENABLE_LDAP`. We can prolong the period of deprecation for this variable as long as we want, because the new function that ensures backwards compatibility provides a clean interface for the future. Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Co-authored-by: Casper --- README.md | 3 +- docs/content/config/advanced/auth-ldap.md | 16 +- docs/content/config/environment.md | 17 +- docs/content/contributing/coding-style.md | 4 +- ...nly-mailserver-with-ldap-authentication.md | 7 +- mailserver.env | 7 + target/scripts/helpers/accounts.sh | 3 +- target/scripts/helpers/dns.sh | 3 + target/scripts/helpers/postfix.sh | 2 +- target/scripts/helpers/variables.sh | 212 ++++++++++++++++++ target/scripts/start-mailserver.sh | 170 +++----------- target/scripts/startup/setup-stack.sh | 39 ++-- test/mail_with_ldap.bats | 4 +- 13 files changed, 302 insertions(+), 185 deletions(-) create mode 100644 target/scripts/helpers/variables.sh diff --git a/README.md b/README.md index 2d242002..b6a3a8f3 100644 --- a/README.md +++ b/README.md @@ -293,7 +293,8 @@ services: - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - ONE_DIR=1 - - ENABLE_LDAP=1 + - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER + - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap # your ldap container/IP/ServerName - LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain - LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain diff --git a/docs/content/config/advanced/auth-ldap.md b/docs/content/config/advanced/auth-ldap.md index 424220d9..1770bed6 100644 --- a/docs/content/config/advanced/auth-ldap.md +++ b/docs/content/config/advanced/auth-ldap.md @@ -34,7 +34,9 @@ Those variables contain the LDAP lookup filters for postfix, using `%s` as the p A really simple `LDAP_QUERY_FILTER` configuration, using only the _user filter_ and allowing only `admin@*` to spoof any sender addresses. ```yaml - - ENABLE_LDAP=1 + - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER + - LDAP_START_TLS=yes + - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_SEARCH_BASE=dc=example,dc=org" - LDAP_BIND_DN=cn=admin,dc=example,dc=org @@ -138,7 +140,7 @@ In addition to LDAP explanation above, when Docker Mailserver is intended to be The configuration shown to get the Group to work is from [here](https://doc.zarafa.com/trunk/Administrator_Manual/en-US/html/_MTAIntegration.html) and [here](https://kb.kopano.io/display/WIKI/Postfix). -``` +```bash # user-patches.sh ... @@ -149,7 +151,7 @@ grep -q '^special_result_attribute = member$' /etc/postfix/ldap-groups.cf || ech - In `/etc/ldap/ldap.conf`, if the `TLS_REQCERT` is `demand` / `hard` (default), the CA certificate used to verify the LDAP server certificate must be recognized as a trusted CA. This can be done by volume mounting the `ca.crt` file and updating the trust store via a `user-patches.sh` script: -``` +```bash # user-patches.sh ... @@ -160,7 +162,7 @@ update-ca-certificates The changes on the configurations necessary to work with Active Directory (**only changes are listed, the rest of the LDAP configuration can be taken from the other examples** shown in this documentation): -``` +```yaml # If StartTLS is the chosen method to establish a secure connection with Active Directory. - LDAP_START_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes @@ -215,7 +217,8 @@ The changes on the configurations necessary to work with Active Directory (**onl - ENABLE_POSTGREY=1 # >>> Postfix LDAP Integration - - ENABLE_LDAP=1 + - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER + - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org - LDAP_BIND_PW=mypassword @@ -287,7 +290,8 @@ The changes on the configurations necessary to work with Active Directory (**onl # <<< SASL Authentication # >>> Postfix Ldap Integration - - ENABLE_LDAP=1 + - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER + - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST= - LDAP_SEARCH_BASE=dc=mydomain,dc=loc - LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index e89603e4..09591cec 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -40,6 +40,17 @@ The log-level will show everything in its class and above. - 0 => state in default directories. - **1** => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes. See the [related FAQ entry][docs-faq-onedir] for more information. +##### ACCOUNT_PROVISIONER + +Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_). + +User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713). + +- **empty** => use FILE +- LDAP => use LDAP authentication +- OIDC => use OIDC authentication (**not yet implemented**) +- FILE => use local files (this is used as the default) + ##### PERMIT_DOCKER Set different options for mynetworks option (can be overwrite in postfix-main.cf) **WARNING**: Adding the docker network's gateway to the list of trusted hosts, e.g. using the `network` or `connected-networks` option, can create an [**open relay**](https://en.wikipedia.org/wiki/Open_mail_relay), for instance if IPv6 is enabled on the host machine but not in Docker. @@ -439,11 +450,7 @@ Note: The defaults of your fetchmailrc file need to be at the top of the file. O ##### ENABLE_LDAP -- **empty** => LDAP authentification is disabled -- 1 => LDAP authentification is enabled -- NOTE: - - A second container for the ldap service is necessary (e.g. [docker-openldap](https://github.com/osixia/docker-openldap)) - - For preparing the ldap server to use in combination with this container [this](http://acidx.net/wordpress/2014/06/installing-a-mailserver-with-postfix-dovecot-sasl-ldap-roundcube/) article may be helpful +Deprecated. See [`ACCOUNT_PROVISIONER`](#account_provisioner). ##### LDAP_START_TLS diff --git a/docs/content/contributing/coding-style.md b/docs/content/contributing/coding-style.md index 11d95d47..06a1d6ba 100644 --- a/docs/content/contributing/coding-style.md +++ b/docs/content/contributing/coding-style.md @@ -130,9 +130,7 @@ function _setup_postfix_aliases cp -f /tmp/docker-mailserver/postfix-virtual.cf /etc/postfix/virtual - # the `to` is important, don't delete it - # shellcheck disable=SC2034 - while read -r FROM TO + while read -r FROM _ do # Setting variables for better readability UNAME=$(echo "${FROM}" | cut -d @ -f1) diff --git a/docs/content/examples/use-cases/forward-only-mailserver-with-ldap-authentication.md b/docs/content/examples/use-cases/forward-only-mailserver-with-ldap-authentication.md index 65b75d62..acb07fff 100644 --- a/docs/content/examples/use-cases/forward-only-mailserver-with-ldap-authentication.md +++ b/docs/content/examples/use-cases/forward-only-mailserver-with-ldap-authentication.md @@ -30,7 +30,8 @@ We can create aliases with `./setup.sh`, like this: If you want to send emails from outside the mail-server you have to authenticate somehow (with a username and password). One way of doing it is described in [this discussion][github-issue-1247]. However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on `mailserver.env` are: ```env -ENABLE_LDAP=1 +ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER +ACCOUNT_PROVISIONER=LDAP LDAP_START_TLS=yes LDAP_SERVER_HOST=ldap.example.org LDAP_SEARCH_BASE=ou=users,dc=example,dc=org @@ -104,9 +105,5 @@ You see that besides `query_filter`, I had to customize as well `result_attribut Another solution that serves as a forward-only mail-server is [this](https://gitlab.com/docker-scripts/postfix). -!!! tip - - One user reports only having success if `ENABLE_LDAP=0` was set. - [docs-userpatches]: ../../config/advanced/override-defaults/user-patches.md [github-issue-1247]: https://github.com/docker-mailserver/docker-mailserver/issues/1247 diff --git a/mailserver.env b/mailserver.env index dc577928..4bd28bd0 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 +# **empty** => use FILE +# LDAP => use LDAP authentication +# OIDC => use OIDC authentication (not yet implemented) +# FILE => use local files (this is used as the default) +ACCOUNT_PROVISIONER= + # empty => postmaster@domain.com # => Specify the postmaster address POSTMASTER_ADDRESS= @@ -324,6 +330,7 @@ FETCHMAIL_POLL=300 # A second container for the ldap service is necessary (i.e. https://github.com/osixia/docker-openldap) # For preparing the ldap server to use in combination with this container this article may be helpful: http://acidx.net/wordpress/2014/06/installing-a-mailserver-with-postfix-dovecot-sasl-ldap-roundcube/ +# with the :edge tag, use ACCOUNT_PROVISIONER=LDAP # empty => LDAP authentification is disabled # 1 => LDAP authentification is enabled ENABLE_LDAP= diff --git a/target/scripts/helpers/accounts.sh b/target/scripts/helpers/accounts.sh index 92d06615..52e7c371 100644 --- a/target/scripts/helpers/accounts.sh +++ b/target/scripts/helpers/accounts.sh @@ -14,10 +14,11 @@ function _create_accounts : >/etc/postfix/vmailbox : >"${DOVECOT_USERDB_FILE}" - [[ ${ENABLE_LDAP} -eq 1 ]] && return 0 + [[ ${ACCOUNT_PROVISIONER} == 'FILE' ]] || return 0 local DATABASE_ACCOUNTS='/tmp/docker-mailserver/postfix-accounts.cf' _create_masters + if [[ -f ${DATABASE_ACCOUNTS} ]] then _log 'trace' "Checking file line endings" diff --git a/target/scripts/helpers/dns.sh b/target/scripts/helpers/dns.sh index 96199273..01b5fd0d 100644 --- a/target/scripts/helpers/dns.sh +++ b/target/scripts/helpers/dns.sh @@ -17,6 +17,9 @@ function _obtain_hostname_and_domainname # TODO: Consider changing to `DMS_FQDN`; a more accurate name, and removing the `export`, assuming no # subprocess like postconf would be called that would need access to the same value via `$HOSTNAME` ENV. # + # ! There is already a stub in variables.sh which contains DMS_FQDN. One will just need to uncomment the + # ! correct lines in variables.sh. + # # TODO: `OVERRIDE_HOSTNAME` was introduced for non-Docker runtimes that could not configure an explicit hostname. # Kubernetes was the particular runtime in 2017. This does not update `/etc/hosts` or other locations, thus risking # inconsistency with expected behaviour. Investigate if it's safe to remove support. (--net=host also uses this as a workaround) diff --git a/target/scripts/helpers/postfix.sh b/target/scripts/helpers/postfix.sh index 0d36670e..f81a4955 100644 --- a/target/scripts/helpers/postfix.sh +++ b/target/scripts/helpers/postfix.sh @@ -80,7 +80,7 @@ function _vhost_collect_postfix_domains # conditionally include a 2nd table (ldap:/etc/postfix/ldap-domains.cf). function _vhost_ldap_support { - [[ ${ENABLE_LDAP} -eq 1 ]] && echo "${DOMAINNAME}" >>"${TMP_VHOST}" + [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] && echo "${DOMAINNAME}" >>"${TMP_VHOST}" } # Docs - Postfix lookup table files: diff --git a/target/scripts/helpers/variables.sh b/target/scripts/helpers/variables.sh new file mode 100644 index 00000000..2d7b972c --- /dev/null +++ b/target/scripts/helpers/variables.sh @@ -0,0 +1,212 @@ +#! /bin/bash + +# shellcheck disable=SC2034 +declare -A VARS + +# shellcheck disable=SC2034 +declare -a FUNCS_SETUP FUNCS_FIX FUNCS_CHECK FUNCS_MISC DAEMONS_START + +# This function handles variables that are deprecated. This allows a +# smooth transition period, without the need of removing a variable +# completely with a single version. +function _environment_variables_backwards_compatibility +{ + if [[ ${ENABLE_LDAP:-0} -eq 1 ]] + then + _log 'warn' "'ENABLE_LDAP=1' is deprecated (and will be removed in v13.0.0) => use 'ACCOUNT_PROVISIONER=LDAP' instead" + ACCOUNT_PROVISIONER='LDAP' + fi + + # TODO this can be uncommented in a PR handling the HOSTNAME/DOMAINNAME issue + # TODO see check_for_changes.sh and dns.sh + # if [[ -n ${OVERRIDE_HOSTNAME:-} ]] + # then + # _log 'warn' "'OVERRIDE_HOSTNAME' is deprecated (and will be removed in v13.0.0) => use 'DMS_FQDN' instead" + # [[ -z ${DMS_FQDN} ]] && DMS_FQDN=${OVERRIDE_HOSTNAME} + # fi +} + +# This function Writes the contents of the `VARS` map (associative array) +# to locations where they can be sourced from (e.g. `/etc/dms-settings`) +# or where they can be used by Bash directly (e.g. `/root/.bashrc`). +function _environment_variables_export +{ + _log 'debug' "Exporting environment variables now (creating '/etc/dms-settings')" + + : >/root/.bashrc # make DMS variables available in login shells and their subprocesses + : >/etc/dms-settings # this file can be sourced by other scripts + + local VAR + for VAR in "${!VARS[@]}" + do + echo "export ${VAR}='${VARS[${VAR}]}'" >>/root/.bashrc + echo "${VAR}='${VARS[${VAR}]}'" >>/etc/dms-settings + done + + sort -o /root/.bashrc /root/.bashrc + sort -o /etc/dms-settings /etc/dms-settings +} + +# This function sets almost all environment variables. This involves setting +# a default if no value was provided and writing the variable and its value +# to the VARS map. +function _environment_variables_general_setup +{ + _log 'debug' 'Handling general environment variable setup' + + # these variables must be defined first + # they are used as default values for other variables + + VARS[POSTMASTER_ADDRESS]="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}" + VARS[REPORT_RECIPIENT]="${REPORT_RECIPIENT:=${POSTMASTER_ADDRESS}}" + VARS[REPORT_SENDER]="${REPORT_SENDER:=mailserver-report@${HOSTNAME}}" + + _log 'trace' 'Setting anti-spam & anti-virus environment variables' + + VARS[AMAVIS_LOGLEVEL]="${AMAVIS_LOGLEVEL:=0}" + VARS[CLAMAV_MESSAGE_SIZE_LIMIT]="${CLAMAV_MESSAGE_SIZE_LIMIT:=25M}" + VARS[FAIL2BAN_BLOCKTYPE]="${FAIL2BAN_BLOCKTYPE:=drop}" + VARS[MOVE_SPAM_TO_JUNK]="${MOVE_SPAM_TO_JUNK:=1}" + VARS[POSTGREY_AUTO_WHITELIST_CLIENTS]="${POSTGREY_AUTO_WHITELIST_CLIENTS:=5}" + VARS[POSTGREY_DELAY]="${POSTGREY_DELAY:=300}" + VARS[POSTGREY_MAX_AGE]="${POSTGREY_MAX_AGE:=35}" + VARS[POSTGREY_TEXT]="${POSTGREY_TEXT:=Delayed by Postgrey}" + VARS[POSTSCREEN_ACTION]="${POSTSCREEN_ACTION:=enforce}" + VARS[SA_KILL]=${SA_KILL:="6.31"} + VARS[SA_SPAM_SUBJECT]=${SA_SPAM_SUBJECT:="***SPAM*** "} + VARS[SA_TAG]=${SA_TAG:="2.0"} + VARS[SA_TAG2]=${SA_TAG2:="6.31"} + VARS[SPAMASSASSIN_SPAM_TO_INBOX]="${SPAMASSASSIN_SPAM_TO_INBOX:=1}" + VARS[SPOOF_PROTECTION]="${SPOOF_PROTECTION:=0}" + VARS[VIRUSMAILS_DELETE_DELAY]="${VIRUSMAILS_DELETE_DELAY:=7}" + + _log 'trace' 'Setting service-enabling environment variables' + + VARS[ENABLE_AMAVIS]="${ENABLE_AMAVIS:=1}" + VARS[ENABLE_CLAMAV]="${ENABLE_CLAMAV:=0}" + VARS[ENABLE_DNSBL]="${ENABLE_DNSBL:=0}" + VARS[ENABLE_FAIL2BAN]="${ENABLE_FAIL2BAN:=0}" + VARS[ENABLE_FETCHMAIL]="${ENABLE_FETCHMAIL:=0}" + VARS[ENABLE_MANAGESIEVE]="${ENABLE_MANAGESIEVE:=0}" + VARS[ENABLE_POP3]="${ENABLE_POP3:=0}" + VARS[ENABLE_POSTGREY]="${ENABLE_POSTGREY:=0}" + VARS[ENABLE_QUOTAS]="${ENABLE_QUOTAS:=1}" + VARS[ENABLE_SASLAUTHD]="${ENABLE_SASLAUTHD:=0}" + VARS[ENABLE_SPAMASSASSIN]="${ENABLE_SPAMASSASSIN:=0}" + VARS[ENABLE_SPAMASSASSIN_KAM]="${ENABLE_SPAMASSASSIN_KAM:=0}" + VARS[ENABLE_SRS]="${ENABLE_SRS:=0}" + VARS[ENABLE_UPDATE_CHECK]="${ENABLE_UPDATE_CHECK:=1}" + + _log 'trace' 'Setting IP, DNS and SSL environment variables' + + VARS[DEFAULT_RELAY_HOST]="${DEFAULT_RELAY_HOST:=}" + # VARS[DMS_FQDN]="${DMS_FQDN:=}" + # VARS[DMS_DOMAINNAME]="${DMS_DOMAINNAME:=}" + # VARS[DMS_HOSTNAME]="${DMS_HOSTNAME:=}" + VARS[NETWORK_INTERFACE]="${NETWORK_INTERFACE:=eth0}" + VARS[OVERRIDE_HOSTNAME]="${OVERRIDE_HOSTNAME:-}" + VARS[RELAY_HOST]="${RELAY_HOST:=}" + VARS[SSL_TYPE]="${SSL_TYPE:=}" + VARS[TLS_LEVEL]="${TLS_LEVEL:=modern}" + + _log 'trace' 'Setting Dovecot- and Postfix-specific environment variables' + + VARS[DOVECOT_INET_PROTOCOLS]="${DOVECOT_INET_PROTOCOLS:=all}" + VARS[DOVECOT_MAILBOX_FORMAT]="${DOVECOT_MAILBOX_FORMAT:=maildir}" + VARS[DOVECOT_TLS]="${DOVECOT_TLS:=no}" + + VARS[POSTFIX_INET_PROTOCOLS]="${POSTFIX_INET_PROTOCOLS:=all}" + VARS[POSTFIX_MAILBOX_SIZE_LIMIT]="${POSTFIX_MAILBOX_SIZE_LIMIT:=0}" + VARS[POSTFIX_MESSAGE_SIZE_LIMIT]="${POSTFIX_MESSAGE_SIZE_LIMIT:=10240000}" # ~10 MB + + _log 'trace' 'Setting miscellaneous environment variables' + + VARS[ACCOUNT_PROVISIONER]="${ACCOUNT_PROVISIONER:=FILE}" + VARS[FETCHMAIL_PARALLEL]="${FETCHMAIL_PARALLEL:=0}" + VARS[FETCHMAIL_POLL]="${FETCHMAIL_POLL:=300}" + VARS[LOG_LEVEL]="${LOG_LEVEL:=info}" + VARS[LOGROTATE_INTERVAL]="${LOGROTATE_INTERVAL:=weekly}" + VARS[LOGWATCH_INTERVAL]="${LOGWATCH_INTERVAL:=none}" + VARS[LOGWATCH_RECIPIENT]="${LOGWATCH_RECIPIENT:=${REPORT_RECIPIENT}}" + VARS[LOGWATCH_SENDER]="${LOGWATCH_SENDER:=${REPORT_SENDER}}" + VARS[ONE_DIR]="${ONE_DIR:=1}" + VARS[PERMIT_DOCKER]="${PERMIT_DOCKER:=none}" + VARS[PFLOGSUMM_RECIPIENT]="${PFLOGSUMM_RECIPIENT:=${REPORT_RECIPIENT}}" + VARS[PFLOGSUMM_SENDER]="${PFLOGSUMM_SENDER:=${REPORT_SENDER}}" + VARS[PFLOGSUMM_TRIGGER]="${PFLOGSUMM_TRIGGER:=none}" + VARS[SMTP_ONLY]="${SMTP_ONLY:=0}" + VARS[SRS_SENDER_CLASSES]="${SRS_SENDER_CLASSES:=envelope_sender}" + VARS[SUPERVISOR_LOGLEVEL]="${SUPERVISOR_LOGLEVEL:=warn}" + VARS[TZ]="${TZ:=}" + VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}" +} + +# This function handles environment variables related to LDAP. +function _environment_variables_ldap +{ + _log 'debug' 'Setting LDAP-related environment variables now' + + VARS[LDAP_BIND_DN]="${LDAP_BIND_DN:=}" + VARS[LDAP_BIND_PW]="${LDAP_BIND_PW:=}" + VARS[LDAP_SEARCH_BASE]="${LDAP_SEARCH_BASE:=}" + VARS[LDAP_SERVER_HOST]="${LDAP_SERVER_HOST:=}" + VARS[LDAP_START_TLS]="${LDAP_START_TLS:=no}" +} + +# This function handles environment variables related to SASLAUTHD +# and, if activated, variables related to SASLAUTHD and LDAP. +function _environment_variables_saslauthd +{ + _log 'debug' 'Setting SASLAUTHD-related environment variables now' + + VARS[SASLAUTHD_MECHANISMS]="${SASLAUTHD_MECHANISMS:=pam}" + + # SASL ENV for configuring an LDAP specific + # `saslauthd.conf` via `setup-stack.sh:_setup_sasulauthd()` + if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] + then + _log 'trace' 'Setting SASLSAUTH-LDAP variables nnow' + + VARS[SASLAUTHD_LDAP_AUTH_METHOD]="${SASLAUTHD_LDAP_AUTH_METHOD:=bind}" + VARS[SASLAUTHD_LDAP_BIND_DN]="${SASLAUTHD_LDAP_BIND_DN:=${LDAP_BIND_DN}}" + VARS[SASLAUTHD_LDAP_FILTER]="${SASLAUTHD_LDAP_FILTER:=(&(uniqueIdentifier=%u)(mailEnabled=TRUE))}" + VARS[SASLAUTHD_LDAP_PASSWORD]="${SASLAUTHD_LDAP_PASSWORD:=${LDAP_BIND_PW}}" + VARS[SASLAUTHD_LDAP_SEARCH_BASE]="${SASLAUTHD_LDAP_SEARCH_BASE:=${LDAP_SEARCH_BASE}}" + VARS[SASLAUTHD_LDAP_SERVER]="${SASLAUTHD_LDAP_SERVER:=${LDAP_SERVER_HOST}}" + [[ ${SASLAUTHD_LDAP_SERVER} != *'://'* ]] && SASLAUTHD_LDAP_SERVER="ldap://${SASLAUTHD_LDAP_SERVER}" + VARS[SASLAUTHD_LDAP_START_TLS]="${SASLAUTHD_LDAP_START_TLS:=no}" + VARS[SASLAUTHD_LDAP_TLS_CHECK_PEER]="${SASLAUTHD_LDAP_TLS_CHECK_PEER:=no}" + + if [[ -z ${SASLAUTHD_LDAP_TLS_CACERT_FILE} ]] + then + SASLAUTHD_LDAP_TLS_CACERT_FILE='' + else + SASLAUTHD_LDAP_TLS_CACERT_FILE="ldap_tls_cacert_file: ${SASLAUTHD_LDAP_TLS_CACERT_FILE}" + fi + VARS[SASLAUTHD_LDAP_TLS_CACERT_FILE]="${SASLAUTHD_LDAP_TLS_CACERT_FILE}" + + if [[ -z ${SASLAUTHD_LDAP_TLS_CACERT_DIR} ]] + then + SASLAUTHD_LDAP_TLS_CACERT_DIR='' + else + SASLAUTHD_LDAP_TLS_CACERT_DIR="ldap_tls_cacert_dir: ${SASLAUTHD_LDAP_TLS_CACERT_DIR}" + fi + VARS[SASLAUTHD_LDAP_TLS_CACERT_DIR]="${SASLAUTHD_LDAP_TLS_CACERT_DIR}" + + if [[ -z ${SASLAUTHD_LDAP_PASSWORD_ATTR} ]] + then + SASLAUTHD_LDAP_PASSWORD_ATTR='' + else + SASLAUTHD_LDAP_PASSWORD_ATTR="ldap_password_attr: ${SASLAUTHD_LDAP_PASSWORD_ATTR}" + fi + VARS[SASLAUTHD_LDAP_PASSWORD_ATTR]="${SASLAUTHD_LDAP_PASSWORD_ATTR}" + + if [[ -z ${SASLAUTHD_LDAP_MECH} ]] + then + SASLAUTHD_LDAP_MECH='' + else + SASLAUTHD_LDAP_MECH="ldap_mech: ${SASLAUTHD_LDAP_MECH}" + fi + VARS[SASLAUTHD_LDAP_MECH]="${SASLAUTHD_LDAP_MECH}" + fi +} diff --git a/target/scripts/start-mailserver.sh b/target/scripts/start-mailserver.sh index 5a46490e..95bcaefb 100755 --- a/target/scripts/start-mailserver.sh +++ b/target/scripts/start-mailserver.sh @@ -31,142 +31,19 @@ source /usr/local/bin/daemons-stack.sh # ------------------------------------------------------------ # ? << Sourcing helpers & stacks # -- -# ? >> Setup Supervisor & DNS names +# ? >> Early setup & environment variables setup # ------------------------------------------------------------ -# Setup supervisord as early as possible -declare -A VARS -VARS[SUPERVISOR_LOGLEVEL]="${SUPERVISOR_LOGLEVEL:=warn}" +# shellcheck source=./helpers/variables.sh +source /usr/local/bin/helpers/variables.sh _setup_supervisor _obtain_hostname_and_domainname +_environment_variables_backwards_compatibility +_environment_variables_general_setup # ------------------------------------------------------------ -# ? << Setup Supervisor & DNS names -# -- -# ? >> Setup of default and global values / variables -# ------------------------------------------------------------ - -# shellcheck disable=SC2034 -declare -a FUNCS_SETUP FUNCS_FIX FUNCS_CHECK FUNCS_MISC DAEMONS_START - -# These variables must be defined first; They are used as default values for other variables. -VARS[POSTMASTER_ADDRESS]="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}" -VARS[REPORT_RECIPIENT]="${REPORT_RECIPIENT:=${POSTMASTER_ADDRESS}}" -VARS[REPORT_SENDER]="${REPORT_SENDER:=mailserver-report@${HOSTNAME}}" - -VARS[AMAVIS_LOGLEVEL]="${AMAVIS_LOGLEVEL:=0}" -VARS[CLAMAV_MESSAGE_SIZE_LIMIT]="${CLAMAV_MESSAGE_SIZE_LIMIT:=25M}" # 25 MB -VARS[DEFAULT_RELAY_HOST]="${DEFAULT_RELAY_HOST:=}" -VARS[DOVECOT_INET_PROTOCOLS]="${DOVECOT_INET_PROTOCOLS:=all}" -VARS[DOVECOT_MAILBOX_FORMAT]="${DOVECOT_MAILBOX_FORMAT:=maildir}" -VARS[DOVECOT_TLS]="${DOVECOT_TLS:=no}" -VARS[ENABLE_AMAVIS]="${ENABLE_AMAVIS:=1}" -VARS[ENABLE_CLAMAV]="${ENABLE_CLAMAV:=0}" -VARS[ENABLE_DNSBL]="${ENABLE_DNSBL:=0}" -VARS[ENABLE_FAIL2BAN]="${ENABLE_FAIL2BAN:=0}" -VARS[ENABLE_FETCHMAIL]="${ENABLE_FETCHMAIL:=0}" -VARS[ENABLE_LDAP]="${ENABLE_LDAP:=0}" -VARS[ENABLE_MANAGESIEVE]="${ENABLE_MANAGESIEVE:=0}" -VARS[ENABLE_POP3]="${ENABLE_POP3:=0}" -VARS[ENABLE_POSTGREY]="${ENABLE_POSTGREY:=0}" -VARS[ENABLE_QUOTAS]="${ENABLE_QUOTAS:=1}" -VARS[ENABLE_SASLAUTHD]="${ENABLE_SASLAUTHD:=0}" -VARS[ENABLE_SPAMASSASSIN]="${ENABLE_SPAMASSASSIN:=0}" -VARS[ENABLE_SPAMASSASSIN_KAM]="${ENABLE_SPAMASSASSIN_KAM:=0}" -VARS[ENABLE_SRS]="${ENABLE_SRS:=0}" -VARS[ENABLE_UPDATE_CHECK]="${ENABLE_UPDATE_CHECK:=1}" -VARS[FAIL2BAN_BLOCKTYPE]="${FAIL2BAN_BLOCKTYPE:=drop}" -VARS[FETCHMAIL_PARALLEL]="${FETCHMAIL_PARALLEL:=0}" -VARS[FETCHMAIL_POLL]="${FETCHMAIL_POLL:=300}" -VARS[LDAP_START_TLS]="${LDAP_START_TLS:=no}" -VARS[LOG_LEVEL]="${LOG_LEVEL:=info}" -VARS[LOGROTATE_INTERVAL]="${LOGROTATE_INTERVAL:=weekly}" -VARS[LOGWATCH_INTERVAL]="${LOGWATCH_INTERVAL:=none}" -VARS[LOGWATCH_RECIPIENT]="${LOGWATCH_RECIPIENT:=${REPORT_RECIPIENT}}" -VARS[LOGWATCH_SENDER]="${LOGWATCH_SENDER:=${REPORT_SENDER}}" -VARS[MOVE_SPAM_TO_JUNK]="${MOVE_SPAM_TO_JUNK:=1}" -VARS[NETWORK_INTERFACE]="${NETWORK_INTERFACE:=eth0}" -VARS[ONE_DIR]="${ONE_DIR:=1}" -VARS[OVERRIDE_HOSTNAME]="${OVERRIDE_HOSTNAME:-}" -VARS[PERMIT_DOCKER]="${PERMIT_DOCKER:=none}" -VARS[PFLOGSUMM_RECIPIENT]="${PFLOGSUMM_RECIPIENT:=${REPORT_RECIPIENT}}" -VARS[PFLOGSUMM_SENDER]="${PFLOGSUMM_SENDER:=${REPORT_SENDER}}" -VARS[PFLOGSUMM_TRIGGER]="${PFLOGSUMM_TRIGGER:=none}" -VARS[POSTFIX_INET_PROTOCOLS]="${POSTFIX_INET_PROTOCOLS:=all}" -VARS[POSTFIX_MAILBOX_SIZE_LIMIT]="${POSTFIX_MAILBOX_SIZE_LIMIT:=0}" -VARS[POSTFIX_MESSAGE_SIZE_LIMIT]="${POSTFIX_MESSAGE_SIZE_LIMIT:=10240000}" # ~10 MB -VARS[POSTGREY_AUTO_WHITELIST_CLIENTS]="${POSTGREY_AUTO_WHITELIST_CLIENTS:=5}" -VARS[POSTGREY_DELAY]="${POSTGREY_DELAY:=300}" -VARS[POSTGREY_MAX_AGE]="${POSTGREY_MAX_AGE:=35}" -VARS[POSTGREY_TEXT]="${POSTGREY_TEXT:=Delayed by Postgrey}" -VARS[POSTSCREEN_ACTION]="${POSTSCREEN_ACTION:=enforce}" -VARS[RELAY_HOST]="${RELAY_HOST:=}" -VARS[SA_KILL]=${SA_KILL:="6.31"} -VARS[SA_SPAM_SUBJECT]=${SA_SPAM_SUBJECT:="***SPAM*** "} -VARS[SA_TAG]=${SA_TAG:="2.0"} -VARS[SA_TAG2]=${SA_TAG2:="6.31"} -VARS[SMTP_ONLY]="${SMTP_ONLY:=0}" -VARS[SPAMASSASSIN_SPAM_TO_INBOX]="${SPAMASSASSIN_SPAM_TO_INBOX:=1}" -VARS[SPOOF_PROTECTION]="${SPOOF_PROTECTION:=0}" -VARS[SRS_SENDER_CLASSES]="${SRS_SENDER_CLASSES:=envelope_sender}" -VARS[SSL_TYPE]="${SSL_TYPE:=}" -VARS[TLS_LEVEL]="${TLS_LEVEL:=modern}" -VARS[TZ]="${TZ:=}" -VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}" -VARS[VIRUSMAILS_DELETE_DELAY]="${VIRUSMAILS_DELETE_DELAY:=7}" - -# SASL specific variables -VARS[LDAP_BIND_DN]="${LDAP_BIND_DN:=}" -VARS[LDAP_BIND_PW]="${LDAP_BIND_PW:=}" -VARS[LDAP_SEARCH_BASE]="${LDAP_SEARCH_BASE:=}" -VARS[LDAP_SERVER_HOST]="${LDAP_SERVER_HOST:=}" - -VARS[SASLAUTHD_LDAP_AUTH_METHOD]="${SASLAUTHD_LDAP_AUTH_METHOD:=bind}" -VARS[SASLAUTHD_LDAP_BIND_DN]="${SASLAUTHD_LDAP_BIND_DN:=${LDAP_BIND_DN}}" -VARS[SASLAUTHD_LDAP_FILTER]="${SASLAUTHD_LDAP_FILTER:=(&(uniqueIdentifier=%u)(mailEnabled=TRUE))}" -VARS[SASLAUTHD_LDAP_PASSWORD]="${SASLAUTHD_LDAP_PASSWORD:=${LDAP_BIND_PW}}" -VARS[SASLAUTHD_LDAP_SEARCH_BASE]="${SASLAUTHD_LDAP_SEARCH_BASE:=${LDAP_SEARCH_BASE}}" -VARS[SASLAUTHD_LDAP_SERVER]="${SASLAUTHD_LDAP_SERVER:=${LDAP_SERVER_HOST}}" -[[ ${SASLAUTHD_LDAP_SERVER} != *'://'* ]] && SASLAUTHD_LDAP_SERVER="ldap://${SASLAUTHD_LDAP_SERVER}" -VARS[SASLAUTHD_LDAP_START_TLS]="${SASLAUTHD_LDAP_START_TLS:=no}" -VARS[SASLAUTHD_LDAP_TLS_CHECK_PEER]="${SASLAUTHD_LDAP_TLS_CHECK_PEER:=no}" -VARS[SASLAUTHD_MECHANISMS]="${SASLAUTHD_MECHANISMS:=pam}" - -if [[ -z ${SASLAUTHD_LDAP_TLS_CACERT_FILE} ]] -then - SASLAUTHD_LDAP_TLS_CACERT_FILE='' -else - SASLAUTHD_LDAP_TLS_CACERT_FILE="ldap_tls_cacert_file: ${SASLAUTHD_LDAP_TLS_CACERT_FILE}" -fi -VARS[SASLAUTHD_LDAP_TLS_CACERT_FILE]="${SASLAUTHD_LDAP_TLS_CACERT_FILE}" - -if [[ -z ${SASLAUTHD_LDAP_TLS_CACERT_DIR} ]] -then - SASLAUTHD_LDAP_TLS_CACERT_DIR='' -else - SASLAUTHD_LDAP_TLS_CACERT_DIR="ldap_tls_cacert_dir: ${SASLAUTHD_LDAP_TLS_CACERT_DIR}" -fi -VARS[SASLAUTHD_LDAP_TLS_CACERT_DIR]="${SASLAUTHD_LDAP_TLS_CACERT_DIR}" - -if [[ -z ${SASLAUTHD_LDAP_PASSWORD_ATTR} ]] -then - SASLAUTHD_LDAP_PASSWORD_ATTR='' -else - SASLAUTHD_LDAP_PASSWORD_ATTR="ldap_password_attr: ${SASLAUTHD_LDAP_PASSWORD_ATTR}" -fi -VARS[SASLAUTHD_LDAP_PASSWORD_ATTR]="${SASLAUTHD_LDAP_PASSWORD_ATTR}" - -if [[ -z ${SASLAUTHD_LDAP_MECH} ]] -then - SASLAUTHD_LDAP_MECH='' -else - SASLAUTHD_LDAP_MECH="ldap_mech: ${SASLAUTHD_LDAP_MECH}" -fi -VARS[SASLAUTHD_LDAP_MECH]="${SASLAUTHD_LDAP_MECH}" - -# ------------------------------------------------------------ -# ? << Setup of default and global values / variables +# ? << Early setup & environment variables setup # -- # ? >> Registering functions # ------------------------------------------------------------ @@ -183,22 +60,42 @@ function _register_functions # ? >> Setup - _register_setup_function '_setup_default_vars' _register_setup_function '_setup_file_permissions' - - [[ -n ${TZ} ]] && _register_setup_function '_setup_timezone' + _register_setup_function '_setup_timezone' if [[ ${SMTP_ONLY} -ne 1 ]] then _register_setup_function '_setup_dovecot' _register_setup_function '_setup_dovecot_dhparam' _register_setup_function '_setup_dovecot_quota' - _register_setup_function '_setup_dovecot_local_user' fi - [[ ${ENABLE_LDAP} -eq 1 ]] && _register_setup_function '_setup_ldap' + case "${ACCOUNT_PROVISIONER}" in + ( 'FILE' ) + _register_setup_function '_setup_dovecot_local_user' + ;; + + ( 'LDAP' ) + _environment_variables_ldap + _register_setup_function '_setup_ldap' + ;; + + ( 'OIDC' ) + _register_setup_function '_setup_oidc' + ;; + + ( * ) + _shutdown "'${ACCOUNT_PROVISIONER}' is not a valid value for ACCOUNT_PROVISIONER" + ;; + esac + + if [[ ${ENABLE_SASLAUTHD} -eq 1 ]] + then + _environment_variables_saslauthd + _register_setup_function '_setup_saslauthd' + fi + [[ ${ENABLE_POSTGREY} -eq 1 ]] && _register_setup_function '_setup_postgrey' - [[ ${ENABLE_SASLAUTHD} -eq 1 ]] && _register_setup_function '_setup_saslauthd' [[ ${POSTFIX_INET_PROTOCOLS} != 'all' ]] && _register_setup_function '_setup_postfix_inet_protocols' [[ ${DOVECOT_INET_PROTOCOLS} != 'all' ]] && _register_setup_function '_setup_dovecot_inet_protocols' [[ ${ENABLE_FAIL2BAN} -eq 1 ]] && _register_setup_function '_setup_fail2ban' @@ -259,6 +156,7 @@ function _register_functions # ? >> Miscellaneous _register_misc_function '_misc_save_states' + _register_setup_function '_environment_variables_export' # ? >> Daemons @@ -282,7 +180,7 @@ function _register_functions [[ ${ENABLE_FAIL2BAN} -eq 1 ]] && _register_start_daemon '_start_daemon_fail2ban' [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && _register_start_daemon '_start_daemon_fetchmail' [[ ${ENABLE_CLAMAV} -eq 1 ]] && _register_start_daemon '_start_daemon_clamav' - [[ ${ENABLE_LDAP} -eq 0 ]] && _register_start_daemon '_start_daemon_changedetector' + [[ ${ACCOUNT_PROVISIONER} == 'FILE' ]] && _register_start_daemon '_start_daemon_changedetector' [[ ${ENABLE_AMAVIS} -eq 1 ]] && _register_start_daemon '_start_daemon_amavis' } diff --git a/target/scripts/startup/setup-stack.sh b/target/scripts/startup/setup-stack.sh index 03abbe1d..9ce552d4 100644 --- a/target/scripts/startup/setup-stack.sh +++ b/target/scripts/startup/setup-stack.sh @@ -14,6 +14,8 @@ function _setup function _setup_supervisor { + SUPERVISOR_LOGLEVEL="${SUPERVISOR_LOGLEVEL:-warn}" + if ! grep -q "loglevel = ${SUPERVISOR_LOGLEVEL}" /etc/supervisor/supervisord.conf then case "${SUPERVISOR_LOGLEVEL}" in @@ -26,9 +28,7 @@ function _setup_supervisor exit ;; - ( 'warn' ) - return 0 - ;; + ( 'warn' ) ;; ( * ) _log 'warn' \ @@ -41,24 +41,6 @@ function _setup_supervisor return 0 } -function _setup_default_vars -{ - _log 'debug' 'Setting up default variables' - - : >/root/.bashrc # make DMS variables available in login shells and their subprocesses - : >/etc/dms-settings # this file can be sourced by other scripts - - local VAR - for VAR in "${!VARS[@]}" - do - echo "export ${VAR}='${VARS[${VAR}]}'" >>/root/.bashrc - echo "${VAR}='${VARS[${VAR}]}'" >>/etc/dms-settings - done - - sort -o /root/.bashrc /root/.bashrc - sort -o /etc/dms-settings /etc/dms-settings -} - # File/folder permissions are fine when using docker volumes, but may be wrong # when file system folders are mounted into the container. # Set the expected values and create missing folders/files just in case. @@ -220,7 +202,7 @@ function _setup_dovecot_quota _log 'debug' 'Setting up Dovecot quota' # Dovecot quota is disabled when using LDAP or SMTP_ONLY or when explicitly disabled. - if [[ ${ENABLE_LDAP} -eq 1 ]] || [[ ${SMTP_ONLY} -eq 1 ]] || [[ ${ENABLE_QUOTAS} -eq 0 ]] + if [[ ${ACCOUNT_PROVISIONER} != 'FILE' ]] || [[ ${SMTP_ONLY} -eq 1 ]] || [[ ${ENABLE_QUOTAS} -eq 0 ]] then # disable dovecot quota in docevot confs if [[ -f /etc/dovecot/conf.d/90-quota.conf ]] @@ -274,9 +256,10 @@ function _setup_dovecot_quota function _setup_dovecot_local_user { - _log 'debug' 'Setting up Dovecot Local User' + [[ ${SMTP_ONLY} -eq 1 ]] && return 0 + [[ ${ACCOUNT_PROVISIONER} == 'FILE' ]] || return 0 - [[ ${ENABLE_LDAP} -eq 1 ]] && return 0 + _log 'debug' 'Setting up Dovecot Local User' if [[ ! -f /tmp/docker-mailserver/postfix-accounts.cf ]] then @@ -401,6 +384,11 @@ function _setup_ldap return 0 } +function _setup_oidc +{ + _shutdown 'OIDC user account provisioning is not yet implemented' +} + function _setup_postgrey { _log 'debug' 'Configuring Postgrey' @@ -471,7 +459,7 @@ function _setup_spoof_protection 's|smtpd_sender_restrictions =|smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch,|' \ /etc/postfix/main.cf - if [[ ${ENABLE_LDAP} -eq 1 ]] + if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] then if [[ -z ${LDAP_QUERY_FILTER_SENDERS} ]] then @@ -1197,6 +1185,7 @@ EOF function _setup_timezone { + [[ -n ${TZ} ]] || return 0 _log 'debug' "Setting timezone to '${TZ}'" local ZONEINFO_FILE="/usr/share/zoneinfo/${TZ}" diff --git a/test/mail_with_ldap.bats b/test/mail_with_ldap.bats index 6e404235..882beddc 100644 --- a/test/mail_with_ldap.bats +++ b/test/mail_with_ldap.bats @@ -32,7 +32,7 @@ function setup_file() { -e DOVECOT_PASS_FILTER="(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))" \ -e DOVECOT_TLS=no \ -e DOVECOT_USER_FILTER="(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))" \ - -e ENABLE_LDAP=1 \ + -e ACCOUNT_PROVISIONER=LDAP \ -e PFLOGSUMM_TRIGGER=logrotate \ -e ENABLE_SASLAUTHD=1 \ -e LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain \ @@ -42,9 +42,9 @@ function setup_file() { -e LDAP_QUERY_FILTER_GROUP="(&(mailGroupMember=%s)(mailEnabled=TRUE))" \ -e LDAP_QUERY_FILTER_SENDERS="(|(&(mail=%s)(mailEnabled=TRUE))(&(mailGroupMember=%s)(mailEnabled=TRUE))(|(&(mailAlias=%s)(objectClass=PostfixBookMailForward))(&(mailAlias=%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)))(uniqueIdentifier=some.user.id))" \ -e LDAP_QUERY_FILTER_USER="(&(mail=%s)(mailEnabled=TRUE))" \ + -e LDAP_START_TLS=no \ -e LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain \ -e LDAP_SERVER_HOST=ldap \ - -e LDAP_START_TLS=no \ -e PERMIT_DOCKER=container \ -e POSTMASTER_ADDRESS="postmaster@${FQDN_LOCALHOST_A}" \ -e REPORT_RECIPIENT=1 \