Merge c811e6c854
into 2cf5856961
This commit is contained in:
commit
75326f8c3b
|
@ -68,6 +68,9 @@ target/postsrsd/** text
|
|||
*.local text
|
||||
### Postfix
|
||||
*.pcre text
|
||||
### Config Templates feature
|
||||
*.base text
|
||||
*.tmpl text
|
||||
|
||||
#################################################
|
||||
### Tests #####################################
|
||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -119,14 +119,8 @@ COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot
|
|||
# --- LDAP & SpamAssassin's Cron ----------------
|
||||
# -----------------------------------------------
|
||||
|
||||
COPY target/dovecot/dovecot-ldap.conf.ext /etc/dovecot
|
||||
COPY \
|
||||
target/postfix/ldap-users.cf \
|
||||
target/postfix/ldap-groups.cf \
|
||||
target/postfix/ldap-aliases.cf \
|
||||
target/postfix/ldap-domains.cf \
|
||||
target/postfix/ldap-senders.cf \
|
||||
/etc/postfix/
|
||||
# LDAP config template support:
|
||||
COPY --link target/features/ldap/ /etc/dms/ldap/
|
||||
|
||||
# hadolint ignore=SC2016
|
||||
RUN <<EOF
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
base = ou=people,dc=example,dc=com
|
||||
dn = ${BIND_DN}
|
||||
dnpass = ${BIND_PW}
|
||||
uris = ${SERVER_HOST}
|
||||
base = ${SEARCH_BASE}
|
||||
default_pass_scheme = SSHA
|
||||
dn = cn=admin,dc=example,dc=com
|
||||
dnpass = admin
|
||||
uris = ldap://mail.example.com
|
||||
tls = no
|
||||
ldap_version = 3
|
||||
pass_attrs = uniqueIdentifier=user,userPassword=password
|
||||
pass_filter = (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
|
||||
user_attrs = mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail
|
||||
user_filter = (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
|
||||
auth_bind = no
|
|
@ -0,0 +1,32 @@
|
|||
# Dovecot LDAP config docs: https://github.com/dovecot/core/blob/bbb600e46ca650a3a5ef812ea3a1e8c45a6ea0ba/doc/example-config/dovecot-ldap.conf.ext
|
||||
hosts = ${HOSTS}
|
||||
uris = ${URIS}
|
||||
dn = ${DN}
|
||||
dnpass = ${DNPASS}
|
||||
sasl_bind = ${SASL_BIND}
|
||||
sasl_mech = ${SASL_MECH}
|
||||
sasl_realm = ${SASL_REALM}
|
||||
sasl_authz_id = ${SASL_AUTHZ_ID}
|
||||
tls = ${TLS}
|
||||
tls_ca_cert_file = ${TLS_CA_CERT_FILE}
|
||||
tls_ca_cert_dir = ${TLS_CA_CERT_DIR}
|
||||
tls_cipher_suite = ${TLS_CIPHER_SUITE}
|
||||
tls_cert_file = ${TLS_CERT_FILE}
|
||||
tls_key_file = ${TLS_KEY_FILE}
|
||||
tls_require_cert = ${TLS_REQUIRE_CERT}
|
||||
ldaprc_path = ${LDAPRC_PATH}
|
||||
debug_level = ${DEBUG_LEVEL}
|
||||
auth_bind = ${AUTH_BIND}
|
||||
auth_bind_userdn = ${AUTH_BIND_USERDN}
|
||||
ldap_version = ${LDAP_VERSION}
|
||||
base = ${BASE}
|
||||
deref = ${DEREF}
|
||||
scope = ${SCOPE}
|
||||
user_attrs = ${USER_ATTRS}
|
||||
user_filter = ${USER_FILTER}
|
||||
pass_attrs = ${PASS_ATTRS}
|
||||
pass_filter = ${PASS_FILTER}
|
||||
iterate_attrs = ${ITERATE_ATTRS}
|
||||
iterate_filter = ${ITERATE_FILTER}
|
||||
default_pass_scheme = ${DEFAULT_PASS_SCHEME}
|
||||
blocking = ${BLOCKING}
|
|
@ -0,0 +1,7 @@
|
|||
bind_dn = ${BIND_DN}
|
||||
bind_pw = ${BIND_PW}
|
||||
server_host = ${SERVER_HOST}
|
||||
search_base = ${SEARCH_BASE}
|
||||
bind = yes
|
||||
result_attribute = mail
|
||||
version = 3
|
|
@ -0,0 +1,35 @@
|
|||
# Postfix LDAP table docs: http://www.postfix.org/ldap_table.5.html
|
||||
server_host = ${SERVER_HOST}
|
||||
server_port = ${SERVER_PORT}
|
||||
timeout = ${TIMEOUT}
|
||||
search_base = ${SEARCH_BASE}
|
||||
query_filter = ${QUERY_FILTER}
|
||||
result_format = ${RESULT_FORMAT}
|
||||
domain = ${DOMAIN}
|
||||
result_attribute = ${RESULT_ATTRIBUTE}
|
||||
special_result_attribute = ${SPECIAL_RESULT_ATTRIBUTE}
|
||||
terminal_result_attribute = ${TERMINAL_RESULT_ATTRIBUTE}
|
||||
leaf_result_attribute = ${LEAF_RESULT_ATTRIBUTE}
|
||||
scope = ${SCOPE}
|
||||
bind = ${BIND}
|
||||
bind_dn = ${BIND_DN}
|
||||
bind_pw = ${BIND_PW}
|
||||
recursion_limit = ${RECURSION_LIMIT}
|
||||
expansion_limit = ${EXPANSION_LIMIT}
|
||||
size_limit = ${SIZE_LIMIT}
|
||||
dereference = ${DEREFERENCE}
|
||||
chase_referrals = ${CHASE_REFERRALS}
|
||||
version = ${VERSION}
|
||||
debuglevel = ${DEBUGLEVEL}
|
||||
sasl_mechs = ${SASL_MECHS}
|
||||
sasl_realm = ${SASL_REALM}
|
||||
sasl_authz_id = ${SASL_AUTHZ_ID}
|
||||
sasl_minssf = ${SASL_MINSSF}
|
||||
start_tls = ${START_TLS}
|
||||
tls_ca_cert_dir = ${TLS_CA_CERT_DIR}
|
||||
tls_ca_cert_file = ${TLS_CA_CERT_FILE}
|
||||
tls_cert = ${TLS_CERT}
|
||||
tls_key = ${TLS_KEY}
|
||||
tls_require_cert = ${TLS_REQUIRE_CERT}
|
||||
tls_random_file = ${TLS_RANDOM_FILE}
|
||||
tls_cipher_suite = ${TLS_CIPHER_SUITE}
|
|
@ -0,0 +1,6 @@
|
|||
ldap_bind_dn: ${BIND_DN}
|
||||
ldap_bind_pw: ${BIND_PW}
|
||||
ldap_servers: ${SERVER_HOST}
|
||||
ldap_search_base: ${SEARCH_BASE}
|
||||
ldap_filter: (&(uniqueIdentifier=%u)(mailEnabled=TRUE))
|
||||
ldap_referrals: yes
|
|
@ -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}
|
|
@ -1,9 +0,0 @@
|
|||
bind = yes
|
||||
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=example,dc=com
|
||||
server_host = mail.example.com
|
||||
start_tls = no
|
||||
version = 3
|
|
@ -1,9 +0,0 @@
|
|||
bind = yes
|
||||
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=example,dc=com
|
||||
server_host = mail.example.com
|
||||
start_tls = no
|
||||
version = 3
|
|
@ -1,9 +0,0 @@
|
|||
bind = yes
|
||||
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=example,dc=com
|
||||
server_host = mail.example.com
|
||||
start_tls = no
|
||||
version = 3
|
|
@ -1,9 +0,0 @@
|
|||
bind = yes
|
||||
bind_dn = cn=admin,dc=example,dc=com
|
||||
bind_pw = admin
|
||||
query_filter = (mail=%s)
|
||||
result_attribute = mail, uid
|
||||
search_base = ou=people,dc=example,dc=com
|
||||
server_host = mail.example.com
|
||||
start_tls = no
|
||||
version = 3
|
|
@ -1,9 +0,0 @@
|
|||
bind = yes
|
||||
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=example,dc=com
|
||||
server_host = mail.example.com
|
||||
start_tls = no
|
||||
version = 3
|
|
@ -92,6 +92,17 @@ function _install_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:
|
||||
# Download from GH releases to stdout, then extract the zenv file to make available via PATH:
|
||||
curl -L "https://github.com/numToStr/zenv/releases/download/0.8.0/zenv-0.8.0-$(uname --machine)-unknown-linux-gnu.tar.gz" -o - | tar --gzip --extract --directory /usr/local/bin --file - zenv
|
||||
}
|
||||
|
||||
function _install_dovecot() {
|
||||
declare -a DOVECOT_PACKAGES
|
||||
|
||||
|
@ -227,5 +238,6 @@ _install_rspamd
|
|||
_install_fail2ban
|
||||
_install_getmail
|
||||
_install_utils
|
||||
_install_feature_config_templates
|
||||
_remove_data_after_package_installations
|
||||
_post_installation_steps
|
||||
|
|
|
@ -69,7 +69,7 @@ function _vhost_collect_postfix_domains() {
|
|||
# NOTE: `setup-stack.sh:_setup_ldap` has related logic:
|
||||
# - `main.cf:mydestination` setting removes `$mydestination` as an LDAP bugfix.
|
||||
# - `main.cf:virtual_mailbox_domains` uses `/etc/postfix/vhost`, but may
|
||||
# conditionally include a 2nd table (ldap:/etc/postfix/ldap-domains.cf).
|
||||
# conditionally include a 2nd table (ldap:/etc/postfix/ldap/domains.cf).
|
||||
function _vhost_ldap_support() {
|
||||
[[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] && echo "${DOMAINNAME}" >>"${TMP_VHOST}"
|
||||
}
|
||||
|
|
|
@ -153,3 +153,41 @@ 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"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Replace `${VAR}` variables of the input file with their equivalent ENV values (excluding the common prefix)
|
||||
#
|
||||
# @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
|
||||
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
}
|
||||
|
|
|
@ -2,80 +2,63 @@
|
|||
|
||||
function _setup_ldap() {
|
||||
_log 'debug' 'Setting up LDAP'
|
||||
_log 'trace' 'Checking for custom configs'
|
||||
|
||||
for i in 'users' 'groups' 'aliases' 'domains'; do
|
||||
local FPATH="/tmp/docker-mailserver/ldap-${i}.cf"
|
||||
if [[ -f ${FPATH} ]]; then
|
||||
cp "${FPATH}" "/etc/postfix/ldap-${i}.cf"
|
||||
fi
|
||||
_log 'trace' "Configuring Postfix for LDAP"
|
||||
|
||||
# Configure Postfix settings for LDAP configs in advance:
|
||||
postconf \
|
||||
'virtual_mailbox_maps = ldap:/etc/postfix/ldap/users.cf' \
|
||||
'virtual_mailbox_domains = /etc/postfix/vhost ldap:/etc/postfix/ldap/domains.cf' \
|
||||
'virtual_alias_maps = ldap:/etc/postfix/ldap/aliases.cf ldap:/etc/postfix/ldap/groups.cf'
|
||||
|
||||
# Generate Postfix LDAP configs:
|
||||
mkdir -p /etc/postfix/ldap
|
||||
for QUERY_KIND in 'users' 'groups' 'aliases' 'domains' 'senders'; do
|
||||
_create_config_postfix "${QUERY_KIND}"
|
||||
done
|
||||
|
||||
_log 'trace' 'Starting to override configs'
|
||||
|
||||
local FILES=(
|
||||
/etc/postfix/ldap-users.cf
|
||||
/etc/postfix/ldap-groups.cf
|
||||
/etc/postfix/ldap-aliases.cf
|
||||
/etc/postfix/ldap-domains.cf
|
||||
/etc/postfix/ldap-senders.cf
|
||||
/etc/postfix/maps/sender_login_maps.ldap
|
||||
)
|
||||
|
||||
for FILE in "${FILES[@]}"; do
|
||||
[[ ${FILE} =~ ldap-user ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_USER}"
|
||||
[[ ${FILE} =~ ldap-group ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_GROUP}"
|
||||
[[ ${FILE} =~ ldap-aliases ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_ALIAS}"
|
||||
[[ ${FILE} =~ ldap-domains ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_DOMAIN}"
|
||||
[[ ${FILE} =~ ldap-senders ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_SENDERS}"
|
||||
[[ -f ${FILE} ]] && _replace_by_env_in_file 'LDAP_' "${FILE}"
|
||||
done
|
||||
|
||||
_log 'trace' "Configuring Dovecot LDAP"
|
||||
|
||||
declare -A DOVECOT_LDAP_MAPPING
|
||||
|
||||
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:="${LDAP_SERVER_HOST}"}"
|
||||
|
||||
_log 'trace' "Configuring Dovecot for LDAP"
|
||||
# Default DOVECOT_PASS_FILTER to the same value as DOVECOT_USER_FILTER
|
||||
DOVECOT_LDAP_MAPPING['DOVECOT_PASS_FILTER']="${DOVECOT_PASS_FILTER:="${DOVECOT_USER_FILTER}"}"
|
||||
|
||||
for VAR in "${!DOVECOT_LDAP_MAPPING[@]}"; do
|
||||
export "${VAR}=${DOVECOT_LDAP_MAPPING[${VAR}]}"
|
||||
done
|
||||
|
||||
_replace_by_env_in_file 'DOVECOT_' '/etc/dovecot/dovecot-ldap.conf.ext'
|
||||
local DOVECOT_PASS_FILTER="${DOVECOT_PASS_FILTER:="${DOVECOT_USER_FILTER}"}"
|
||||
_create_config_dovecot
|
||||
|
||||
_log 'trace' 'Enabling Dovecot LDAP authentication'
|
||||
|
||||
sed -i -e '/\!include auth-ldap\.conf\.ext/s/^#//' /etc/dovecot/conf.d/10-auth.conf
|
||||
sed -i -e '/\!include auth-passwdfile\.inc/s/^/#/' /etc/dovecot/conf.d/10-auth.conf
|
||||
|
||||
_log 'trace' "Configuring LDAP"
|
||||
|
||||
if [[ -f /etc/postfix/ldap-users.cf ]]; then
|
||||
postconf 'virtual_mailbox_maps = ldap:/etc/postfix/ldap-users.cf'
|
||||
else
|
||||
_log 'warn' "'/etc/postfix/ldap-users.cf' not found"
|
||||
fi
|
||||
|
||||
if [[ -f /etc/postfix/ldap-domains.cf ]]; then
|
||||
postconf 'virtual_mailbox_domains = /etc/postfix/vhost, ldap:/etc/postfix/ldap-domains.cf'
|
||||
else
|
||||
_log 'warn' "'/etc/postfix/ldap-domains.cf' not found"
|
||||
fi
|
||||
|
||||
if [[ -f /etc/postfix/ldap-aliases.cf ]] && [[ -f /etc/postfix/ldap-groups.cf ]]; then
|
||||
postconf 'virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf, ldap:/etc/postfix/ldap-groups.cf'
|
||||
else
|
||||
_log 'warn' "'/etc/postfix/ldap-aliases.cf' and / or '/etc/postfix/ldap-groups.cf' not found"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
sed -i 's|mydestination = \$myhostname, |mydestination = |' /etc/postfix/main.cf
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 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.
|
||||
function _create_config_dovecot() {
|
||||
_cleanse_config '=' <(cat 2>/dev/null \
|
||||
<(_template_with_env 'LDAP_' /etc/dms/ldap/dovecot.base) \
|
||||
/tmp/docker-mailserver/ldap/dovecot.conf \
|
||||
<(_template_with_env 'DOVECOT_' /etc/dms/ldap/dovecot.tmpl) \
|
||||
) > /etc/dovecot/dovecot-ldap.conf.ext
|
||||
}
|
||||
|
||||
function _create_config_postfix() {
|
||||
local QUERY_KIND=${1:?QUERY_KIND is required in _create_config_postfix}
|
||||
local LDAP_CONFIG_FILE="/etc/postfix/ldap/${QUERY_KIND}.cf"
|
||||
|
||||
_cleanse_config '=' <(cat 2>/dev/null \
|
||||
<(_template_with_env 'LDAP_' /etc/dms/ldap/postfix.base) \
|
||||
"/tmp/docker-mailserver/ldap-${QUERY_KIND}.cf" \
|
||||
<(_template_with_env 'POSTFIX_' /etc/dms/ldap/postfix.tmpl) \
|
||||
<(_template_with_env "POSTFIX_${QUERY_KIND^^}_" /etc/dms/ldap/postfix.tmpl) \
|
||||
) > "${LDAP_CONFIG_FILE}"
|
||||
|
||||
# Opt-out of generated config if `query_filter` was not configured:
|
||||
if ! grep -q '^query_filter =' "${LDAP_CONFIG_FILE}"; then
|
||||
_log 'warn' "'${LDAP_CONFIG_FILE}' is missing the 'query_filter' setting - disabling"
|
||||
|
||||
sed -i "s/$(_escape_for_sed "${LDAP_CONFIG_FILE}")//" /etc/postfix/main.cf
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -9,24 +9,7 @@ function _setup_saslauthd() {
|
|||
if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] \
|
||||
&& [[ ! -f /etc/saslauthd.conf ]]; then
|
||||
_log 'trace' 'Creating /etc/saslauthd.conf'
|
||||
|
||||
# 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
|
||||
_create_config_saslauthd
|
||||
fi
|
||||
|
||||
sed -i \
|
||||
|
@ -42,3 +25,14 @@ EOF
|
|||
|
||||
gpasswd -a postfix sasl >/dev/null
|
||||
}
|
||||
|
||||
# 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.
|
||||
function _create_config_saslauthd() {
|
||||
_cleanse_config ':' <(cat 2>/dev/null \
|
||||
<(_template_with_env 'LDAP_' /etc/dms/ldap/saslauthd.base) \
|
||||
/tmp/docker-mailserver/ldap/saslauthd.conf \
|
||||
<(_template_with_env 'SASLAUTHD_' /etc/dms/ldap/saslauthd.tmpl) \
|
||||
) > /etc/saslauthd.conf
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ function _setup_spoof_protection() {
|
|||
|
||||
if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]]; then
|
||||
if [[ -z ${LDAP_QUERY_FILTER_SENDERS} ]]; then
|
||||
postconf 'smtpd_sender_login_maps = ldap:/etc/postfix/ldap-users.cf ldap:/etc/postfix/ldap-aliases.cf ldap:/etc/postfix/ldap-groups.cf'
|
||||
postconf 'smtpd_sender_login_maps = ldap:/etc/postfix/ldap/users.cf ldap:/etc/postfix/ldap/aliases.cf ldap:/etc/postfix/ldap/groups.cf'
|
||||
else
|
||||
postconf 'smtpd_sender_login_maps = ldap:/etc/postfix/ldap-senders.cf'
|
||||
postconf 'smtpd_sender_login_maps = ldap:/etc/postfix/ldap/senders.cf'
|
||||
fi
|
||||
else
|
||||
# NOTE: This file is always created at startup, it potentially has content added.
|
||||
|
|
|
@ -39,25 +39,24 @@ function setup_file() {
|
|||
#
|
||||
|
||||
# LDAP filter queries explained.
|
||||
# NOTE: All LDAP configs for Postfix (with the exception of `ldap-senders.cf`), return the `mail` attribute value of matched results.
|
||||
# This is through the config key `result_attribute`, which the ENV substitution feature can only replace across all configs, not selectively like `query_filter`.
|
||||
# NOTE: All LDAP configs use `result_attribute = mail` for Postfix to return the `mail` attribute value from query matched results.
|
||||
# NOTE: The queries below rely specifically upon attributes and classes defined by the schema `postfix-book.ldif`. These are not compatible with all LDAP setups.
|
||||
|
||||
# `mailAlias`` is supported by both classes provided from the schema `postfix-book.ldif`, but `mailEnabled` is only available to `PostfixBookMailAccount` class:
|
||||
local QUERY_ALIAS='(&(mailAlias=%s) (| (objectClass=PostfixBookMailForward) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) ))'
|
||||
# `mailAlias` is supported by both classes provided from the schema `postfix-book.ldif`, but `mailEnabled` is only available to `PostfixBookMailAccount` class:
|
||||
local QUERY_ALIASES='(&(mailAlias=%s) (| (objectClass=PostfixBookMailForward) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) ))'
|
||||
|
||||
# Postfix does domain lookups with the domain of the recipient to check if DMS manages the mail domain.
|
||||
# For this lookup `%s` only represents the domain, not a full email address. Hence the match pattern using a wildcard prefix `*@`.
|
||||
# For a breakdown, see QUERY_SENDERS comment.
|
||||
# For a breakdown, see the `QUERY_SENDERS` comment.
|
||||
# NOTE: Although `result_attribute = mail` will return each accounts full email address, Postfix will only compare to domain-part.
|
||||
local QUERY_DOMAIN='(| (& (|(mail=*@%s) (mailAlias=*@%s) (mailGroupMember=*@%s)) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) ) (&(mailAlias=*@%s)(objectClass=PostfixBookMailForward)) )'
|
||||
local QUERY_DOMAINS='(| (& (|(mail=*@%s) (mailAlias=*@%s) (mailGroupMember=*@%s)) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) ) (&(mailAlias=*@%s)(objectClass=PostfixBookMailForward)) )'
|
||||
|
||||
# Simple queries for a single attribute that additionally requires `mailEnabled=TRUE` from the `PostfixBookMailAccount` class:
|
||||
# NOTE: `mail` attribute is not unique to `PostfixBookMailAccount`. The `mailEnabled` attribute is to further control valid mail accounts.
|
||||
# TODO: For tests, since `mailEnabled` is not relevant (always configured as TRUE currently),
|
||||
# a simpler query like `mail=%s` or `mailGroupMember=%s` would be sufficient. The additional constraints could be covered in our docs instead.
|
||||
local QUERY_GROUP='(&(mailGroupMember=%s) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) )'
|
||||
local QUERY_USER='(&(mail=%s) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) )'
|
||||
local QUERY_GROUPS='(&(mailGroupMember=%s) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) )'
|
||||
local QUERY_USERS='(&(mail=%s) (&(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)) )'
|
||||
|
||||
# Given the sender address `%s` from Postfix, query LDAP for accounts that meet the search filter,
|
||||
# the `result_attribute` is `mail` + `uid` (`userID`) attributes for login names that are authorized to use that sender address.
|
||||
|
@ -93,10 +92,9 @@ function setup_file() {
|
|||
--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):
|
||||
# NOTE: `scripts/startup/setup.d/ldap.sh:_setup_ldap()` uses helper methods to generate / override LDAP configs (grouped by common ENV 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'
|
||||
|
@ -113,11 +111,12 @@ function setup_file() {
|
|||
--env DOVECOT_TLS=no
|
||||
|
||||
# Postfix:
|
||||
--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 POSTFIX_ALIASES_QUERY_FILTER="${QUERY_ALIASES}"
|
||||
--env POSTFIX_DOMAINS_QUERY_FILTER="${QUERY_DOMAINS}"
|
||||
--env POSTFIX_GROUPS_QUERY_FILTER="${QUERY_GROUPS}"
|
||||
--env POSTFIX_SENDERS_QUERY_FILTER="${QUERY_SENDERS}"
|
||||
--env POSTFIX_USERS_QUERY_FILTER="${QUERY_USERS}"
|
||||
--env POSTFIX_START_TLS=no
|
||||
)
|
||||
|
||||
# Extra ENV needed to support specific test-cases:
|
||||
|
@ -199,7 +198,7 @@ function teardown() {
|
|||
# REF: https://github.com/docker-mailserver/docker-mailserver/pull/642#issuecomment-313916384
|
||||
# NOTE: This account has no `mailAlias` or `mailGroupMember` defined in it's `.ldif`.
|
||||
local MAIL_ACCOUNT="some.user.email@${FQDN_LOCALHOST_A}"
|
||||
_run_in_container postmap -q "${MAIL_ACCOUNT}" ldap:/etc/postfix/ldap-users.cf
|
||||
_run_in_container postmap -q "${MAIL_ACCOUNT}" ldap:/etc/postfix/ldap/users.cf
|
||||
assert_success
|
||||
assert_output "${MAIL_ACCOUNT}"
|
||||
}
|
||||
|
@ -210,9 +209,9 @@ function teardown() {
|
|||
export CONTAINER_NAME=${CONTAINER3_NAME}
|
||||
|
||||
local LDAP_CONFIGS_POSTFIX=(
|
||||
/etc/postfix/ldap-users.cf
|
||||
/etc/postfix/ldap-groups.cf
|
||||
/etc/postfix/ldap-aliases.cf
|
||||
/etc/postfix/ldap/users.cf
|
||||
/etc/postfix/ldap/groups.cf
|
||||
/etc/postfix/ldap/aliases.cf
|
||||
)
|
||||
|
||||
for LDAP_CONFIG in "${LDAP_CONFIGS_POSTFIX[@]}"; do
|
||||
|
@ -230,19 +229,9 @@ function teardown() {
|
|||
)
|
||||
|
||||
for LDAP_SETTING in "${LDAP_SETTINGS_POSTFIX[@]}"; do
|
||||
# "${LDAP_SETTING%=*}" is to match only the key portion of the var (helpful for assert_output error messages)
|
||||
# NOTE: `start_tls = no` is a default setting, but the white-space differs when ENV `LDAP_START_TLS` is not set explicitly.
|
||||
_run_in_container grep "${LDAP_SETTING%=*}" /etc/postfix/ldap-users.cf
|
||||
assert_output "${LDAP_SETTING}"
|
||||
assert_success
|
||||
|
||||
_run_in_container grep "${LDAP_SETTING%=*}" /etc/postfix/ldap-groups.cf
|
||||
assert_output "${LDAP_SETTING}"
|
||||
assert_success
|
||||
|
||||
_run_in_container grep "${LDAP_SETTING%=*}" /etc/postfix/ldap-aliases.cf
|
||||
assert_output "${LDAP_SETTING}"
|
||||
assert_success
|
||||
_should_have_matching_setting "${LDAP_SETTING}" /etc/postfix/ldap/users.cf
|
||||
_should_have_matching_setting "${LDAP_SETTING}" /etc/postfix/ldap/groups.cf
|
||||
_should_have_matching_setting "${LDAP_SETTING}" /etc/postfix/ldap/aliases.cf
|
||||
done
|
||||
}
|
||||
|
||||
|
@ -270,9 +259,7 @@ function teardown() {
|
|||
)
|
||||
|
||||
for LDAP_SETTING in "${LDAP_SETTINGS_DOVECOT[@]}"; do
|
||||
_run_in_container grep "${LDAP_SETTING%=*}" /etc/dovecot/dovecot-ldap.conf.ext
|
||||
assert_output "${LDAP_SETTING}"
|
||||
assert_success
|
||||
_should_have_matching_setting "${LDAP_SETTING}" /etc/dovecot/dovecot-ldap.conf.ext
|
||||
done
|
||||
}
|
||||
|
||||
|
@ -402,20 +389,20 @@ function _should_exist_in_ldap_tables() {
|
|||
local DOMAIN_PART="${MAIL_ACCOUNT#*@}"
|
||||
|
||||
# Each LDAP config file sets `query_filter` to lookup a key in LDAP (values defined in `.ldif` test files)
|
||||
# `mail` (ldap-users), `mailAlias` (ldap-aliases), `mailGroupMember` (ldap-groups)
|
||||
# `mail` (ldap/users.cf), `mailAlias` (ldap/aliases.cf), `mailGroupMember` (ldap/groups.cf)
|
||||
# `postmap` is queried with the mail account address, and the LDAP service should respond with
|
||||
# `result_attribute` which is the LDAP `mail` value (should match what we'r'e quering `postmap` with)
|
||||
|
||||
_run_in_container postmap -q "${MAIL_ACCOUNT}" ldap:/etc/postfix/ldap-users.cf
|
||||
_run_in_container postmap -q "${MAIL_ACCOUNT}" ldap:/etc/postfix/ldap/users.cf
|
||||
assert_success
|
||||
assert_output "${MAIL_ACCOUNT}"
|
||||
|
||||
# Check which account has the `postmaster` virtual alias:
|
||||
_run_in_container postmap -q "postmaster@${DOMAIN_PART}" ldap:/etc/postfix/ldap-aliases.cf
|
||||
_run_in_container postmap -q "postmaster@${DOMAIN_PART}" ldap:/etc/postfix/ldap/aliases.cf
|
||||
assert_success
|
||||
assert_output "${MAIL_ACCOUNT}"
|
||||
|
||||
_run_in_container postmap -q "employees@${DOMAIN_PART}" ldap:/etc/postfix/ldap-groups.cf
|
||||
_run_in_container postmap -q "employees@${DOMAIN_PART}" ldap:/etc/postfix/ldap/groups.cf
|
||||
assert_success
|
||||
assert_output "${MAIL_ACCOUNT}"
|
||||
}
|
||||
|
@ -439,3 +426,16 @@ function _should_successfully_deliver_mail_to() {
|
|||
# NOTE: Prevents compatibility for running testcases in parallel (for same container) when the count could become racey:
|
||||
_count_files_in_directory_in_container "${MAIL_STORAGE_RECIPIENT}" 1
|
||||
}
|
||||
|
||||
function _should_have_matching_setting() {
|
||||
local KEY_VALUE=${1}
|
||||
local CONFIG_FILE=${2}
|
||||
local KV_DELIMITER=${3:-'='}
|
||||
|
||||
local KEY
|
||||
KEY="${KEY_VALUE%%"${KV_DELIMITER}"*}"
|
||||
# Look up the KEY portion from the target config file and use sed to reduce white-space between key and value:
|
||||
_run_in_container_bash "grep '^${KEY}' '${CONFIG_FILE}' | sed 's/\s*${KV_DELIMITER}\s*/ ${KV_DELIMITER} /'"
|
||||
assert_output "${LDAP_SETTING}"
|
||||
assert_success
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue