refactor: Generate `saslauthd.conf` via Config Template feature

From a inline config via HereDoc to using a `.tmpl` file that has of all supported keys for SASLAuthd LDAP config.

This additionally supports layering the ENV `.tmpl` generated config over a user-provided config. With a utility method that will ensure earlier duplicate keys are removed.

The two new utilities are documented well enough to grok. `Dockerfile` and `packages.sh` updated to bring in new dependencies and provide the `.tmpl` file.

`log_level` is not documented as a LDAP config key. Original PR did not explain why this key and value chosen were added.
This commit is contained in:
polarathene 2023-09-03 16:40:53 +12:00
parent 25c7024cc4
commit a699c03ba9
5 changed files with 106 additions and 18 deletions

View File

@ -120,6 +120,9 @@ COPY \
target/postfix/ldap-senders.cf \ target/postfix/ldap-senders.cf \
/etc/postfix/ /etc/postfix/
# LDAP config support:
COPY --link target/features/ldap/ /etc/dms/ldap/
# hadolint ignore=SC2016 # hadolint ignore=SC2016
RUN <<EOF RUN <<EOF
sedfile -i -r 's/^(CRON)=0/\1=1/g' /etc/default/spamassassin sedfile -i -r 's/^(CRON)=0/\1=1/g' /etc/default/spamassassin

View File

@ -0,0 +1,36 @@
# Parameter docs: https://github.com/cyrusimap/cyrus-sasl/blob/3959d45aa187d906d5fb3e8edf7e3661780967a5/saslauthd/LDAP_SASLAUTHD#L85-L242
ldap_auth_method: ${LDAP_AUTH_METHOD}
ldap_bind_dn: ${LDAP_BIND_DN}
ldap_bind_pw: ${LDAP_BIND_PW}
ldap_default_domain: ${LDAP_DEFAULT_DOMAIN}
ldap_default_realm: ${LDAP_DEFAULT_REALM}
ldap_deref: ${LDAP_DEREF}
ldap_filter: ${LDAP_FILTER}
ldap_group_attr: ${LDAP_GROUP_ATTR}
ldap_group_dn: ${LDAP_GROUP_DN}
ldap_group_filter: ${LDAP_GROUP_FILTER}
ldap_group_match_method: ${LDAP_GROUP_MATCH_METHOD}
ldap_group_search_base: ${LDAP_GROUP_SEARCH_BASE}
ldap_group_scope: ${LDAP_GROUP_SCOPE}
ldap_password: ${LDAP_PASSWORD}
ldap_password_attr: ${LDAP_PASSWORD_ATTR}
ldap_referrals: ${LDAP_REFERRALS}
ldap_restart: ${LDAP_RESTART}
ldap_id: ${LDAP_ID}
ldap_authz_id: ${LDAP_AUTHZ_ID}
ldap_mech: ${LDAP_MECH}
ldap_realm: ${LDAP_REALM}
ldap_scope: ${LDAP_SCOPE}
ldap_search_base: ${LDAP_SEARCH_BASE}
ldap_servers: ${LDAP_SERVERS}
ldap_start_tls: ${LDAP_START_TLS}
ldap_time_limit: ${LDAP_TIME_LIMIT}
ldap_timeout: ${LDAP_TIMEOUT}
ldap_tls_check_peer: ${LDAP_TLS_CHECK_PEER}
ldap_tls_cacert_file: ${LDAP_TLS_CACERT_FILE}
ldap_tls_cacert_dir: ${LDAP_TLS_CACERT_DIR}
ldap_tls_ciphers: ${LDAP_TLS_CIPHERS}
ldap_tls_cert: ${LDAP_TLS_CERT}
ldap_tls_key: ${LDAP_TLS_KEY}
ldap_use_sasl: ${LDAP_USE_SASL}
ldap_version: ${LDAP_VERSION}

View File

