Fix/refactor relayhost_map update when dynamically adding account

check-for-changes.sh did not have a special case to handle lines in
postfix-relaymap.cf consisting of only a domain (indicating that said
domain should never be relayed). This case is handled by
start-mailserver.sh so when such a line existed, things would work well
until a config file update was detected by check-for-changes.sh. After
that, the generated relayhost_map file would be corrupted.

Fixed by factoring a 'populate_relayhost_map' function out of
start-mailserver.sh and into helper_functions.sh and reusing it in
check-for-changes.sh.

Note: There are certainly quite a few more pieces of code that could be
refactored in a similar fashion.

Note2: check-for-changes.sh would previously never update the
relayhost_map file when $ENABLE_LDAP was set to 1. I don't think this
was intended —there is after all no such condition in
start-mailserver.sh— and so this condition no longer applies.
This commit is contained in:
mwnx 2020-08-24 22:08:11 +02:00
parent 2a70f33a4b
commit 1286a1266b
4 changed files with 123 additions and 116 deletions

View file

@ -95,7 +95,6 @@ if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
if [ ! -z "$RELAY_HOST" ]; then if [ ! -z "$RELAY_HOST" ]; then
# keep old config # keep old config
echo -n > /etc/postfix/sasl_passwd echo -n > /etc/postfix/sasl_passwd
echo -n > /etc/postfix/relayhost_map
if [ ! -z "$SASL_PASSWD" ]; then if [ ! -z "$SASL_PASSWD" ]; then
echo "$SASL_PASSWD" >> /etc/postfix/sasl_passwd echo "$SASL_PASSWD" >> /etc/postfix/sasl_passwd
fi fi
@ -111,14 +110,6 @@ if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
if [ ! -z "$RELAY_USER" ] && [ ! -z "$RELAY_PASSWORD" ]; then if [ ! -z "$RELAY_USER" ] && [ ! -z "$RELAY_PASSWORD" ]; then
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >> /etc/postfix/sasl_passwd echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >> /etc/postfix/sasl_passwd
fi fi
# add relay maps from file
if [ -f /tmp/docker-mailserver/postfix-relaymap.cf ]; then
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-relaymap.cf || true) | while read line; do
if ! echo "$line" | grep -q -e "\s*#"; then
echo "$line" >> /etc/postfix/relayhost_map
fi
done
fi
fi fi
# Creating users # Creating users
@ -152,22 +143,15 @@ if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
# Copy user provided sieve file, if present # Copy user provided sieve file, if present
test -e /tmp/docker-mailserver/${login}.dovecot.sieve && cp /tmp/docker-mailserver/${login}.dovecot.sieve /var/mail/${domain}/${user}/.dovecot.sieve test -e /tmp/docker-mailserver/${login}.dovecot.sieve && cp /tmp/docker-mailserver/${login}.dovecot.sieve /var/mail/${domain}/${user}/.dovecot.sieve
echo ${domain} >> /tmp/vhost.tmp echo ${domain} >> /tmp/vhost.tmp
# add domains to relayhost_map
if [ ! -z "$RELAY_HOST" ]; then
if ! grep -q -e "^@${domain}\s" /etc/postfix/relayhost_map; then
echo "@${domain} [$RELAY_HOST]:$RELAY_PORT" >> /etc/postfix/relayhost_map
fi
fi
done done
fi fi
if [ ! -z "$RELAY_HOST" ]; then
populate_relayhost_map
fi
if [ -f /etc/postfix/sasl_passwd ]; then if [ -f /etc/postfix/sasl_passwd ]; then
chown root:root /etc/postfix/sasl_passwd chown root:root /etc/postfix/sasl_passwd
chmod 0600 /etc/postfix/sasl_passwd chmod 0600 /etc/postfix/sasl_passwd
fi fi
if [ -f /etc/postfix/relayhost_map ]; then
chown root:root /etc/postfix/relayhost_map
chmod 0600 /etc/postfix/relayhost_map
fi
if [ -f postfix-virtual.cf ]; then if [ -f postfix-virtual.cf ]; then
# regen postfix aliases # regen postfix aliases
echo -n > /etc/postfix/virtual echo -n > /etc/postfix/virtual

