docker-mailserver/target/scripts/helpers/accounts.sh
Georg Lauterbach ab55343d8e
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 <casperklein@users.noreply.github.com>
2022-08-22 08:31:32 +02:00

215 lines
7.9 KiB
Bash

#! /bin/bash
# Support for Postfix accounts managed via Dovecot
# It looks like the DOMAIN in below logic is being stored in /etc/postfix/vhost,
# even if it's a value used for Postfix `main.cf:mydestination`, which apparently isn't good?
# Only an issue when $myhostname is an exact match (eg: bare domain FQDN).
DOVECOT_USERDB_FILE=/etc/dovecot/userdb
DOVECOT_MASTERDB_FILE=/etc/dovecot/masterdb
function _create_accounts
{
: >/etc/postfix/vmailbox
: >"${DOVECOT_USERDB_FILE}"
[[ ${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"
sed -i 's|\r||g' "${DATABASE_ACCOUNTS}"
_log 'trace' "Regenerating postfix user list"
echo "# WARNING: this file is auto-generated. Modify ${DATABASE_ACCOUNTS} to edit the user list." > /etc/postfix/vmailbox
# checking that ${DATABASE_ACCOUNTS} ends with a newline
# shellcheck disable=SC1003
sed -i -e '$a\' "${DATABASE_ACCOUNTS}"
chown dovecot:dovecot "${DOVECOT_USERDB_FILE}"
chmod 640 "${DOVECOT_USERDB_FILE}"
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
# creating users ; 'pass' is encrypted
# comments and empty lines are ignored
local LOGIN PASS USER_ATTRIBUTES
while IFS=$'|' read -r LOGIN PASS USER_ATTRIBUTES
do
# Setting variables for better readability
USER=$(echo "${LOGIN}" | cut -d @ -f1)
DOMAIN=$(echo "${LOGIN}" | cut -d @ -f2)
# test if user has a defined quota
if [[ -f /tmp/docker-mailserver/dovecot-quotas.cf ]]
then
declare -a USER_QUOTA
IFS=':' read -r -a USER_QUOTA < <(grep "${USER}@${DOMAIN}:" -i /tmp/docker-mailserver/dovecot-quotas.cf)
if [[ ${#USER_QUOTA[@]} -eq 2 ]]
then
USER_ATTRIBUTES="${USER_ATTRIBUTES:+${USER_ATTRIBUTES} }userdb_quota_rule=*:bytes=${USER_QUOTA[1]}"
fi
fi
if [[ -z ${USER_ATTRIBUTES} ]]
then
_log 'debug' "Creating user '${USER}' for domain '${DOMAIN}'"
else
_log 'debug' "Creating user '${USER}' for domain '${DOMAIN}' with attributes '${USER_ATTRIBUTES}'"
fi
local POSTFIX_VMAILBOX_LINE DOVECOT_USERDB_LINE
POSTFIX_VMAILBOX_LINE="${LOGIN} ${DOMAIN}/${USER}/"
if grep -qF "${POSTFIX_VMAILBOX_LINE}" /etc/postfix/vmailbox
then
_log 'warn' "User '${USER}@${DOMAIN}' will not be added to '/etc/postfix/vmailbox' twice"
else
echo "${POSTFIX_VMAILBOX_LINE}" >>/etc/postfix/vmailbox
fi
# Dovecot's userdb has the following format
# user:password:uid:gid:(gecos):home:(shell):extra_fields
DOVECOT_USERDB_LINE="${LOGIN}:${PASS}:5000:5000::/var/mail/${DOMAIN}/${USER}::${USER_ATTRIBUTES}"
if grep -qF "${DOVECOT_USERDB_LINE}" "${DOVECOT_USERDB_FILE}"
then
_log 'warn' "Login '${LOGIN}' will not be added to '${DOVECOT_USERDB_FILE}' twice"
else
echo "${DOVECOT_USERDB_LINE}" >>"${DOVECOT_USERDB_FILE}"
fi
mkdir -p "/var/mail/${DOMAIN}/${USER}"
# copy user provided sieve file, if present
if [[ -e "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" ]]
then
cp "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" "/var/mail/${DOMAIN}/${USER}/.dovecot.sieve"
fi
done < <(_get_valid_lines_from_file "${DATABASE_ACCOUNTS}")
_create_dovecot_alias_dummy_accounts
fi
}
# Required when using Dovecot Quotas to avoid blacklisting risk from backscatter
# Note: This is a workaround only suitable for basic aliases that map to single real addresses,
# not multiple addresses (real accounts or additional aliases), those will not work with Postfix
# `quota-status` policy service and remain at risk of backscatter.
#
# see https://github.com/docker-mailserver/docker-mailserver/pull/2248#issuecomment-953313852
# for more details on this method
function _create_dovecot_alias_dummy_accounts
{
local DATABASE_VIRTUAL='/tmp/docker-mailserver/postfix-virtual.cf'
if [[ -f ${DATABASE_VIRTUAL} ]] && [[ ${ENABLE_QUOTAS} -eq 1 ]]
then
# adding aliases to Dovecot's userdb
# ${REAL_FQUN} is a user's fully-qualified username
local ALIAS REAL_FQUN DOVECOT_USERDB_LINE
while read -r ALIAS REAL_FQUN
do
# alias is assumed to not be a proper e-mail
# these aliases do not need to be added to Dovecot's userdb
[[ ! ${ALIAS} == *@* ]] && continue
# clear possibly already filled arrays
# do not remove the following line of code
unset REAL_ACC USER_QUOTA
declare -a REAL_ACC USER_QUOTA
local REAL_USERNAME REAL_DOMAINNAME
REAL_USERNAME=$(cut -d '@' -f 1 <<< "${REAL_FQUN}")
REAL_DOMAINNAME=$(cut -d '@' -f 2 <<< "${REAL_FQUN}")
if ! grep -q "${REAL_FQUN}" "${DATABASE_ACCOUNTS}"
then
_log 'debug' "Alias '${ALIAS}' is non-local (or mapped to a non-existing account) and will not be added to Dovecot's userdb"
continue
fi
_log 'debug' "Adding alias '${ALIAS}' for user '${REAL_FQUN}' to Dovecot's userdb"
# ${REAL_ACC[0]} => real account name (e-mail address) == ${REAL_FQUN}
# ${REAL_ACC[1]} => password hash
# ${REAL_ACC[2]} => optional user attributes
IFS='|' read -r -a REAL_ACC < <(grep "${REAL_FQUN}" "${DATABASE_ACCOUNTS}")
if [[ -z ${REAL_ACC[1]} ]]
then
dms_panic__misconfigured 'postfix-accounts.cf' 'alias configuration'
fi
# test if user has a defined quota
if [[ -f /tmp/docker-mailserver/dovecot-quotas.cf ]]
then
IFS=':' read -r -a USER_QUOTA < <(grep "${REAL_FQUN}:" -i /tmp/docker-mailserver/dovecot-quotas.cf)
if [[ ${#USER_QUOTA[@]} -eq 2 ]]
then
REAL_ACC[2]="${REAL_ACC[2]:+${REAL_ACC[2]} }userdb_quota_rule=*:bytes=${USER_QUOTA[1]}"
fi
fi
DOVECOT_USERDB_LINE="${ALIAS}:${REAL_ACC[1]}:5000:5000::/var/mail/${REAL_DOMAINNAME}/${REAL_USERNAME}::${REAL_ACC[2]:-}"
if grep -qi "^${ALIAS}:" "${DOVECOT_USERDB_FILE}"
then
_log 'warn' "Alias '${ALIAS}' will not be added to '${DOVECOT_USERDB_FILE}' twice"
else
echo "${DOVECOT_USERDB_LINE}" >>"${DOVECOT_USERDB_FILE}"
fi
done < <(_get_valid_lines_from_file "${DATABASE_VIRTUAL}")
fi
}
# Support Dovecot master user: https://doc.dovecot.org/configuration_manual/authentication/master_users/
# Supporting LDAP users requires `auth_bind = yes` in `dovecot-ldap.conf.ext`, see docker-mailserver/docker-mailserver/pull/2535 for details
function _create_masters
{
: >"${DOVECOT_MASTERDB_FILE}"
local DATABASE_DOVECOT_MASTERS='/tmp/docker-mailserver/dovecot-masters.cf'
if [[ -f ${DATABASE_DOVECOT_MASTERS} ]]
then
_log 'trace' "Checking file line endings"
sed -i 's|\r||g' "${DATABASE_DOVECOT_MASTERS}"
_log 'trace' "Regenerating dovecot masters list"
# checking that ${DATABASE_DOVECOT_MASTERS} ends with a newline
# shellcheck disable=SC1003
sed -i -e '$a\' "${DATABASE_DOVECOT_MASTERS}"
chown dovecot:dovecot "${DOVECOT_MASTERDB_FILE}"
chmod 640 "${DOVECOT_MASTERDB_FILE}"
sed -i -e '/\!include auth-master\.inc/s/^#//' /etc/dovecot/conf.d/10-auth.conf
# creating users ; 'pass' is encrypted
# comments and empty lines are ignored
local LOGIN PASS
while IFS=$'|' read -r LOGIN PASS
do
_log 'debug' "Creating master user '${LOGIN}'"
local DOVECOT_MASTERDB_LINE
# Dovecot's masterdb has the following format
# user:password
DOVECOT_MASTERDB_LINE="${LOGIN}:${PASS}"
if grep -qF "${DOVECOT_MASTERDB_LINE}" "${DOVECOT_MASTERDB_FILE}"
then
_log 'warn' "Login '${LOGIN}' will not be added to '${DOVECOT_MASTERDB_FILE}' twice"
else
echo "${DOVECOT_MASTERDB_LINE}" >>"${DOVECOT_MASTERDB_FILE}"
fi
done < <(_get_valid_lines_from_file "${DATABASE_DOVECOT_MASTERS}")
fi
}