From cf9eb8278ad56d6a06a7e4370b9a0d7855ce9999 Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Tue, 22 Aug 2023 10:03:41 +0200 Subject: [PATCH] scripts: add wrapper to update Postfix configuration safely (#3484) The new function can 1. update/append 2. update/prepend 3. initialize if non-existent options in `/etc/postfix/main.cf` in a safe and secure manner. When the container is improperly restarted, the option is not applied twice. --- Co-authored-by: Casper Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- target/scripts/helpers/aliases.sh | 7 +--- target/scripts/helpers/postfix.sh | 37 +++++++++++++++++++ target/scripts/helpers/relay.sh | 2 +- target/scripts/helpers/utils.sh | 9 +++++ .../scripts/startup/setup.d/dmarc_dkim_spf.sh | 9 ++--- .../startup/setup.d/security/rspamd.sh | 7 ++-- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/target/scripts/helpers/aliases.sh b/target/scripts/helpers/aliases.sh index 9690aae5..0890d994 100644 --- a/target/scripts/helpers/aliases.sh +++ b/target/scripts/helpers/aliases.sh @@ -30,12 +30,7 @@ function _handle_postfix_regexp_config() { _log 'trace' "Adding regexp alias file postfix-regexp.cf" cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp - - if ! grep 'virtual_alias_maps.*pcre:/etc/postfix/regexp' /etc/postfix/main.cf; then - sed -i -E \ - 's|virtual_alias_maps(.*)|virtual_alias_maps\1 pcre:/etc/postfix/regexp|g' \ - /etc/postfix/main.cf - fi + _add_to_or_update_postfix_main 'virtual_alias_maps' 'pcre:/etc/postfix/regexp' fi } diff --git a/target/scripts/helpers/postfix.sh b/target/scripts/helpers/postfix.sh index 32897d0e..b376dce9 100644 --- a/target/scripts/helpers/postfix.sh +++ b/target/scripts/helpers/postfix.sh @@ -91,3 +91,40 @@ function _vhost_ldap_support() { # # /etc/aliases is handled by `alias.sh` and uses `postalias` to update the Postfix alias database. No need for `postmap`. # http://www.postfix.org/postalias.1.html + +# Add an key with an value to Postfix's main configuration file +# or update an existing key. An already existing key can be updated +# by either appending to the existing value (default) or by prepending. +# +# @param ${1} = key name in Postfix's main configuration file +# @param ${2} = new value (appended or prepended) +# @param ${3} = "append" (default) or "prepend" [OPTIONAL] +function _add_to_or_update_postfix_main() { + local KEY=${1:?Key name is required} + local NEW_VALUE=${2:?New value is required} + local ACTION=${3:-append} + + # If entry does not exist, add it - otherwise update with ACTION: + if ! grep -q -E "^${KEY}" /etc/postfix/main.cf; then + postconf "${KEY} = ${NEW_VALUE}" + else + KEY=$(_escape_for_sed "${KEY}") + NEW_VALUE=$(_escape_for_sed "${NEW_VALUE}") + local SED_STRING + + case "${ACTION}" in + ('append') + SED_STRING="/${NEW_VALUE}/! s|^(${KEY} *=.*)|\1 ${NEW_VALUE}|g" + ;; + ('prepend') + SED_STRING="/${NEW_VALUE}/! s|^(${KEY}) *= *(.*)|\1 = ${NEW_VALUE} \2|g" + ;; + (*) + _log 'error' "Action '${3}' in _add_to_or_update_postfix_main is unknown" + return 1 + ;; + esac + + sed -i -E "${SED_STRING}" /etc/postfix/main.cf + fi +} diff --git a/target/scripts/helpers/relay.sh b/target/scripts/helpers/relay.sh index c2713651..de29dddc 100644 --- a/target/scripts/helpers/relay.sh +++ b/target/scripts/helpers/relay.sh @@ -173,7 +173,7 @@ function _setup_relayhost() { _log 'debug' 'Setting up Postfix Relay Hosts' if [[ -n ${DEFAULT_RELAY_HOST} ]]; then - _log 'trace' "Setting default relay host ${DEFAULT_RELAY_HOST} to /etc/postfix/main.cf" + _log 'trace' "Setting default relay host ${DEFAULT_RELAY_HOST}" postconf "relayhost = ${DEFAULT_RELAY_HOST}" fi diff --git a/target/scripts/helpers/utils.sh b/target/scripts/helpers/utils.sh index 0c5a0321..02d29cc9 100644 --- a/target/scripts/helpers/utils.sh +++ b/target/scripts/helpers/utils.sh @@ -4,6 +4,15 @@ function _escape() { echo "${1//./\\.}" } +# Replaces a string so that it can be used inside +# `sed` safely. +# +# @param ${1} = string to escape +# @output = prints the escaped string +function _escape_for_sed() { + sed -E 's/[]\/$*.^[]/\\&/g' <<< "${1:?String to escape for sed is required}" +} + # Returns input after filtering out lines that are: # empty, white-space, comments (`#` as the first non-whitespace character) function _get_valid_lines_from_file() { diff --git a/target/scripts/startup/setup.d/dmarc_dkim_spf.sh b/target/scripts/startup/setup.d/dmarc_dkim_spf.sh index 85c63d96..c0d731f2 100644 --- a/target/scripts/startup/setup.d/dmarc_dkim_spf.sh +++ b/target/scripts/startup/setup.d/dmarc_dkim_spf.sh @@ -16,10 +16,9 @@ function _setup_opendkim() { _log 'trace' "Adding OpenDKIM to Postfix's milters" postconf 'dkim_milter = inet:localhost:8891' # shellcheck disable=SC2016 - sed -i -E \ - -e '/\$dkim_milter/! s|^(smtpd_milters =.*)|\1 \$dkim_milter|g' \ - -e '/\$dkim_milter/! s|^(non_smtpd_milters =.*)|\1 \$dkim_milter|g' \ - /etc/postfix/main.cf + _add_to_or_update_postfix_main 'smtpd_milters' '$dkim_milter' + # shellcheck disable=SC2016 + _add_to_or_update_postfix_main 'non_smtpd_milters' '$dkim_milter' # check if any keys are available if [[ -e /tmp/docker-mailserver/opendkim/KeyTable ]]; then @@ -64,7 +63,7 @@ function _setup_opendmarc() { postconf 'dmarc_milter = inet:localhost:8893' # Make sure to append the OpenDMARC milter _after_ the OpenDKIM milter! # shellcheck disable=SC2016 - sed -i -E '/\$dmarc_milter/! s|^(smtpd_milters =.*)|\1 \$dmarc_milter|g' /etc/postfix/main.cf + _add_to_or_update_postfix_main 'smtpd_milters' '$dmarc_milter' sed -i \ -e "s|^AuthservID.*$|AuthservID ${HOSTNAME}|g" \ diff --git a/target/scripts/startup/setup.d/security/rspamd.sh b/target/scripts/startup/setup.d/security/rspamd.sh index 4ece646b..d2389a07 100644 --- a/target/scripts/startup/setup.d/security/rspamd.sh +++ b/target/scripts/startup/setup.d/security/rspamd.sh @@ -138,7 +138,7 @@ function __rspamd__setup_postfix() { postconf 'rspamd_milter = inet:localhost:11332' # shellcheck disable=SC2016 - sed -i -E 's|^(smtpd_milters =.*)|\1 \$rspamd_milter|g' /etc/postfix/main.cf + _add_to_or_update_postfix_main 'smtpd_milters' '$rspamd_milter' } # If ClamAV is enabled, we will integrate it into Rspamd. @@ -180,6 +180,7 @@ function __rspamd__setup_default_modules() { metric_exporter ) + local MODULE for MODULE in "${DISABLE_MODULES[@]}"; do __rspamd__helper__enable_disable_module "${MODULE}" 'false' done @@ -289,7 +290,7 @@ function __rspamd__handle_user_modules_adjustments() { # # @param ${1} = file name in ${RSPAMD_OVERRIDE_D}/ # @param ${2} = module name as it should appear in the log - # @patam ${3} = option name in the module + # @param ${3} = option name in the module # @param ${4} = value of the option # # ## Note @@ -333,7 +334,6 @@ function __rspamd__handle_user_modules_adjustments() { while read -r COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3; do case "${COMMAND}" in - ('disable-module') __rspamd__helper__enable_disable_module "${ARGUMENT1}" 'false' 'override' ;; @@ -367,7 +367,6 @@ function __rspamd__handle_user_modules_adjustments() { __rspamd__log 'warn' "Command '${COMMAND}' is invalid" continue ;; - esac done < <(_get_valid_lines_from_file "${RSPAMD_CUSTOM_COMMANDS_FILE}") fi