SETTING UP AND HARDENING POSTFIX

INSTALL

see INSTALLING POSTFIX

SYSTEM-WIDE SETUP

give a name to your root account (something that helps identify the system)

vipw

root@HOST

proceed with system-wide mail setup

# BSD and sendmail
cp -pi /etc/mail/aliases /etc/mail/aliases.dist
vi /etc/mail/aliases

# postfix
cp -pi /etc/aliases /etc/aliases.dist
vi /etc/aliases

we don’t want redirections here, for once, as we’re actually hosting the messages locally. note the abuse mail alias SHOULD be defined. eventually get rid of the root alias so you read the bounces locally e.g. w/ Alpine and w/o mixup those up with other messages.

postmaster:     root
abuse:          root
www:            root
webmaster:      root

root:           MAILUSER
USER:           MAILUSER

and apply

newaliases
#postalias /etc/mail/aliases
#postalias /etc/aliases

START CLEAN

cd /etc/postfix/
diff -bu main.cf.proto main.cf | less -r # differs
diff -bu master.cf.proto master.cf # identical

mv -i main.cf main.cf.dist
grep -vE '^[[:space:]]*(#|$)' main.cf.dist > main.cf.clean
grep -vE '^[[:space:]]*(#|$)' main.cf.dist > main.cf

mv -i master.cf master.cf.dist
sed '/^#/d; /^$/d' master.cf.dist > master.cf.clean
sed '/^#/d; /^$/d' master.cf.dist > master.cf

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 v3.5.7 here.

postconf -d | grep version
man 5 postconf

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

        warn_if_reject,

SUPPORTING FILES

NETWORK

vi /etc/postfix/access.client

.compute.amazonaws.com      REJECT too much SPAM from compute.amazonaws.com
compute.amazonaws.com       REJECT too much SPAM from compute.amazonaws.com

.smtpout.orange.fr              OK
smtpout.orange.fr               OK

.g7.fr                          OK
g7.fr                           OK

postmap /etc/postfix/access.client

EHLO

vi /etc/postfix/access.helo.regexp

/^localhost$/       550 you are not me
/^xc$/          550 you are not me
/^xc\.nethence\.com$/   550 you are not me
/^nethence\.com$/   550 you are not me

MAIL FROM

vi /etc/postfix/access.sender

# http://www.postfix.org/access.5.html
online.net               OK
.online.net              OK
ovh.com                  OK
.ovh.com                 OK
dhcp.yndx.net            OK
.dhcp.yndx.net           OK
onmicrosoft.com          REJECT
.onmicrosoft.com         REJECT

vi /etc/postfix/access.backscatterer

<> reject_rbl_client ips.backscatterer.org
postmaster reject_rbl_client ips.backscatterer.org

postmap /etc/postfix/access.sender
postmap /etc/postfix/access.backscatterer

DNSRBL

see dnsrbl

A GOOD START

keep vx. fix those defaults note mailbox_size_limit for happy procmail

biff = no
append_dot_mydomain = no
readme_directory = no
compatibility_level = 3.6
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

smtpd_banner = $myhostname ESMTP

# happy procmail
mailbox_size_limit = 0
mailbox_command = /usr/bin/procmail
#mailbox_command = /usr/pkg/bin/procmail

# messages missing a valid Message-ID header are not accepted
# may break DKIM but gmail wants it anyhow
always_add_missing_headers = yes

and eventually see procmail for more details

MAIN CONFIG & RELAY

eventually add space-separated CIDRs to mynetworks e.g. for Docker (172.17.0.0/16) or local subnets

note myorigin defaults to myhostname and mydomain defaults to FQDN minus the first component

note we are using the simple form of myorigin, pointing to the FQDN, because we are using pools for outbound, so it’s best to differenciate relays.

myhostname = xc.nethence.com
myorigin = nethence.com
mydomain = nethence.com
mydestination = localhost
    xc.nethence.com
    nethence.com
    ...

