From ed84dca1471fa6d599f1672c874ab3585a2b0074 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Sat, 2 Sep 2023 22:07:02 +1200 Subject: [PATCH] chore: LDAP config improvements (#3522) * chore: Drop management of `SASLAUTHD_*` ENV - `variables-stack.sh` does not need to manage all these extra ENV or store them. They're not used anywhere else. - `saslauthd.sh` is the only consumer of these ENV which are effectively direct key/value mappings, with some defaults provided / inherited. Instead of trying to conditionally support key/value pairs when ENV is set, we could instead use `sed` to delete lines with empty values. * chore: Drop fallbacks + update configs to match docs - Drop deprecated support: - `DOVECOT_HOSTS` is an ENV deprecated since v10. - Fallback for missing URI scheme introduced for Dovecot and SASLAuthd in v10. - Adding error log message when no LDAP URI scheme is detected for the supported ENV (when set). - Docs updated for ENV to reflect the mandatory requirement. `mailserver.env` partially synced equivalent sections. - Provided base LDAP configs (for overriding) likewise updated from `domain.com` to `example.com`. - LDAP test updated for required `ldap://` URI scheme. Common ENV shared across LDAP configs hoisted out of the Postfix group. * chore: Remove unset lines in generated `saslauthd.conf` --- docs/content/config/environment.md | 11 ++-- mailserver.env | 8 +-- target/dovecot/dovecot-ldap.conf.ext | 6 +-- target/postfix/ldap-aliases.cf | 6 +-- target/postfix/ldap-domains.cf | 6 +-- target/postfix/ldap-groups.cf | 6 +-- target/postfix/ldap-senders.cf | 6 +-- target/postfix/ldap-users.cf | 6 +-- target/scripts/startup/setup.d/ldap.sh | 8 +-- target/scripts/startup/setup.d/saslauthd.sh | 39 +++++++------- target/scripts/startup/variables-stack.sh | 56 ++++----------------- test/tests/serial/mail_with_ldap.bats | 19 ++++--- 12 files changed, 69 insertions(+), 108 deletions(-) diff --git a/docs/content/config/environment.md b/docs/content/config/environment.md index 3e40bd89..e5aba889 100644 --- a/docs/content/config/environment.md +++ b/docs/content/config/environment.md @@ -594,8 +594,8 @@ Enable or disable `getmail`. ##### LDAP_SERVER_HOST - **empty** => mail.example.com -- => Specify the dns-name/ip-address where the ldap-server is listening, or an URI like `ldaps://mail.example.com` -- NOTE: If you going to use DMS in combination with `compose.yaml` you can set the service name here +- => Specify the `` / `` where the LDAP server is reachable via a URI like: `ldaps://mail.example.com`. +- Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). ##### LDAP_SEARCH_BASE @@ -669,9 +669,8 @@ The following variables overwrite the default values for ```/etc/dovecot/dovecot ##### DOVECOT_URIS - **empty** => same as `LDAP_SERVER_HOST` -- => Specify a space separated list of LDAP uris. -- Note: If the protocol is missing, `ldap://` will be used. -- Note: This deprecates `DOVECOT_HOSTS` (as it didn't allow to use LDAPS), which is currently still supported for backwards compatibility. +- => Specify a space separated list of LDAP URIs. +- Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). ##### DOVECOT_LDAP_VERSION @@ -764,7 +763,7 @@ Note: This postgrey setting needs `ENABLE_POSTGREY=1` ##### SASLAUTHD_LDAP_SERVER - **empty** => same as `LDAP_SERVER_HOST` -- Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL. +- Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). ##### SASLAUTHD_LDAP_START_TLS diff --git a/mailserver.env b/mailserver.env index 8e753006..632f6edf 100644 --- a/mailserver.env +++ b/mailserver.env @@ -411,9 +411,9 @@ GETMAIL_POLL=5 # yes => LDAP over TLS enabled for Postfix LDAP_START_TLS= -# If you going to use the mailserver in combination with Docker Compose you can set the service name here -# empty => mail.domain.com -# Specify the dns-name/ip-address where the ldap-server +# empty => mail.example.com +# Specify the `` / `` where the LDAP server is reachable via a URI like: `ldaps://mail.example.com`. +# Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). LDAP_SERVER_HOST= # empty => ou=people,dc=domain,dc=com @@ -500,7 +500,7 @@ SASLAUTHD_MECHANISMS= SASLAUTHD_MECH_OPTIONS= # empty => Use value of LDAP_SERVER_HOST -# Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL. +# Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). SASLAUTHD_LDAP_SERVER= # empty => Use value of LDAP_BIND_DN diff --git a/target/dovecot/dovecot-ldap.conf.ext b/target/dovecot/dovecot-ldap.conf.ext index edf04b9e..66311106 100644 --- a/target/dovecot/dovecot-ldap.conf.ext +++ b/target/dovecot/dovecot-ldap.conf.ext @@ -1,8 +1,8 @@ -base = ou=people,dc=domain,dc=com +base = ou=people,dc=example,dc=com default_pass_scheme = SSHA -dn = cn=admin,dc=domain,dc=com +dn = cn=admin,dc=example,dc=com dnpass = admin -uris = ldap://mail.domain.com +uris = ldap://mail.example.com tls = no ldap_version = 3 pass_attrs = uniqueIdentifier=user,userPassword=password diff --git a/target/postfix/ldap-aliases.cf b/target/postfix/ldap-aliases.cf index 73bfe722..5c8d443a 100644 --- a/target/postfix/ldap-aliases.cf +++ b/target/postfix/ldap-aliases.cf @@ -1,9 +1,9 @@ bind = yes -bind_dn = cn=admin,dc=domain,dc=com +bind_dn = cn=admin,dc=example,dc=com bind_pw = admin query_filter = (&(mailAlias=%s)(mailEnabled=TRUE)) result_attribute = mail -search_base = ou=people,dc=domain,dc=com -server_host = mail.domain.com +search_base = ou=people,dc=example,dc=com +server_host = mail.example.com start_tls = no version = 3 diff --git a/target/postfix/ldap-domains.cf b/target/postfix/ldap-domains.cf index 5edd2441..c118ebf6 100644 --- a/target/postfix/ldap-domains.cf +++ b/target/postfix/ldap-domains.cf @@ -1,9 +1,9 @@ bind = yes -bind_dn = cn=admin,dc=domain,dc=com +bind_dn = cn=admin,dc=example,dc=com bind_pw = admin query_filter = (&(|(mail=*@%s)(mailalias=*@%s))(mailEnabled=TRUE)) result_attribute = mail -search_base = ou=people,dc=domain,dc=com -server_host = mail.domain.com +search_base = ou=people,dc=example,dc=com +server_host = mail.example.com start_tls = no version = 3 diff --git a/target/postfix/ldap-groups.cf b/target/postfix/ldap-groups.cf index 914e31a1..dc7fa14f 100644 --- a/target/postfix/ldap-groups.cf +++ b/target/postfix/ldap-groups.cf @@ -1,9 +1,9 @@ bind = yes -bind_dn = cn=admin,dc=domain,dc=com +bind_dn = cn=admin,dc=example,dc=com bind_pw = admin query_filter = (&(mailGroupMember=%s)(mailEnabled=TRUE)) result_attribute = mail -search_base = ou=people,dc=domain,dc=com -server_host = mail.domain.com +search_base = ou=people,dc=example,dc=com +server_host = mail.example.com start_tls = no version = 3 diff --git a/target/postfix/ldap-senders.cf b/target/postfix/ldap-senders.cf index 5f1ed6f5..88a6cd87 100644 --- a/target/postfix/ldap-senders.cf +++ b/target/postfix/ldap-senders.cf @@ -1,9 +1,9 @@ bind = yes -bind_dn = cn=admin,dc=domain,dc=com +bind_dn = cn=admin,dc=example,dc=com bind_pw = admin query_filter = (mail=%s) result_attribute = mail, uid -search_base = ou=people,dc=domain,dc=com -server_host = mail.domain.com +search_base = ou=people,dc=example,dc=com +server_host = mail.example.com start_tls = no version = 3 diff --git a/target/postfix/ldap-users.cf b/target/postfix/ldap-users.cf index a7b29cb6..943801f4 100644 --- a/target/postfix/ldap-users.cf +++ b/target/postfix/ldap-users.cf @@ -1,9 +1,9 @@ bind = yes -bind_dn = cn=admin,dc=domain,dc=com +bind_dn = cn=admin,dc=example,dc=com bind_pw = admin query_filter = (&(mail=%s)(mailEnabled=TRUE)) result_attribute = mail -search_base = ou=people,dc=domain,dc=com -server_host = mail.domain.com +search_base = ou=people,dc=example,dc=com +server_host = mail.example.com start_tls = no version = 3 diff --git a/target/scripts/startup/setup.d/ldap.sh b/target/scripts/startup/setup.d/ldap.sh index ceaca4eb..1451ec32 100644 --- a/target/scripts/startup/setup.d/ldap.sh +++ b/target/scripts/startup/setup.d/ldap.sh @@ -38,13 +38,7 @@ function _setup_ldap() { DOVECOT_LDAP_MAPPING['DOVECOT_BASE']="${DOVECOT_BASE:="${LDAP_SEARCH_BASE}"}" DOVECOT_LDAP_MAPPING['DOVECOT_DN']="${DOVECOT_DN:="${LDAP_BIND_DN}"}" DOVECOT_LDAP_MAPPING['DOVECOT_DNPASS']="${DOVECOT_DNPASS:="${LDAP_BIND_PW}"}" - DOVECOT_LDAP_MAPPING['DOVECOT_URIS']="${DOVECOT_URIS:="${DOVECOT_HOSTS:="${LDAP_SERVER_HOST}"}"}" - - # Add protocol to DOVECOT_URIS so that we can use dovecot's "uris" option: - # https://doc.dovecot.org/configuration_manual/authentication/ldap/ - if [[ ${DOVECOT_LDAP_MAPPING["DOVECOT_URIS"]} != *'://'* ]]; then - DOVECOT_LDAP_MAPPING['DOVECOT_URIS']="ldap://${DOVECOT_LDAP_MAPPING["DOVECOT_URIS"]}" - fi + DOVECOT_LDAP_MAPPING['DOVECOT_URIS']="${DOVECOT_URIS:="${LDAP_SERVER_HOST}"}" # Default DOVECOT_PASS_FILTER to the same value as DOVECOT_USER_FILTER DOVECOT_LDAP_MAPPING['DOVECOT_PASS_FILTER']="${DOVECOT_PASS_FILTER:="${DOVECOT_USER_FILTER}"}" diff --git a/target/scripts/startup/setup.d/saslauthd.sh b/target/scripts/startup/setup.d/saslauthd.sh index 12f00726..eb33a243 100644 --- a/target/scripts/startup/setup.d/saslauthd.sh +++ b/target/scripts/startup/setup.d/saslauthd.sh @@ -1,29 +1,29 @@ #!/bin/bash - function _setup_saslauthd() { _log 'debug' 'Setting up SASLAUTHD' - if [[ ! -f /etc/saslauthd.conf ]]; then + # NOTE: It's unlikely this file would already exist, + # Unlike Dovecot/Postfix LDAP support, this file has no ENV replacement + # nor does it copy from the DMS config volume to this internal location. + if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] \ + && [[ ! -f /etc/saslauthd.conf ]]; then _log 'trace' 'Creating /etc/saslauthd.conf' - cat > /etc/saslauthd.conf << EOF -ldap_servers: ${SASLAUTHD_LDAP_SERVER} - -ldap_auth_method: ${SASLAUTHD_LDAP_AUTH_METHOD} -ldap_bind_dn: ${SASLAUTHD_LDAP_BIND_DN} -ldap_bind_pw: ${SASLAUTHD_LDAP_PASSWORD} - -ldap_search_base: ${SASLAUTHD_LDAP_SEARCH_BASE} -ldap_filter: ${SASLAUTHD_LDAP_FILTER} - -ldap_start_tls: ${SASLAUTHD_LDAP_START_TLS} -ldap_tls_check_peer: ${SASLAUTHD_LDAP_TLS_CHECK_PEER} - -${SASLAUTHD_LDAP_TLS_CACERT_FILE} -${SASLAUTHD_LDAP_TLS_CACERT_DIR} -${SASLAUTHD_LDAP_PASSWORD_ATTR} -${SASLAUTHD_LDAP_MECH} + # Create a config based on ENV + sed '/^.*: $/d'> /etc/saslauthd.conf << EOF +ldap_servers: ${SASLAUTHD_LDAP_SERVER:=${LDAP_SERVER_HOST}} +ldap_auth_method: ${SASLAUTHD_LDAP_AUTH_METHOD:=bind} +ldap_bind_dn: ${SASLAUTHD_LDAP_BIND_DN:=${LDAP_BIND_DN}} +ldap_bind_pw: ${SASLAUTHD_LDAP_PASSWORD:=${LDAP_BIND_PW}} +ldap_search_base: ${SASLAUTHD_LDAP_SEARCH_BASE:=${LDAP_SEARCH_BASE}} +ldap_filter: ${SASLAUTHD_LDAP_FILTER:=(&(uniqueIdentifier=%u)(mailEnabled=TRUE))} +ldap_start_tls: ${SASLAUTHD_LDAP_START_TLS:=no} +ldap_tls_check_peer: ${SASLAUTHD_LDAP_TLS_CHECK_PEER:=no} +ldap_tls_cacert_file: ${SASLAUTHD_LDAP_TLS_CACERT_FILE} +ldap_tls_cacert_dir: ${SASLAUTHD_LDAP_TLS_CACERT_DIR} +ldap_password_attr: ${SASLAUTHD_LDAP_PASSWORD_ATTR} +ldap_mech: ${SASLAUTHD_LDAP_MECH} ldap_referrals: yes log_level: 10 EOF @@ -42,4 +42,3 @@ EOF gpasswd -a postfix sasl >/dev/null } - diff --git a/target/scripts/startup/variables-stack.sh b/target/scripts/startup/variables-stack.sh index cf099220..b18a61dc 100644 --- a/target/scripts/startup/variables-stack.sh +++ b/target/scripts/startup/variables-stack.sh @@ -17,6 +17,14 @@ function __environment_variables_backwards_compatibility() { _log 'error' "'ENABLE_LDAP=1' has been changed to 'ACCOUNT_PROVISIONER=LDAP' since DMS v13" fi + # Dovecot and SASLAuthd have applied an 'ldap://' fallback for compatibility since v10 (June 2021) + # This was silently applied, but users should be explicit: + if [[ ${LDAP_SERVER_HOST:-'://'} != *'://'* ]] \ + || [[ ${DOVECOT_URIS:-'://'} != *'://'* ]] \ + || [[ ${SASLAUTHD_LDAP_SERVER:-'://'} != *'://'* ]]; then + _log 'error' "The ENV for which LDAP host to connect to must include the URI scheme ('ldap://', 'ldaps://', 'ldapi://')" + 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:-} ]] @@ -141,6 +149,7 @@ function __environment_variables_general_setup() { } # This function handles environment variables related to LDAP. +# NOTE: SASLAuthd and Dovecot LDAP support inherit these common ENV. function _environment_variables_ldap() { _log 'debug' 'Setting LDAP-related environment variables now' @@ -152,55 +161,12 @@ function _environment_variables_ldap() { } # This function handles environment variables related to SASLAUTHD -# and, if activated, variables related to SASLAUTHD and LDAP. +# LDAP specific ENV handled in: `startup/setup.d/saslauthd.sh:_setup_saslauthd()` function _environment_variables_saslauthd() { _log 'debug' 'Setting SASLAUTHD-related environment variables now' + # Only used by the supervisor service command (upstream default: `/etc/default/saslauthd`) 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 } # This function Writes the contents of the `VARS` map (associative array) diff --git a/test/tests/serial/mail_with_ldap.bats b/test/tests/serial/mail_with_ldap.bats index c3c60b92..1b73643e 100644 --- a/test/tests/serial/mail_with_ldap.bats +++ b/test/tests/serial/mail_with_ldap.bats @@ -92,29 +92,32 @@ function setup_file() { local ENV_LDAP_CONFIG=( --env ACCOUNT_PROVISIONER=LDAP + # Common LDAP ENV: + # NOTE: `scripts/startup/setup.d/ldap.sh:_setup_ldap()` uses `_replace_by_env_in_file()` to configure settings (stripping `DOVECOT_` / `LDAP_` prefixes): + --env LDAP_SERVER_HOST="ldap://${FQDN_LDAP}" + --env LDAP_SEARCH_BASE='ou=users,dc=example,dc=test' + --env LDAP_START_TLS=no + # Credentials needed for read access to LDAP_SEARCH_BASE: + --env LDAP_BIND_DN='cn=admin,dc=example,dc=test' + --env LDAP_BIND_PW='admin' + # Postfix SASL auth provider (SASLAuthd instead of default Dovecot provider): --env ENABLE_SASLAUTHD=1 --env SASLAUTHD_MECHANISMS=ldap --env SASLAUTHD_LDAP_FILTER="${SASLAUTHD_QUERY}" # ENV to configure LDAP configs for Dovecot + Postfix: - # NOTE: `scripts/startup/setup.d/ldap.sh:_setup_ldap()` uses `_replace_by_env_in_file()` to configure settings (stripping `DOVECOT_` / `LDAP_` prefixes): # Dovecot: --env DOVECOT_PASS_FILTER="${DOVECOT_QUERY_PASS}" - --env DOVECOT_TLS=no --env DOVECOT_USER_FILTER="${DOVECOT_QUERY_USER}" + --env DOVECOT_TLS=no # Postfix: - --env LDAP_BIND_DN='cn=admin,dc=example,dc=test' - --env LDAP_BIND_PW='admin' --env LDAP_QUERY_FILTER_ALIAS="${QUERY_ALIAS}" --env LDAP_QUERY_FILTER_DOMAIN="${QUERY_DOMAIN}" --env LDAP_QUERY_FILTER_GROUP="${QUERY_GROUP}" --env LDAP_QUERY_FILTER_SENDERS="${QUERY_SENDERS}" --env LDAP_QUERY_FILTER_USER="${QUERY_USER}" - --env LDAP_SEARCH_BASE='ou=users,dc=example,dc=test' - --env LDAP_SERVER_HOST="${FQDN_LDAP}" - --env LDAP_START_TLS=no ) # Extra ENV needed to support specific test-cases: @@ -221,7 +224,7 @@ function teardown() { @test "postfix: ldap config overwrites success" { local LDAP_SETTINGS_POSTFIX=( - "server_host = ${FQDN_LDAP}" + "server_host = ldap://${FQDN_LDAP}" 'start_tls = no' 'search_base = ou=users,dc=example,dc=test' 'bind_dn = cn=admin,dc=example,dc=test'