@ -92,6 +92,18 @@ function _install_packages() {
"${DEBUG_PACKAGES[@]}" "${DEBUG_PACKAGES[@]}"
} }
function _install_feature_config_templates() {
_log 'debug' 'Installing support for feature - Config Templates'
# envsubst:
apt-get "${QUIET}" --no-install-recommends install gettext-base
# zenv:
local URL_ZENV="https://github.com/numToStr/zenv/releases/download/0.8.0/zenv-0.8.0-$(uname --machine)-unknown-linux-gnu.tar.gz"
# Download from GH releases to stdout, then extract the zenv file to make available via PATH:
curl -L "${URL_ZENV}" -o - | tar --gzip --extract --directory /usr/local/bin --file - zenv
}
function _install_dovecot() { function _install_dovecot() {
declare -a DOVECOT_PACKAGES declare -a DOVECOT_PACKAGES
@ -219,5 +231,6 @@ _install_rspamd
_install_fail2ban _install_fail2ban
_install_getmail _install_getmail
_install_utils _install_utils
_install_feature_config_templates
_remove_data_after_package_installations _remove_data_after_package_installations
_post_installation_steps _post_installation_steps

View File

@ -153,3 +153,39 @@ function _env_var_expect_integer() {
_log 'warn' "The value of '${ENV_VAR_NAME}' is not an integer ('${!ENV_VAR_NAME}'), but was expected to be" _log 'warn' "The value of '${ENV_VAR_NAME}' is not an integer ('${!ENV_VAR_NAME}'), but was expected to be"
return 1 return 1
} }
# Ensures that zenv only runs envsubst with ENV filtered from the provided prefix.
# Those ENV are loaded by zenv in the format of an `.env` file (with prefix dropped by sed)
# When an ENV is not available, envsubst will evaluate it as empty.
#
# @param ${1} = Use a prefix for a group of environment variables
# @param ${2} = Filepath to ENV template
# @output = Template file content populated with available ENV
function _template_with_env() {
local ENV_PREFIX=${1:?ENV prefix is required}
local ENV_TEMPLATE=${2:?ENV template filepath is required}
if [[ ! -f ${ENV_TEMPLATE} ]]; then
_dms_panic__invalid_value "file '${ENV_TEMPLATE}' does not exist" 'utils.sh:_use_env_template'
fi
# NOTE: $PATH is retained to avoid needing absolute paths for binaries.
env --ignore-environment PATH="${PATH}" \
zenv --file <(env | grep "^${ENV_PREFIX}" | sed "s/^${ENV_PREFIX}//") \
envsubst < "${ENV_TEMPLATE}"
}
# Utility to cleanup a config file that may have unset or duplicate keys.
# - sed => Removes lines where keys have no value assigned.
# - tac + sort => Remove any duplicate keys (keeps the last instance found).
#
# @param ${1} = A delimiter between key and value columns
# @param ${2} = Input filepath to clean
# @output = The transformed file content
function _cleanse_config() {
local KV_DELIMITER=${1:?KV Delimiter is required}
local INPUT_FILE=${2?:Input file is required}
sed "/^[^${KV_DELIMITER}]*${KV_DELIMITER}\s*$/d" ${INPUT_FILE} \
| tac | sort -u -t"${KV_DELIMITER}" -k1,1
}

View File

@ -9,24 +9,7 @@ function _setup_saslauthd() {
if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] \ if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] \
&& [[ ! -f /etc/saslauthd.conf ]]; then && [[ ! -f /etc/saslauthd.conf ]]; then
_log 'trace' 'Creating /etc/saslauthd.conf' _log 'trace' 'Creating /etc/saslauthd.conf'
_create_config_saslauthd
# 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
fi fi
sed -i \ sed -i \
@ -42,3 +25,20 @@ EOF
gpasswd -a postfix sasl >/dev/null gpasswd -a postfix sasl >/dev/null
} }
function _create_config_saslauthd() {
local SASLAUTHD_LDAP_SERVER=${SASLAUTHD_LDAP_SERVER:=${LDAP_SERVER_HOST}}
local SASLAUTHD_LDAP_BIND_DN=${SASLAUTHD_LDAP_BIND_DN:=${LDAP_BIND_DN}}
local SASLAUTHD_LDAP_PASSWORD=${SASLAUTHD_LDAP_PASSWORD:=${LDAP_BIND_PW}}
local SASLAUTHD_LDAP_SEARCH_BASE=${SASLAUTHD_LDAP_SEARCH_BASE:=${LDAP_SEARCH_BASE}}
local SASLAUTHD_LDAP_FILTER=${SASLAUTHD_LDAP_FILTER:=(&(uniqueIdentifier=%u)(mailEnabled=TRUE))}
local SASLAUTHD_LDAP_REFERRALS=${SASLAUTHD_LDAP_REFERRALS:=yes}
# Generates a config from an ENV template while layering several other sources
# into a single temporary file, used as input into `_cleanse_config` which
# prepares the final output config.
_cleanse_config ':' <(cat 2>/dev/null \
/tmp/docker-mailserver/ldap/saslauthd.conf \
<(_template_with_env 'SASLAUTHD_' /etc/dms/ldap/saslauthd.tmpl) \
) > /etc/saslauthd.conf
}