bypass any blacklists and restrictions - we want our own hosts to be able to deliver daily reports

mynetworks = 127.0.0.1/32 [::1]/128 172.17.0.0/16 172.24.0.0/16
    FRIENDLY-CIDR

# w/o permit_sasl_authenticated as we use a dedicated port for that
smtpd_relay_restrictions = permit_mynetworks reject_unauth_destination

CASUAL

nothing fancy so far, while disabling SASL on port 25

# 10MB --> 25MB
# (default: 10240000)
message_size_limit = 26214400

# unused when mbox or procmail
#home_mailbox = Maildir/

strict_rfc821_envelopes = yes
disable_vrfy_command = yes
smtpd_delay_reject = yes
enable_long_queue_ids = yes
smtpd_sasl_auth_enable = no

NETWORK

# slow down inbound on errors
# default is 10
smtpd_soft_error_limit = 5

# reduce inbound errors
# default is 20
smtpd_hard_error_limit = 10

# bounce faster when messages get stuck
# graylisting should be fine within 5 hours delay
# default is 5d
bounce_queue_lifetime = 5h
maximal_queue_lifetime = 6h

smtpd_client_restrictions = permit_mynetworks reject_unauth_pipelining
    reject_unknown_client_hostname
    check_client_access hash:/etc/postfix/access.client
    reject_rbl_client bl.spamcop.net
    reject_rbl_client public.sarbl.org
    reject_rbl_client cbl.abuseat.org
    reject_rbl_client psbl.surriel.com
    reject_rbl_client db.wpbl.info
    reject_rbl_client ix.dnsbl.manitu.net
    reject_rbl_client b.barracudacentral.org
    reject_rbl_client zen.spamhaus.org
    reject_rhsbl_reverse_client dbl.spamhaus.org
    reject_rhsbl_helo dbl.spamhaus.org
    reject_rhsbl_sender dbl.spamhaus.org

TESTING
    reject_rbl_client dnsbl.inps.de
    reject_rbl_client bl.blocklist.de
    reject_rbl_client spam.spamrats.com
    reject_rbl_client truncate.gbudb.net
    reject_rbl_client dnsbl.dronebl.org

#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_unauth_pipelining,
    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 & SPF

smtpd_sender_restrictions = permit_mynetworks, reject_unauth_pipelining,
    check_sender_access hash:/etc/postfix/access.sender,
    reject_non_fqdn_sender,
    reject_unknown_sender_domain,
    check_policy_service unix:private/policy
        check_sender_access hash:/etc/postfix/access.backscatterer

#deprecated: policy_time_limit = 3600
smtpd_policy_service_request_limit = 1

RCPT TO

smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_pipelining,
    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

STARTTLS

see postfix-tls

SPF OUTBOUND RECORDS

setup your MX and SPF DNS records accordingly

SPF INBOUND CHECKS

see SPF INBOUND for installing policyd-spf depending on your system

now enable the SPF inbound check and port 25 WITHOUT SASL

vi /etc/postfix/master.cf

policy    unix  -       n       n       -       0       spawn
    user=nobody argv=/usr/bin/policyd-spf
    #user=nobody argv=/usr/pkg/bin/policyd-spf

note check_policy_service unix:private/policy and smtpd_policy_service_request_limit above in smtpd_sender_restrictions

DKIM

see dkim

READY TO GO

this won’t harm – although everything should be fine already

postfix set-permissions
postfix upgrade-configuration

debian/ubuntu

systemctl restart opendkim
systemctl status opendkim

systemctl restart postfix
systemctl status postfix

slackware

start and enable

chmod +x /etc/rc.d/rc.postfix
/etc/rc.d/rc.postfix restart

from source

start and enable

vi /etc/rc.local

#self verbose
/usr/local/sbin/postfix start

status

postfix status
ps auxfww | grep postfix | grep -v grep
ls -lF /var/spool/postfix/private/policy

logs