View file

@ -74,6 +74,93 @@ for key, value in acme.items():
fi fi
} }
declare -A DEFAULT_VARS
DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}"
function notify () {
c_red="\e[0;31m"
c_green="\e[0;32m"
c_brown="\e[0;33m"
c_blue="\e[0;34m"
c_bold="\033[1m"
c_reset="\e[0m"
notification_type=$1
notification_msg=$2
notification_format=$3
msg=""
case "${notification_type}" in
'taskgrp')
msg="${c_bold}${notification_msg}${c_reset}"
;;
'task')
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
msg=" ${notification_msg}${c_reset}"
fi
;;
'inf')
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
msg="${c_green} * ${notification_msg}${c_reset}"
fi
;;
'started')
msg="${c_green} ${notification_msg}${c_reset}"
;;
'warn')
msg="${c_brown} * ${notification_msg}${c_reset}"
;;
'err')
msg="${c_red} * ${notification_msg}${c_reset}"
;;
'fatal')
msg="${c_red}Error: ${notification_msg}${c_reset}"
;;
*)
msg=""
;;
esac
case "${notification_format}" in
'n')
options="-ne"
;;
*)
options="-e"
;;
esac
[[ ! -z "${msg}" ]] && echo $options "${msg}"
}
# setup /etc/postfix/relayhost_map
# --
# @domain1.com [smtp.mailgun.org]:587
# @domain2.com [smtp.mailgun.org]:587
# @domain3.com [smtp.mailgun.org]:587
function populate_relayhost_map() {
echo -n > /etc/postfix/relayhost_map
chown root:root /etc/postfix/relayhost_map
chmod 0600 /etc/postfix/relayhost_map
if [ -f /tmp/docker-mailserver/postfix-relaymap.cf ]; then
notify 'inf' "Adding relay mappings from postfix-relaymap.cf"
# Keep lines which are not a comment *and* have a destination.
sed -n '/^\s*[^#[:space:]]\S*\s\+\S/p' /tmp/docker-mailserver/postfix-relaymap.cf \
>> /etc/postfix/relayhost_map
fi
# Note: Won't detect domains when lhs has spaces (but who does that?!).
sed -n '/^\s*[^#[:space:]]/ s/^[^@|]*@\([^|]\+\)|.*$/\1/p' /tmp/docker-mailserver/postfix-accounts.cf |
while read domain; do
if ! grep -q -e "^@${domain}\b" /etc/postfix/relayhost_map &&
! grep -qs -e "^\s*@${domain}\s*$" /tmp/docker-mailserver/postfix-relaymap.cf; then
# Domain not already present *and* not ignored.
notify 'inf' "Adding relay mapping for ${domain}"
echo "@${domain} [$RELAY_HOST]:$RELAY_PORT" >> /etc/postfix/relayhost_map
fi
done
}
# File storing the checksums of the monitored files. # File storing the checksums of the monitored files.
CHKSUM_FILE=/tmp/docker-mailserver-config-chksum CHKSUM_FILE=/tmp/docker-mailserver-config-chksum

View file

