postfwd: install config from files/postfwd, idempotent /etc/default/postfwd handling
- Ship postfwd.cf and list files in files/postfwd; used by install_postfwd.sh and install_postfix_advanced.sh (lists only if missing, cf with backup). - Add update_postfwd_defaults.sh. - install_amavis.sh: disable smtpd_end_of_data_restrictions on localhost:10025. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+147
-424
@@ -1186,444 +1186,167 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
echononl " Adjust /etc/default/postfwd"
|
||||
perl -i -n -p -e "s#^(\s*)(STARTUP=.*)#\#\1\2\nSTARTUP=1#" \
|
||||
/etc/default/postfwd > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
fi
|
||||
## - Adjust /etc/default/postfwd
|
||||
## -
|
||||
## - set_postfwd_default <VAR> <VALUE>
|
||||
## -
|
||||
## - - VAR is set (uncommented) and has the wanted value -> nothing to do
|
||||
## - - VAR is set with a different value -> comment out the current
|
||||
## - setting and add the new setting directly below it
|
||||
## - - VAR is not set at all -> append the new setting
|
||||
## -
|
||||
## - Quotes around the value are ignored when comparing ("1" == 1).
|
||||
## -
|
||||
postfwd_default_file="/etc/default/postfwd"
|
||||
|
||||
_file="/etc/postfix/postfwd.wl-nets"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# Trusted networks whitelisted by postfwd
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # web0.warenform.de
|
||||
# #83.223.86.76
|
||||
# #2a01:30:0:505:286:96ff:fe4a:6ee
|
||||
# #2a01:30:0:13:286:96ff:fe4a:6eee
|
||||
#
|
||||
# ---
|
||||
set_postfwd_default() {
|
||||
local _var="$1"
|
||||
local _val="$2"
|
||||
local _tmp_file
|
||||
|
||||
# give truested networrk adresses here
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
echononl " ${_var}=${_val}"
|
||||
|
||||
_file="/etc/postfix/postfwd.wl-hosts"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# Trusted hosts whitelisted by postfwd
|
||||
#
|
||||
# This file is called with '=~'. This means perl regexp is possible
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # all hosts of domain 'oopen.de'
|
||||
# \.oopen\.de$
|
||||
#
|
||||
# # host a.mx.oopen.de
|
||||
# a.mx.oopen.de
|
||||
#
|
||||
# ---
|
||||
if [[ ! -f "$postfwd_default_file" ]] ; then
|
||||
touch "$postfwd_default_file" > "$log_file" 2>&1 || { echo_failed ; error "$(cat $log_file)" ; return 1 ; }
|
||||
fi
|
||||
|
||||
# give truested hostnames here
|
||||
_tmp_file="$(mktemp)"
|
||||
awk -v var="$_var" -v val="$_val" '
|
||||
function unq(s) {
|
||||
gsub(/^[ \t]+|[ \t]+$/, "", s)
|
||||
if (s ~ /^".*"$/ || s ~ /^\047.*\047$/) s = substr(s, 2, length(s) - 2)
|
||||
return s
|
||||
}
|
||||
$0 ~ "^[ \t]*" var "=" {
|
||||
cur = $0 ; sub("^[ \t]*" var "=", "", cur)
|
||||
if (! done && unq(cur) == unq(val)) { print ; done = 1 ; next }
|
||||
print "#" $0
|
||||
if (! done) { print var "=" val ; done = 1 }
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
END { if (! done) print var "=" val }
|
||||
' "$postfwd_default_file" > "$_tmp_file" 2> "$log_file"
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.wl-user"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# SASL Users whitelisted by postfwd
|
||||
# ---
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.wl-sender"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# Trusted senders whitelisted by postfwd
|
||||
#
|
||||
# This file is called with '=~'. This means perl regexp is possible
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # all senders of maildomaindomain 'oopen.de'
|
||||
# @oopen\.de$
|
||||
#
|
||||
# # sender address ckubu@oopen.de
|
||||
# ckubu@oopen.de
|
||||
#
|
||||
# ---
|
||||
|
||||
# give trusted sender addresses here
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.bl-nets"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# Networks blocked by postfwd
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # web0.warenform.de
|
||||
# #83.223.86.76
|
||||
# #2a01:30:0:505:286:96ff:fe4a:6ee
|
||||
# #2a01:30:0:13:286:96ff:fe4a:6eee
|
||||
#
|
||||
# ---
|
||||
|
||||
# give networks to block here
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.bl-hosts"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# hosts blocked by postfwd
|
||||
#
|
||||
# This file is called with '=~'. This means perl regexp is possible
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # block all hosts of domain 'oopen.de'
|
||||
# \.oopen\.de$
|
||||
#
|
||||
# # block host a.mx.oopen.de
|
||||
# a.mx.oopen.de
|
||||
#
|
||||
# ---
|
||||
|
||||
# give hostnames to blocke here
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.bl-user"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# SASL Users blocked by postfwd
|
||||
# ---
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.bl-sender"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# Sender addresses blocked by postfwd
|
||||
# ---
|
||||
|
||||
# annoying spammer domains
|
||||
@acieu.co.uk
|
||||
|
||||
# annoying spammer addresses
|
||||
error@mailfrom.com
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
_file="/etc/postfix/postfwd.bl-sender"
|
||||
echononl " Create whitelist file '$_file' for postfwd"
|
||||
if [[ ! -f "$_file" ]]; then
|
||||
cat << EOF > "$_file"
|
||||
# ---
|
||||
# Sender addresses blocked by postfwd
|
||||
#
|
||||
# This file is called with '=~'. This means perl regexp is possible
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # all senders of maildomaindomain 'oopen.de'
|
||||
# @oopen\.de$
|
||||
#
|
||||
# # sender address ckubu@oopen.de
|
||||
# ckubu@oopen.de
|
||||
#
|
||||
# ---
|
||||
|
||||
# annoying spammer domains
|
||||
@acieu.co.uk
|
||||
|
||||
# annoying spammer addresses
|
||||
error@mailfrom.com
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
echononl " Create configuration file '/etc/postfix/postfwd.cf'.."
|
||||
if [[ ! -f "/etc/postfix/postfwd.cf" ]]; then
|
||||
cat << EOF > /etc/postfix/postfwd.cf
|
||||
|
||||
#======= Definitions ============
|
||||
|
||||
# Match messages with an associated SASL username
|
||||
&&SASL_AUTH {
|
||||
sasl_username!~^\$
|
||||
if [[ $? -ne 0 ]] ; then
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
elif cmp -s "$_tmp_file" "$postfwd_default_file" ; then
|
||||
echo_skipped
|
||||
else
|
||||
# - cat instead of mv/cp keeps owner and permissions of the original file
|
||||
cat "$_tmp_file" > "$postfwd_default_file" 2> "$log_file"
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
fi
|
||||
fi
|
||||
rm -f "$_tmp_file"
|
||||
}
|
||||
|
||||
# Trusted networks
|
||||
&&TRUSTED_NETS {
|
||||
client_address==file:/etc/postfix/postfwd.wl-nets
|
||||
}
|
||||
|
||||
# Trusted hostnames
|
||||
# client_name~=.warenform.de$
|
||||
&&TRUSTED_HOSTS {
|
||||
client_name=~file:/etc/postfix/postfwd.wl-hosts
|
||||
}
|
||||
|
||||
# Trusted users
|
||||
&&TRUSTED_USERS {
|
||||
sasl_username==file:/etc/postfix/postfwd.wl-user
|
||||
}
|
||||
|
||||
# Trusted senders
|
||||
&&TRUSTED_SENDERS {
|
||||
sender=~file:/etc/postfix/postfwd.wl-sender
|
||||
}
|
||||
|
||||
# Blacklist networks
|
||||
&&BLOCK_NETS {
|
||||
client_address==file:/etc/postfix/postfwd.bl-nets
|
||||
}
|
||||
|
||||
# Blacklist hostnames
|
||||
&&BLOCK_HOSTS {
|
||||
client_name=~file:/etc/postfix/postfwd.bl-hosts
|
||||
}
|
||||
|
||||
# Blacklist users
|
||||
&&BLOCK_USERS {
|
||||
sasl_username==file:/etc/postfix/postfwd.bl-user
|
||||
}
|
||||
|
||||
# Blacklist sender adresses
|
||||
&&BLOCK_SENDER {
|
||||
# =~
|
||||
# using '=~' allows also matching entries for domains (i.e. @acieu.co.uk)
|
||||
sender=~file:/etc/postfix/postfwd.bl-sender
|
||||
}
|
||||
|
||||
# Inbound emails only
|
||||
&&INCOMING {
|
||||
client_address!=127.0.0.1
|
||||
}
|
||||
echo ""
|
||||
echo " Adjust ${postfwd_default_file}"
|
||||
set_postfwd_default STARTUP "1"
|
||||
set_postfwd_default CONF "/etc/postfix/postfwd.cf"
|
||||
set_postfwd_default INET "127.0.0.1"
|
||||
set_postfwd_default PORT "10040"
|
||||
set_postfwd_default ARGS '"--summary=600 --cache=0"'
|
||||
echo ""
|
||||
|
||||
|
||||
#======= Rule Sets ============
|
||||
## - Install postfwd configuration and list files shipped with this
|
||||
## - repository (directory 'files/postfwd').
|
||||
## -
|
||||
## - - postfwd.cf: installed; an existing, differing file is saved
|
||||
## - as postfwd.cf.<backup_date> first.
|
||||
## - - list files (whitelists, blacklists, high-rate exceptions):
|
||||
## - only installed if not yet present - existing lists contain
|
||||
## - local data and are never overwritten.
|
||||
## -
|
||||
postfwd_src_dir="${working_dir}/files/postfwd"
|
||||
postfwd_conf_dir="/etc/postfix"
|
||||
|
||||
# ---
|
||||
#
|
||||
# Processing of the Rule Sets
|
||||
#
|
||||
# The parser checks the elements of a policy delegation request against the postfwd set
|
||||
# of rules and, if necessary, triggers the configured action (action=). Similar to a
|
||||
# classic firewall, a rule is considered true if every element of the set of rules (or
|
||||
# one from every element list) applies to the comparison. I.e. the following rule:
|
||||
#
|
||||
# client_address=1.1.1.1, 1.1.1.2; client_name==unknown; action=REJECT
|
||||
#
|
||||
# triggers a REJECT if the
|
||||
#
|
||||
# Client address is equal (1.1.1.1 OR 1.1.1.2) AND the client name 'unknown'
|
||||
#
|
||||
#
|
||||
# Note:
|
||||
# If an element occurs more than once, an element list is formed:
|
||||
#
|
||||
# The following rule set is equivalent to the above:
|
||||
#
|
||||
# client_address=1.1.1.1; client_address=1.1.1.2; client_name==unknown; action=REJECT
|
||||
#
|
||||
#
|
||||
# triggers a REJECT if (as above) the
|
||||
#
|
||||
# Client address (1.1.1.1 OR 1.1.1.2) AND the client name 'unknown'
|
||||
#
|
||||
# ---
|
||||
postfwd_list_files="
|
||||
postfwd.wl-nets
|
||||
postfwd.wl-hosts
|
||||
postfwd.wl-user
|
||||
postfwd.wl-sender
|
||||
postfwd.bl-nets
|
||||
postfwd.bl-hosts
|
||||
postfwd.bl-user
|
||||
postfwd.bl-sender
|
||||
postfwd.bl-recipient-exeeds-msg-size-20mb
|
||||
postfwd.high_rate_ip
|
||||
postfwd.high_rate_hostname
|
||||
postfwd.high-rate-number-messages
|
||||
postfwd.high-rate-number-recipients
|
||||
"
|
||||
|
||||
# Whitelists
|
||||
|
||||
# Whitelist trusted networks
|
||||
id=WHL_NETS
|
||||
&&TRUSTED_NETS
|
||||
action=DUNNO
|
||||
|
||||
# Whitelist trusted hostnames
|
||||
id=WHL_HOSTS
|
||||
&&TRUSTED_HOSTS
|
||||
action=DUNNO
|
||||
|
||||
# Whitelist sasl users
|
||||
id=WHL_USERS
|
||||
&&TRUSTED_USERS
|
||||
action=DUNNO
|
||||
|
||||
# Whitelist senders
|
||||
id=WHL_SENDERS
|
||||
&&INCOMING
|
||||
&&TRUSTED_SENDERS
|
||||
action=DUNNO
|
||||
|
||||
|
||||
# Blacklists
|
||||
|
||||
# Block networks
|
||||
id=BL_NETS
|
||||
&&BLOCK_NETS
|
||||
action=REJECT Network Address \$\$client_address blocked by Mailserver admins. Error: BL_NETS
|
||||
|
||||
# Block hostname
|
||||
id=BL_HOSTS
|
||||
&&BLOCK_HOSTS
|
||||
action=REJECT \$\$client_name blocked by Mailserver admins. Error: BL_HOSTS
|
||||
|
||||
# Block users
|
||||
id=BL_USERS
|
||||
&&BLOCK_USERS
|
||||
action=REJECT User is blocked by Mailserver admins. Error: BL_USERS
|
||||
|
||||
# Blacklist sender
|
||||
#
|
||||
# Claim successful delivery and silently discard the message.
|
||||
#
|
||||
id=BL_SENDER
|
||||
&&BLOCK_SENDER
|
||||
#action=DISCARD
|
||||
action=REJECT Sender address is blocked by Mailserver admins. Error: BL_SENDER
|
||||
|
||||
|
||||
# Rate Limits
|
||||
|
||||
# Throttle unknown clients to 5 recipients per 5 minutes:
|
||||
id=RATE_UNKNOWN_CLIENT_ADDR
|
||||
sasl_username =~ /^$/
|
||||
client_name==unknown
|
||||
action=rate(client_address/5/300/450 4.7.1 only 5 recipients per 5 minutes allowed)
|
||||
|
||||
# Block clients (ip-addresses) sending more than 50 messages per minute exceeded. Error:RATE_CLIENT)
|
||||
id=RATE_CLIENT_ADDR
|
||||
&&INCOMING
|
||||
action=rate(\$\$client_address/50/60/421 421 4.7.0 Too many connections from \$\$client_address)
|
||||
|
||||
# Block messages with more than 50 recipients
|
||||
id=BLOCK_MSG_RCPT
|
||||
&&INCOMING
|
||||
&&SASL_AUTH
|
||||
recipient_count=50
|
||||
action=REJECT Too many recipients, please reduce to less than 50 or consider using a mailing list. Error: BLOCK_MSG_RCPT
|
||||
|
||||
# Block users sending more than 50 messages/hour
|
||||
id=RATE_MSG
|
||||
&&INCOMING
|
||||
&&SASL_AUTH
|
||||
action=rate(\$\$sasl_username/50/3600/450 4.7.1 Number messages per hour exceeded. Error:RATE_MSG)
|
||||
|
||||
# Block users sending more than 250 recipients total/hour
|
||||
id=RATE_RCPT
|
||||
&&INCOMING
|
||||
&&SASL_AUTH
|
||||
action=rcpt(\$\$sasl_username/250/3600/450 4.7.1 Number recipients per hour exceeded. Error:RATE_RCPT)
|
||||
|
||||
EOF
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fi
|
||||
if [[ ! -d "$postfwd_src_dir" ]] || [[ ! -f "${postfwd_src_dir}/postfwd.cf" ]] ; then
|
||||
error "Directory '${postfwd_src_dir}' (or postfwd.cf in it) not found - postfwd configuration NOT installed!"
|
||||
else
|
||||
echo_skipped
|
||||
|
||||
for _file in $postfwd_list_files ; do
|
||||
echononl " Install list file '${postfwd_conf_dir}/${_file}'"
|
||||
if [[ ! -f "${postfwd_src_dir}/${_file}" ]] ; then
|
||||
echo_failed
|
||||
error "Source file '${postfwd_src_dir}/${_file}' not found."
|
||||
elif [[ -f "${postfwd_conf_dir}/${_file}" ]] ; then
|
||||
echo_skipped
|
||||
else
|
||||
install -m 0644 -o root -g root "${postfwd_src_dir}/${_file}" \
|
||||
"${postfwd_conf_dir}/${_file}" > "$log_file" 2>&1
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
_file="${postfwd_conf_dir}/postfwd.cf"
|
||||
if [[ -f "$_file" ]] && ! cmp -s "${postfwd_src_dir}/postfwd.cf" "$_file" ; then
|
||||
echononl " Backup existing '${_file}'"
|
||||
cp -a "$_file" "${_file}.${backup_date}" > "$log_file" 2>&1
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echononl " Install configuration file '${_file}'"
|
||||
if [[ -f "$_file" ]] && cmp -s "${postfwd_src_dir}/postfwd.cf" "$_file" ; then
|
||||
echo_skipped
|
||||
else
|
||||
install -m 0644 -o root -g root "${postfwd_src_dir}/postfwd.cf" "$_file" > "$log_file" 2>&1
|
||||
if [[ $? -eq 0 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# - Check configuration: postfwd reports missing list files, undefined
|
||||
# - macros or invalid lines only as warnings and keeps running.
|
||||
echononl " Check postfwd configuration"
|
||||
_postfwd_check="$(postfwd --showconfig --file "$_file" 2>&1 \
|
||||
| grep -E '^\[LOG (warning|err|crit)')"
|
||||
if [[ -z "$_postfwd_check" ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_warning
|
||||
warn "postfwd reports problems with '${_file}':\n\n${_postfwd_check}"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
echononl " Enable Postfix firewall daemon at boot time .."
|
||||
if $systemd_exists ; then
|
||||
systemctl enable postfwd > /dev/null 2> $log_file
|
||||
|
||||
Reference in New Issue
Block a user