#!/bin/sh # # /etc/local-COMMON/postfix/postfix.sh # Copyright 2002-2007 Jonas Smedegaard # # $Id: postfix.sh,v 1.76 2008-05-25 19:00:16 jonas Exp $ # # Auto-tweak plain installed postfix Debian package # # TODO: # * Implement stuff from here: http://www.wsrcc.com/spam/ # * Implement stuff from here: http://www.muine.org/~hoang/postfix.html # * Implement stuff from here: http://jimsun.linxnet.com/misc/postfix-anti-UCE.txt # * Figure out a way to use chroot jail for TLS stuff. # * Use https://www.dnswl.org/ set -e # Let's standardize sort export LC_ALL=C warn() { echo >&2 "Warning: $1" } exit1() { echo >&2 "Error: $1" echo >&2 "Exiting..." exit 1 } REDPILL=${REDPILL:-REDPILL} REALM=${REALM:-ORG} TLS_CERT=${TLS_CERT:-/etc/ssl/certs/postfix.pem} TLS_KEY=${TLS_KEY:-/etc/ssl/private/postfix.pem} realmsdir="/etc/local-$REDPILL" thisrealm=$(cat "/etc/local-$REALM/realm" || dnsdomainname | tr '[:lower:]' '[:upper:]') confdir=/etc/postfix if ! [ -d "$realmsdir" ]; then warn "Realms directory \"$realmsdir\" does not exist." fi #postgrey= #if [ -x /usr/sbin/postgrey ]; then # # FIXME: Use this somehow, and only warn below # postgrey=1 #else # exit1 "ERROR: Greylisting support (Debian package postgrey) missing." #fi dovecot= dovecot_lmtp= dovecot_deliver= if [ -x /usr/sbin/dovecot ]; then dovecot=1 if [ -x /usr/lib/dovecot/lmtp ]; then dovecot_lmtp=1 elif [ -x /usr/lib/dovecot/deliver ]; then warn "Dovecot LMTP missing - (Debian package dovecot-lmtp)." dovecot_deliver=1 else warn "Dovecot deliver missing." fi else warn "Dovecot missing - (Debian package dovecot-core or dovecot)." fi tls_cert= if [ -f "$TLS_CERT" ] && [ -f "$TLS_KEY" ]; then tls_cert=1 else warn "No TLS - requires certificate \"$TLS_CERT\" and key \"$TLS_KEY\"." fi # TODO: enable only on systems with user accounts submission=1 # TODO: check that dovecot SASL is configured sasl_inbound= if [ -n "$tls_cert" ] && [ -n "$dovecot" ] && [ -n "$submission" ]; then sasl_inbound=1 else warn "No inbound SASL authentication - requires TLS encryption and Dovecot." fi amavis= # TODO: maybe use AMaViS default of 20 (instead tuning spamassassin) # default_amavis_maxproc=2 if [ -x /usr/sbin/amavisd ] || [ -x /usr/sbin/amavisd-new ]; then amavis=1 else warn "AMaViS missing (Debian package amavisd-new)." fi dkimproxy= #dkimproxy_maxproc_in=5 # FIXME: update actual outgoing maxproc from /etc/default/dkimproxy (and /etc/dkimproxy/dkimproxy_out.conf too?) dkimproxy_maxproc_out=5 if [ -x /usr/sbin/dkimproxy.in ]; then if [ "1" = "$amavis" ]; then dkimproxy=1 else warn "No DKIM/Domainkey - requires DKIMproxy and AMaViS." fi else warn "DKIMproxy missing (Debian package dkimproxy)." fi catfilefromownrealm() { set -e file=$1 [ -d "$realmsdir" ] || exit 0 cat "$realmsdir/$thisrealm/$file" } catallfilesfromotherrealms() { set -e file=$1 [ -d "$realmsdir" ] || exit 0 [ -f "$realmsdir/realms" ] || exit 0 realms=$(sed 's/#.*//' < "$realmsdir/realms") for realm in $realms; do if [ "$thisrealm" != "$realm" ]; then cat "$realmsdir/$realm/$file" fi done } catfirstfile() { set -e file=$1 context=${2:-postfix} configdir= for dir in /etc/local "/etc/local-$REALM" "/etc/local-$REDPILL" /etc/local-COMMON; do if [ -d "$dir/$context" ] && [ -f "$dir/$context/$file" ]; then configdir="$dir/$context" break fi done if [ -z "$configdir" ]; then exit1 "ERROR: file \"$file\" not found." fi cat "$configdir/$file" } # TODO: support quoted string, and trailing comment. getperlvarfromfile() { set -e export var=$1 export default=$2 file=$3 context=${4:-postfix} catfirstfile "$file" "$context" \ | perl -nE'/^\$$ENV{"var"}\h*=\h*(\d+)/ and $s=$1; END {print length($s) ? $s : $ENV{"default"}}' } getlinesfromfile() { set -e param=$1 shift replacements= for subparam in "$@"; do case "$subparam" in *=) oldparam=$(echo "$subparam" | awk -F= '{print $1}') replacements="$replacements;s/,*[^,]*$oldparam[^,]*,*/,/" continue ;; *=*=*) oldparam=$(echo "$subparam" | awk -F= '{print $1}') newparam=$(echo "$subparam" | awk -F= '{print $2}') newparamfile=$(echo "$subparam" | awk -F= '{print $3}') ;; *) oldparam=$subparam newparam=$subparam newparamfile=$subparam ;; esac newparamvalues=$(getlinesfromfile "$newparamfile" | sed -e 's/.*=[ ]*//' -e 's/,/ /g') newstring= for newparamvalue in $newparamvalues; do newstring="${newstring}$newparam $newparamvalue," done replacements="$replacements;s/$oldparam/$newstring/" done echo -n "$param = " catfirstfile "$param" | sed 's/#.*//' | tr '\n' ',' | sed -e 's/^[, ]*//;s/[, ]\+/,/g' -e 's/\^/ /g' -e "s/,\$//$replacements" } _postconf() { postconf -c "$tempdir" "$1" "$(echo "$2" | tr '\n' ' ')" } postmapfiles= tempdir=$(mktemp -td postfix.XXXXXX) cp -a -t "$tempdir" "$confdir"/* # Inspired by D. J. Bernstein: http://cr.yp.to/smtp/greeting.html _postconf -e smtpd_banner="\$myhostname NO UCE ESMTP \$mail_name (Debian/GNU)" # Some badly configured setup use hostname instead of FQDN # Disable completely: Effective, but hurts executive type guys using windows servers... :-( #if _postconf myhostname | grep -q '\.'; then # _postconf -e smtpd_helo_required=yes #fi _postconf -e smtpd_helo_required=no _postconf -e "$(getlinesfromfile permit_mx_backup_networks)" _postconf -e maps_rbl_domains= _postconf -e "$(getlinesfromfile smtpd_client_restrictions reject_rhsbl_client)" _postconf -e "$(getlinesfromfile smtpd_helo_restrictions)" _postconf -e "$(getlinesfromfile smtpd_sender_restrictions reject_rhsbl_sender ${dkimproxy:-sender_access_regex=})" _postconf -e "$(getlinesfromfile smtpd_recipient_restrictions reject_maps_rbl=reject_rbl_client=maps_rbl_domains)" _postconf -e "$(getlinesfromfile smtpd_data_restrictions)" # FIXME: clear only specific line (not whole file) when dkimproxy unused if [ -f "$tempdir/sender_access_regex" ]; then if [ -n "$dkimproxy" ]; then grep -q -F '/^/ FILTER dkimsign:[127.0.0.1]:10026' "$tempdir/sender_access_regex" \ || echo '/^/ FILTER dkimsign:[127.0.0.1]:10026' >> "$tempdir/sender_access_regex" else if grep -q -F '/^/ FILTER dkimsign:[127.0.0.1]:10026' "$tempdir/sender_access_regex"; then echo "echo '' > \"$confdir/sender_access_regex\"" >> "$tempdir/COMMANDS" rm "$tempdir/sender_access_regex" fi fi postmapfiles="$postmapfiles sender_access_regex" fi # Support exceptions to default response # (Day Old Bread (dob) lists need to reject only temporarily) _postconf -e rbl_reply_maps="hash:$confdir/rbl_reply_map" sed 's/#.*//' \ < /etc/local-COMMON/postfix/rbl_reply_map \ > "$tempdir/rbl_reply_map" postmapfiles="$postmapfiles rbl_reply_map" # Verify senders of common suspicious and known verifiable domains # (exclude verification of postmaster@ to not verify verification probes) # (add own domains before peers for (rare) cases of duplicates) # FIXME: somehow do this step only if enabled in smtpd_sender_restrictions # TODO: Properly implement exception exclusion like yahoo (which does not want to be checked any longer!) grep -v yahoo \ < /etc/local-COMMON/postfix/maildomains \ | sort \ | sed 's/$/ reject_unverified_sender/' \ > "$tempdir/sender_access" ( catfilefromownrealm maildomains | sort; catallfilesfromotherrealms maildomains | sort ) \ | sed 's/\(.*\)$/postmaster@\1 permit\n\1 reject_unverified_sender/' \ >> "$tempdir/sender_access" [ ! -f "$tempdir/sender_access.addon" ] || cat "$tempdir/sender_access.addon" \ >> "$tempdir/sender_access" postmapfiles="$postmapfiles sender_access" _postconf -e unverified_sender_reject_code=550 # Trust recipient verification too _postconf -e unverified_recipient_reject_code=550 if [ -n "$dovecot_lmtp" ]; then _postconf -e mailbox_transport=lmtp:unix:private/dovecot-lmtp _postconf -X mailbox_command elif [ -n "$dovecot_deliver" ]; then _postconf -X mailbox_transport _postconf -e mailbox_command=/usr/lib/dovecot/deliver else _postconf -X mailbox_transport _postconf -X mailbox_command fi # outbound opportunistic encryption _postconf -e smtp_tls_security_level=may if [ -n "$tls_cert" ]; then _postconf -e smtp_tls_cert_file="$TLS_CERT" _postconf -e smtp_tls_key_file="$TLS_KEY" else _postconf -X smtp_tls_cert_file _postconf -X smtp_tls_key_file fi _postconf -e smtp_tls_loglevel=1 # enforce TLS trust path towards peers catallfilesfromotherrealms mailhost | sort \ | sed 's/^/[/;s/$/]:submission secure/' \ > "$tempdir/tls_policy" [ ! -f "$tempdir/tls_policy.addon" ] || cat "$tempdir/tls_policy.addon" \ >> "$tempdir/tls_policy" postmapfiles="$postmapfiles tls_policy" _postconf -e smtp_tls_policy_maps="hash:$confdir/tls_policy" # inbound opportunistic encryption if [ -n "$tls_cert" ]; then _postconf -e smtpd_tls_security_level=may _postconf -e smtpd_tls_cert_file="$TLS_CERT" _postconf -e smtpd_tls_key_file="$TLS_KEY" _postconf -e smtpd_tls_loglevel=1 _postconf -e smtpd_tls_auth_only=yes _postconf -e smtpd_tls_received_header=yes else _postconf -e smtpd_tls_security_level=none _postconf -X smtpd_tls_cert_file _postconf -X smtpd_tls_key_file _postconf -X smtpd_tls_loglevel _postconf -X smtpd_tls_auth_only _postconf -X smtpd_tls_received_header fi # Avoid smtpd_tls_CApath or smtpd_tls_CAfile to trick outlook.com # See _postconf -X smtpd_tls_CApath _postconf -X smtpd_tls_CAfile # obsolete TLS-related settings _postconf -X smtpd_use_tls _postconf -X smtp_tls_CApath _postconf -X lmtp_tls_CApath _postconf -X smtp_tls_CAfile _postconf -X lmtp_tls_CAfile _postconf -X smtpd_tls_ask_ccert _postconf -X smtp_tls_note_starttls_offer _postconf -X smtpd_tls_session_cache_database _postconf -X smtpd_tls_session_cache_timeout _postconf -X smtp_tls_session_cache_database _postconf -X tls_random_exchange_name _postconf -X tls_random_source # submission # # if [ -n "$submission" ]; then _postconf -Me submission/inet=" submission inet n - y - - smtpd -o syslog_name=postfix/\$service_name -o smtpd_tls_security_level=encrypt " _postconf -Me smtps/inet=" smtps inet n - y - - smtpd -o syslog_name=postfix/\$service_name -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes " if [ -n "$sasl_inbound" ]; then _postconf -Pe submission/inet/smtpd_sasl_auth_enable=yes _postconf -Pe smtps/inet/smtpd_sasl_auth_enable=yes else _postconf -PX submission/inet/smtpd_sasl_auth_enable _postconf -PX smtps/inet/smtpd_sasl_auth_enable fi else _postconf -MX submission/inet _postconf -MX smtps/inet fi if [ -n "$sasl_inbound" ]; then _postconf -e smtpd_sasl_local_domain=\$mydomain _postconf -e smtpd_sasl_type=dovecot _postconf -e smtpd_sasl_path=private/auth _postconf -e broken_sasl_auth_clients=yes else _postconf -X smtpd_sasl_local_domain _postconf -X smtpd_sasl_type _postconf -X smtpd_sasl_path _postconf -X broken_sasl_auth_clients fi # obsolete SASL-related settings _postconf -X smtpd_sasl_auth_enable _postconf -X smtpd_sasl_security_options if [ -n "$amavis" ]; then amavis_maxproc=$(getperlvarfromfile max_servers "$default_amavis_maxproc" amavisd.conf.addon amavis) _postconf -Me amavisfeed/unix=" amavisfeed unix - - n - $amavis_maxproc lmtp -o lmtp_data_done_timeout=1200s -o lmtp_send_xforward_command=yes -o disable_dns_lookups=yes -o max_use=$amavis_maxproc " _postconf -Me 127.0.0.1:10025/inet=' 127.0.0.1:10025 inet n - n - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_delay_reject=no -o smtpd_client_restrictions=permit_mynetworks,reject -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions=reject_unauth_pipelining -o smtpd_end_of_data_restrictions= -o mynetworks=127.0.0.0/8 -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters ' _postconf -e receive_override_options=no_address_mappings if [ -n "$dkimproxy" ]; then _postconf -e content_filter='amavisfeed:[127.0.0.1]:10028' _postconf -Pe pickup/fifo/content_filter='dkimsign:127.0.0.1:10028' if [ -n "$submission" ]; then _postconf -Pe submission/inet/content_filter='dkimsign:[127.0.0.1]:10028' _postconf -Pe smtps/inet/content_filter='dkimsign:[127.0.0.1]:10028' fi _postconf -Me dkimsign/unix=" dkimsign unix - - n - $dkimproxy_maxproc_out smtp -o smtp_send_xforward_command=yes -o smtp_discard_ehlo_keywords=8bitmime,starttls " _postconf -Me 127.0.0.1:10029/inet=' 127.0.0.1:10029 inet n - n - - smtpd -o content_filter= -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters -o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o smtpd_authorized_xforward_hosts=127.0.0.0/8 ' else _postconf -MX dkimsign/unix _postconf -MX 127.0.0.1:10029/inet _postconf -e content_filter='amavisfeed:[127.0.0.1]:10024' _postconf -PX pickup/fifo/content_filter if [ -n "$submission" ]; then _postconf -PX submission/inet/content_filter _postconf -PX smtps/inet/content_filter fi fi else _postconf -MX amavisfeed/unix _postconf -MX 127.0.0.1:10025/inet _postconf -X content_filter _postconf -X receive_override_options fi # obsolete settings _postconf -X max_use _postconf -MX smtp-amavis/unix diff -ruNw "$confdir" "$tempdir" || if [ $? -gt 1 ]; then exit $?; else needs_reload=1; fi if [ "$force" = "1" ]; then do_update=y elif [ "1" = "$needs_reload" ]; then echo -n "Above is the intended changes. OK to update (y/N)? " read do_update fi case $do_update in y|Y) if [ -f "$tempdir/COMMANDS" ]; then sh -s < "$tempdir/COMMANDS" fi rm -f "$tempdir/COMMANDS" diff -q "$confdir/master.cf" "$tempdir/master.cf" || if [ $? -gt 1 ]; then exit $?; else needs_restart=1; fi cp -a -f -t "$confdir" "$tempdir"/* rm -rf "$tempdir" for file in $postmapfiles; do postmap "$confdir/$file" done if [ "1" = "$needs_restart" ]; then service postfix restart else service postfix force-reload fi if [ "1" = "$needs_reload" ]; then echo >&2 "Changes applied!" fi ;; *) if [ "1" = "$needs_reload" ]; then exit1 "Aborted!" fi ;; esac if [ "1" != "$needs_reload" ]; then echo >&2 "No changes needed!" fi # Based on this: http://jimsun.linxnet.com/misc/postfix-anti-UCE.txt # Support for trusted MX backup networks added # PCRE stuff avoided, as PCRE is only optional on newest Debian packages # RBLs replaced with those recommended by http://www.antispews.org/ # spam filter based on these: http://www.postfix.org/FILTER_README.html # https://www.ijs.si/software/amavisd/amavisd-new-docs.html # TLS based on this: http://www.postfix.org/TLS_README.html # Here's a convenient overview of different blackholes: # http://rbls.org/