Hardened Anti-Spam Postfix Setup

WARNING

always check the configuration manual for accuracy of the features. those merely depend on the exact Postfix version you are using. We are talking Postfix 3.1.0, 3.1.4, 3.3.0 here.

postconf -d | grep version
man 5 postconf

if you are unsure about some setting, you can evaluate it even on production with

        warn_if_reject,

DEFAULTS

vi /etc/postfix/main.cf

we are starting with those upstream/netbsd defaults

compatibility_level = 2
smtputf8_enable=no
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
data_directory = /var/db/postfix
mail_owner = postfix
inet_protocols = all
unknown_local_recipient_reject_code = 550
debug_peer_level = 2
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/sbin/sendmail
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = maildrop
html_directory = /usr/share/doc/html/postfix
manpage_directory = /usr/share/man
sample_directory = /usr/share/examples/postfix
readme_directory = /usr/share/examples/postfix

and we are fixing those

#html_directory = /usr/share/doc/html/postfix
html_directory = no
sample_directory = no
readme_directory = no
shlib_directory = no
#meta_directory =

MAIN CONFIG

#defaults to myhostname
myorigin = nethence.com
#defaults to FQDN minus the first component
mydomain = nethence.com
myhostname = mx.nethence.com
mydestination = nethence.com
#PLUS DOCKER / DUMMY / INTERNAL
mynetworks = 127.0.0.0/8
#[::ffff:127.0.0.0]/104 [::1]/128

CASUAL

we are now adding casual settings, nothing fancy so far

message_size_limit = 30720000
#home_mailbox = Maildir/
#defaults to $myhostname ESMTP $mail_name
smtpd_banner = $myhostname ESMTP
biff = no
append_dot_mydomain = no
strict_rfc821_envelopes = yes
disable_vrfy_command = yes
smtpd_delay_reject = yes

NETWORK

        #check_policy_service unix:private/policy,
smtpd_client_restrictions = permit_mynetworks,
        reject_unknown_reverse_client_hostname,
        reject_unknown_client_hostname,
        check_client_access hash:/etc/postfix/access.network,
        reject_rbl_client bl.spamcop.net,
        reject_rbl_client cbl.abuseat.org,
        reject_rbl_client b.barracudacentral.org,
        reject_rbl_client zen.spamhaus.org,
        reject_unauth_pipelining

# this is too restrictive
#       reject_rhsbl_sender dsn.rfc-clueless.org,

# apews is blocking bsd.nethence.com (online.net)
#       reject_rbl_client l2.apews.org,

# sarbl.org not found
#       reject_rbl_client public.sarbl.org,

#deprecated: policy_time_limit = 3600
smtpd_policy_service_request_limit = 1
#http://www.postfix.org/SMTPD_POLICY_README.html

#reject_unknown_client_hostname --> unknown_client_reject_code
#reject_unknown_reverse_client_hostname --> unknown_client_reject_code
unknown_client_reject_code   = 554

#smtpd_client_port_logging = yes

EHLO

smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks,
        reject_invalid_helo_hostname,
        reject_non_fqdn_helo_hostname,
        reject_unknown_helo_hostname,
        regexp:/etc/postfix/access.helo.regexp

#reject_unknown_helo_hostname --> unknown_hostname_reject_code
unknown_hostname_reject_code = 554

MAIL FROM

smtpd_sender_restrictions = permit_mynetworks,
        check_sender_access hash:/etc/postfix/access.sender,
        reject_non_fqdn_sender,
        reject_unknown_sender_domain

RCPT TO

smtpd_recipient_restrictions = permit_mynetworks,
        reject_non_fqdn_recipient,
        reject_unknown_recipient_domain

#reject_unknown_sender_domain --> unknown_address_reject_code
#reject_unknown_recipient_domain --> unknown_address_reject_code
unknown_address_reject_code  = 554

DATA

#client speaks too early
smtpd_data_restrictions = reject_unauth_pipelining

#smtpd_relay_restrictions #defaults to permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

smtpd_error_sleep_time = 1s
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 20

SUPPORTED FILES

NETWORK

cat > /etc/postfix/access.network <<-EOF
compute.amazonaws.com   REJECT too much SPAM from compute.amazonaws.com
.compute.amazonaws.com  REJECT too much SPAM from compute.amazonaws.com
EOF
postmap /etc/postfix/access.network

EHLO

cat > /etc/postfix/access.helo.regexp <<-EOF
/^malabar\.nethence\.com$/     550 you are not me
/^mx\.nethence\.com$/          550 you are not me
/^xc\.nethence\.com$/          550 you are not me
/^nethence\.com$/              550 you are not me
EOF
cat /etc/postfix/access.helo.regexp 

MAIL FROM

cat > /etc/postfix/access.sender <<-EOF
#securityfocus.com       OK
online.net              OK
ovh.com                 OK
EOF
postmap /etc/postfix/access.sender

ADDITIONAL NOTES

unverified_sender and its false-positives

we did NOT enable the unverified_sender feature as it is too restrictive – this prevents unreal addresses to send you messages. try to book a hotel or a flight with that and you will feel the pain. now in case you DO enable it,

draft

        reject_unverified_sender

unverified_sender_reject_code = 550
unverified_sender_reject_reason = Address verification failed
address_verify_map = proxy:btree:$data_directory/verify_cache
address_verify_cache_cleanup_interval = 72h

#unverified_sender_defer_code = 250 #Postfix 2.6 and later
#proxy_write_maps = $smtp_sasl_auth_cache_name $lmtp_sasl_auth_cache_name $address_verify_map $postscreen_cache_map

you will then need to deal with false-positives on the domains that do not pass through

vi /etc/postfix/client_access

securityfocus.com       OK

postmap /etc/postfix/sender_access

TROUBLESHOOTING

while starting Postfix

postfix/postfix-script: warning: group or other writable: /usr/lib/postfix/./sbin/lmtp
...

==> don’t give a fuck about it, it’s complaining about symlinks' rights which point to files with OKAY permissions.

in the logs

close database /var/lib/postfix/verify_cache.db: No such file or directory (possible Berkeley DB bug)

==> use proxy: in the address_verify_map statement as shown above. http://www.postfix.org/ADDRESS_VERIFICATION_README.html

you are triggering reject_unknown_client_hostname or reject_unknown_reverse_client_hostname while you’ve just updated client’s DNS records?

54 5.7.25 Client host rejected: cannot find your hostname, [x.x.x.x]

==> if something fails, it won’t re-check again for a certain time – restart postfix or alter address_verify_negative_cache (default: yes)

RESOURCES

Anti-SPAM & RFC Compliance


Nethence | Pub | Lab | Pbraun | SNE Russia | xhtml