vi logmail

tail -F /var/log/maillog /var/log/secure

chmod +x logmail

SASL

see POSTFIX AND SASL

OPERATIONS & ACCEPTANCE

always check the configuration syntax before restart

postfix check && echo ok

finally operate the daemon and proceed with acceptance testing

ADDITIONAL NOTES

chroot

ls -lF /var/spool/postfix/etc/
diff /etc/localtime /var/spool/postfix/etc/localtime
diff /etc/resolv.conf /var/spool/postfix/etc/resolv.conf
diff /etc/services /var/spool/postfix/etc/services

no symlink inside the chroot

#cp -f /etc/localtime /var/spool/postfix/etc/localtime

backup mx

Warning: I start to consider that this is a bad practice, as it is just too painful to maintain two MXes at once (otherwise spams come through your seconday MX server)

Play with relay_domains, transport_maps and do NOT list example.com into mydestination. Here’s a minimal setup

#backup MX
smtpd_banner = ESMTP
relay_domains = nethence.com
myhostname = ssd.nethence.com
myorigin = ssd.nethence.com
mydestination = ssd.nethence.com
mynetworks = 127.0.0.1/32

You also have to enable outbound STARTTLS in case the primary MX enforces it. Otherwise you would get

530 5.7.0 Must issue a STARTTLS command first (in reply to MAIL FROM command)

Finally, on the other side (primary MX), eventually add the backup MX into mynetworks or fine tune everything to make sure the relaying works accordingly. SPF should pass through already, as long as you’ve setup the appropriate DNS record for the backup MX.

log-aware-host-based-ips

eventually enable sshguard as an attempt to protect yourself from bot nets. however a parser going through logs is less than ideal.

TROUBLES

while using mail

postdrop: warning: unable to look up public/pickup: No such file or directory

==> you forgot to start postfix

ADDITIONAL NOTES

it might be convenient to checkup all mail services at once

vi /root/STATUS

echo postfix
pgrep -a master
echo

echo dkim
pgrep -a opendkim
ls -lF /var/run/opendkim/opendkim.sock
ls -lF /var/spool/postfix/run/opendkim/opendkim.sock
echo

echo dovecot
pgrep -a dovecot
echo

RESOURCES

Wietse Zweitze Venema http://www.porcupine.org/wietse/ http://www.porcupine.org/forensics/wietse.pgp

vdukhovni / postfix (unofficial mirror) https://github.com/vdukhovni/postfix

Postfix Basic Configuration http://www.postfix.org/BASIC_CONFIGURATION_README.html

Postfix Configuration Parameters http://www.postfix.org/postconf.5.html

master - Postfix master process configuration file format http://www.postfix.org/master.5.html

Changing the Postfix maximum email size https://www.electrictoolbox.com/postfix-email-size-limit/

Increasing Attachment Size in Posfix https://easyengine.io/tutorials/mail/postfix-attachment-size/

hardening

RFC2 Realtime List http://rfc-clueless.org/

Hardening Postfix For ISPConfig 3 https://www.howtoforge.com/hardening-postfix-for-ispconfig-3

How To Fight Spam Using Your Postfix Configuration https://www.howtoforge.com/virtual_postfix_antispam

Postfix SMTP relay and access control http://www.postfix.org/SMTPD_ACCESS_README.html

access - Postfix SMTP server access table http://www.postfix.org/access.5.html

acls

http://www.postfix.org/postconf.5.html#smtpd_client_restrictions

http://www.postfix.org/access.5.html

http://ftp.uma.es/mirror/postfix/doc/SMTPD_ACCESS_README.html

http://www.postfix.org/ADDRESS_VERIFICATION_README.html

troubles

POSTFIX LOGGING HOWTO http://rob0.nodns4.us/postfix-logging


HOME | GUIDES | LECTURES | LAB | SMTP HEALTH | HTML5 | CONTACT
Copyright © 2024 Pierre-Philipp Braun