@ -29,7 +29,7 @@ DEFAULT_VARS["POSTFIX_MAILBOX_SIZE_LIMIT"]="${POSTFIX_MAILBOX_SIZE_LIMIT:="0"}"
DEFAULT_VARS["POSTFIX_INET_PROTOCOLS"]="${POSTFIX_INET_PROTOCOLS:="all"}" DEFAULT_VARS["POSTFIX_INET_PROTOCOLS"]="${POSTFIX_INET_PROTOCOLS:="all"}"
DEFAULT_VARS["ENABLE_SASLAUTHD"]="${ENABLE_SASLAUTHD:="0"}" DEFAULT_VARS["ENABLE_SASLAUTHD"]="${ENABLE_SASLAUTHD:="0"}"
DEFAULT_VARS["SMTP_ONLY"]="${SMTP_ONLY:="0"}" DEFAULT_VARS["SMTP_ONLY"]="${SMTP_ONLY:="0"}"
DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}" # DEFAULT_VARS["DMS_DEBUG"] defined in helper_functions.sh
DEFAULT_VARS["OVERRIDE_HOSTNAME"]="${OVERRIDE_HOSTNAME}" DEFAULT_VARS["OVERRIDE_HOSTNAME"]="${OVERRIDE_HOSTNAME}"
DEFAULT_VARS["POSTSCREEN_ACTION"]="${POSTSCREEN_ACTION:="enforce"}" DEFAULT_VARS["POSTSCREEN_ACTION"]="${POSTSCREEN_ACTION:="enforce"}"
DEFAULT_VARS["SPOOF_PROTECTION"]="${SPOOF_PROTECTION:="0"}" DEFAULT_VARS["SPOOF_PROTECTION"]="${SPOOF_PROTECTION:="0"}"
@ -309,62 +309,6 @@ function _register_misc_function() {
########################################################################## ##########################################################################
function notify () {
c_red="\e[0;31m"
c_green="\e[0;32m"
c_brown="\e[0;33m"
c_blue="\e[0;34m"
c_bold="\033[1m"
c_reset="\e[0m"
notification_type=$1
notification_msg=$2
notification_format=$3
msg=""
case "${notification_type}" in
'taskgrp')
msg="${c_bold}${notification_msg}${c_reset}"
;;
'task')
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
msg=" ${notification_msg}${c_reset}"
fi
;;
'inf')
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
msg="${c_green} * ${notification_msg}${c_reset}"
fi
;;
'started')
msg="${c_green} ${notification_msg}${c_reset}"
;;
'warn')
msg="${c_brown} * ${notification_msg}${c_reset}"
;;
'err')
msg="${c_red} * ${notification_msg}${c_reset}"
;;
'fatal')
msg="${c_red}Error: ${notification_msg}${c_reset}"
;;
*)
msg=""
;;
esac
case "${notification_format}" in
'n')
options="-ne"
;;
*)
options="-e"
;;
esac
[[ ! -z "${msg}" ]] && echo $options "${msg}"
}
function defunc() { function defunc() {
notify 'fatal' "Please fix your configuration. Exiting..." notify 'fatal' "Please fix your configuration. Exiting..."
exit 1 exit 1
@ -1339,36 +1283,7 @@ function _setup_postfix_relay_hosts() {
fi fi
# end /etc/postfix/sasl_passwd # end /etc/postfix/sasl_passwd
# setup /etc/postfix/relayhost_map populate_relayhost_map
# --
# @domain1.com [smtp.mailgun.org]:587
# @domain2.com [smtp.mailgun.org]:587
# @domain3.com [smtp.mailgun.org]:587
echo -n > /etc/postfix/relayhost_map
if [ -f /tmp/docker-mailserver/postfix-relaymap.cf ]; then
notify 'inf' "Adding relay mappings from postfix-relaymap.cf"
while read line; do
if ! echo "$line" | grep -q -e "\s*#"; then
echo "$line" >> /etc/postfix/relayhost_map
fi
done < /tmp/docker-mailserver/postfix-relaymap.cf
fi
grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-accounts.cf | while IFS=$'|' read login pass
do
domain=$(echo ${login} | cut -d @ -f2)
if ! grep -q -e "^@${domain}\b" /etc/postfix/relayhost_map; then
notify 'inf' "Adding relay mapping for ${domain}"
echo "@${domain} [$RELAY_HOST]:$RELAY_PORT" >> /etc/postfix/relayhost_map
fi
done
# remove lines with no destination
sed -i '/^@\S*\s*$/d' /etc/postfix/relayhost_map
chown root:root /etc/postfix/relayhost_map
chmod 0600 /etc/postfix/relayhost_map
# end /etc/postfix/relayhost_map
postconf -e \ postconf -e \
"smtp_sasl_auth_enable = yes" \ "smtp_sasl_auth_enable = yes" \

View file

@ -9,8 +9,13 @@ function teardown() {
} }
function setup_file() { function setup_file() {
# We use a temporary config directory since we'll be dynamically editing
# it with setup.sh.
tmp_confdir=$(mktemp -d /tmp/docker-mailserver-config-relay-hosts-XXXXX)
cp -aT test/config/relay-hosts "$tmp_confdir"
docker run -d --name mail_with_relays \ docker run -d --name mail_with_relays \
-v "`pwd`/test/config/relay-hosts":/tmp/docker-mailserver \ -v "$tmp_confdir":/tmp/docker-mailserver \
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \ -v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
-e RELAY_HOST=default.relay.com \ -e RELAY_HOST=default.relay.com \
-e RELAY_PORT=2525 \ -e RELAY_PORT=2525 \
@ -25,6 +30,7 @@ function setup_file() {
function teardown_file() { function teardown_file() {
docker rm -f mail_with_relays docker rm -f mail_with_relays
rm -rf "$tmp_confdir"
} }
@test "first" { @test "first" {
@ -32,28 +38,43 @@ function teardown_file() {
} }
@test "checking relay hosts: default mapping is added from env vars" { @test "checking relay hosts: default mapping is added from env vars" {
run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/relayhost_map | grep -e "^@domainone.tld\s\+\[default.relay.com\]:2525" | wc -l | grep 1' run docker exec mail_with_relays grep -e domainone.tld /etc/postfix/relayhost_map
assert_success assert_output -e '^@domainone.tld\s+\[default.relay.com\]:2525$'
}
@test "checking relay hosts: default mapping is added from env vars for new user entry" {
run docker exec mail_with_relays grep -e domainzero.tld /etc/postfix/relayhost_map
assert_output ''
run ./setup.sh -c mail_with_relays email add user0@domainzero.tld password123
for i in {1..10}; do
sleep 1
run docker exec mail_with_relays grep -e domainzero.tld /etc/postfix/relayhost_map
[[ $status == 0 ]] && break
done
assert_output -e '^@domainzero.tld\s+\[default.relay.com\]:2525$'
} }
@test "checking relay hosts: custom mapping is added from file" { @test "checking relay hosts: custom mapping is added from file" {
run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/relayhost_map | grep -e "^@domaintwo.tld\s\+\[other.relay.com\]:587" | wc -l | grep 1' run docker exec mail_with_relays grep -e domaintwo.tld /etc/postfix/relayhost_map
assert_success assert_output -e '^@domaintwo.tld\s+\[other.relay.com\]:587$'
} }
@test "checking relay hosts: ignored domain is not added" { @test "checking relay hosts: ignored domain is not added" {
run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/relayhost_map | grep -e "^@domainthree.tld\s\+\[any.relay.com\]:25" | wc -l | grep 0' run docker exec mail_with_relays grep -e domainthree.tld /etc/postfix/relayhost_map
assert_success assert_failure 1
assert_output ''
} }
@test "checking relay hosts: auth entry is added" { @test "checking relay hosts: auth entry is added" {
run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/sasl_passwd | grep -e "^@domaintwo.tld\s\+smtp_user_2:smtp_password_2" | wc -l | grep 1' run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/sasl_passwd | grep -e "^@domaintwo.tld\s\+smtp_user_2:smtp_password_2" | wc -l'
assert_success assert_success
assert_output 1
} }
@test "checking relay hosts: default auth entry is added" { @test "checking relay hosts: default auth entry is added" {
run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/sasl_passwd | grep -e "^\[default.relay.com\]:2525\s\+smtp_user:smtp_password" | wc -l | grep 1' run docker exec mail_with_relays /bin/sh -c 'cat /etc/postfix/sasl_passwd | grep -e "^\[default.relay.com\]:2525\s\+smtp_user:smtp_password" | wc -l'
assert_success assert_success
assert_output 1
} }
@test "last" { @test "last" {