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 \