Postfix Revision as of Sunday, 20 December 2015 at 19:56 UTC
Written for CentOS 6.4, Postfix 2.6.6.
- Server is
example.com
. - Mail users map to local accounts (i.e., in
/etc/passwd
). - DNS looks like this:
[me@example.com ~]$ dig example.com -t MX +short
10 mail.example.com.
- Made sure that reverse DNS is set up properly.
Overview
Things were easier to set up after understanding these things, even
cursorily1.
Mail Transfer - Basic Idea
Lots of formal abbreviations! They are, luckily enough, quite
sensible. Here’s the basic
flow:
Sender > Server > Server > ... > Server > Receiver
(MUA) (MTA) (MTA) (MTA) (MUA)
You can be a bit more granular:
Sender > Server > Server > ... > Server | Delivery | > Receiver
(MUA > MSA) (MTA) (MTA) (MTA > MDA) | complete | (MRA > MUA)
(MDAs, when local, are also called LDAs.)
This separation of purpose is good since you can use a variety of
applications and
topologies
at each stage. Lot of possibilities. E.g.
Sender > Postfix > Procmail > Clam Anti-Virus > SpamAssassin > Procmail > Fetchmail > Receiver
(MUA) (MTA) ( MDA ) (MRA) (MUA)
Open Relays
Where it is not required to (1) authenticate to your server, and/or (2)
be in the same network as the server to send email. This is very bad for
public-facing mail servers. From a simpler time when there were very few
email servers and everybody was nice to each other.
Mailbox Formats
There are quite a few, each
with its own pros and cons. I personally like
Maildir.
Installation
yum install postfix cyrus-sasl
ln -s /usr/sbin/sendmail.postfix /etc/alternatives/mta --force
Configuration
The Basics
Postfix ships with sane and secure defaults. Here’s stuff I changed in
/etc/postfix/main.cf
First set the hostname and domain
myhostname = example.com
mydomain = $myhostname
Mail from this server will come from this domain.
myorigin = $mydomain
Accept mail on specified interface2 and all protocols (IPv4 and
IPv63)
inet_interfaces = all
inet_protocols = all
This server will think itself the final MTA (in the chain above) for
these domains:
mydestination = $mydomain, localhost
The server will only trust itself4
mynetworks_style = host
Use the Maildir format for message delivery
home_mailbox = Maildir/
Change the banner for fun (and no version information)
smtpd_banner = $myhostname ESMTP Why, hello there!
Now edit [http://www.postfix.org/master.5.html /etc/postfix/master.cf]
to enable the submission port5.
submission inet n - n - - smtpd
Uncomment any options ("-o"); we’ll take care of these in later.
Testing
Restart the postfix service. Then from another computer,
orangebox:~$
telnet
example.com
25
Trying 96.126.123.32...
Connected to example.com.
Escape character is '^]'.
EHLO
example.com
250-example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL
FROM:
testuser@internet.com
250 2.1.0 Ok
RCPT
TO:
me@example.com
250 2.1.5 Ok
DATA
354 End data with
.
Subject:
Hello!
How
have
you
been?
.
250 2.0.0 Ok: queued as 7602C72C2
QUIT
This should:
- Work for a valid user
MAIL FROM: postmaster@example.com
250 2.1.0 Ok
- Not work an invalid user
RCPT TO: nonexistent@example.com
550 5.1.1
nonexistent@example.com: Recipient address rejected: User unknown in local recipient table
- Deliver a mail to your home folder! The “
Maildir
” folder will be
created automagically.
~/Maildir
├── cur
├── new
│ └── 1377029606.Vca00I4025fM640219.example.com
└── tmp
But this is done insecurely. Let’s fix that.
Doing things securely
Generate a self-signed certificate6
openssl req -new -x509 \
-newkey rsa:4096 \
-days 3650 \
-nodes \
-out /etc/pki/tls/certs/dovecot.crt \
-keyout /etc/pki/tls/private/dovecot.key
chmod o= /etc/pki/tls/private/dovecot.pem
Now configure Postfix to use these certificates for TLS
smtp_tls_security_level = may
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/pki/tls/certs/postfix.crt
smtpd_tls_key_file = /etc/pki/tls/private/postfix.key
smtpd_tls_auth_only = yes
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
Restart Postfix. As always, see /var/log/maillog
for any errors.
Now test. Important You have to use the OpenSSL client instead of
telnet from this point on! Watch out for non-zero “Verify return codes”.
Avoid these by downloading the generated “postfix.crt
” using it with
your OpenSSL command.
openssl s_client -starttls smtp \
-CAfile /path/to/postfix.crt \
-connect nikhil.io:25
Another important warning is to keep the former telnet commands
lowercase. Else, the client will renegotiate every time you type
RCPT TO
. OpenSSL can waste your time like
that!
Some restrictions
Stepping throught the telnet output in the previous section, start
adding some restrictions to the client connection7:
smtpd_client_restrictions = reject_unknown_client_hostname, permit
Then the
[http://www.postfix.org/postconf.5.html#smtpd_helo_restrictions HELO]
command
smtpd_helo_required = yes
smtpd_helo_restrictions = reject_unknown_helo_hostname, reject_non_fqdn_helo_hostname, reject_invalid_helo_hostname, permit
[http://www.postfix.org/postconf.5.html#smtpd_sender_restrictions MAIL FROM]
smtpd_sender_login_maps = pcre:/etc/postfix/login_maps.pcre
smtpd_sender_restrictions = reject_non_fqdn_sender, reject_sender_login_mismatch, reject_unknown_sender_domain, permit
And finally,
[http://www.postfix.org/postconf.5.html#smtpd_recipient_restrictions RCPT TO]
.
This will allow you to relay messages (i.e. send email to other
domains) if you’re SASL-authenticated.
smtpd_recipient_restrictions = permit_sasl_authenticated, reject_unauth_destination, permit
Add this to /etc/postfix/login_maps.pcre
8.
/^(.*)@example.com$/ ${1}
Test away! You should see good errors like:
450 4.1.8
askljdas@lksjdklfsdjf.com: Sender address rejected: Domain not found
450 4.7.1
: Helo command rejected: Host not found
553 5.7.1
me@example.com: Sender address rejected: not logged in
Now add a way in which you an log in to the server remotely to send
messages through it.
SASL Authentication
Will use Cyrus.
Postfix uses it by
default.
You can see what other libraries Postfix was compiled with support for
as well:
[root@example ~]#
postconf
-a
cyrus
dovecot
You can then see what authentication methods Cyrus supports:
[root@example !]#
saslauthd
-v
saslauthd 2.1.23
authentication mechanisms: getpwent kerberos5 pam rimap shadow ldap
Since we’re dealing with local accounts, let’s tell Cyrus to use
/etc/shadow
. Open /etc/sysconfig/saslauthd
:
SOCKETDIR=/var/run/saslauthd
MECH=shadow
FLAGS=
Start the service
service saslauthd start
Make sure it starts when you reboot your server
chkconfig saslauthd on
Test!
[root@example !]#
testsaslauthd
-u
testuser
-p
secretpassword
0: OK "Success."
Now tell Postfix to use Cyrus in /etc/postfix/main.cf
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = cyrus
smtpd_sasl_security_options = noanonymous
Restart the Postfix service. Test via OpenSSL using the same helo
,
mail from
, etc. commands, but this time you’ll actually authenticate
before doing those things.
[root@toolkit ~]# telnet example.com 25
---
250 DSN
helo
example.com
250 example.com
auth
plain
An8o0tjsHojfDausWtzblk4bnZA
235 2.7.0 Authentication successful
Generate the funky MD5 output with your username and password:
echo -ne '\000user\000password' | base64
Preventing Spam, Bad Email, and DOS Attacks
Using Blocklists
Change smtpd_recipient_restrictions
to add some blocklists9 and
other stringent policies:
smtpd_recipient_restrictions =
permit_sasl_authenticated,
reject_invalid_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unauth_destination,
reject_rbl_client zen.spamhaus.org,
reject_rbl_client psbl.surriel.com,
reject_rbl_client bl.spamcop.net,
permit
Most residential IPs are banned by blocklists, so keep that in mind when
testing your setup:
554 5.7.1 Service unavailable; Client host [173.29.77.33] blocked using zen.spamhaus.org;
http://www.spamhaus.org/query/bl?ip=173.29.77.33
Using SPF
Sender Policy Framework
prevents fake sender addresses from your domain. It’s a great idea and
is something everyone should do10.
To empower Postfix with SPF, first install some required packages from
EPEL:
yum install perl-Mail-SPF perl-Sys-Hostname-Long --enablerepo=epel
I’m going to try the Perl implementation of
SPF. 11 Download,
extract, move to a good place:
tar -xvzf postfix-policyd-spf-perl-2.010.tar.gz
cd postfix-policyd-spf-perl-2.010
mv postfix-policyd-spf-perl /usr/local/bin/
Now set up /etc/postfix/main.cf
. Add to smtpd_recipient_restrictions
# Other options not shown for brevity
smtpd_recipient_restrictions =
check_policy_service unix:private/policy-spf,
policy_time_limit = 3600 # Default is 1000; too short
12
Then add to /etc/postfix/master.cf
policy-spf unix - n n - 0 spawn user=nobody argv=/usr/bin/perl /usr/local/bin/postfix-policyd-spf-perl
Restart the service. Send yourself an email from another service Gmail,
and look for SPF output in /var/log/maillog
Some Limits on Interaction
# Set the time unit
anvil_rate_time_unit
= 120s
# This allows 20 connections per second
smtpd_client_connection_rate_limit
= 2400
# Don't allow more than 100 messages per second
smtpd_client_message_rate_limit
= 12000
# Messages can't be more than 20M in size
message_size_limit
= 2048000
# Can tweak these depending on use
smtpd_soft_error_limit
= 10
smtpd_hard_error_limit
= 20
smtpd_error_sleep_time
= 60
Prevent Abuse
Greylisting is a great
approach to fighting spam. The idea is that spammy mail servers do not
respect the RFC spec that, if an email couldn’t be delivered initially,
they are to re-attempt delivery later.
Postgrey works well for this. By
default, it asks MTAs to attempt redelivery in 5 minutes.
yum install postgrey
service postgrey start
chkconfig postgrey on
This will run on a Unix socket. The next step is to get Postfix to use
it. Edit /etc/postfix/main.cf
.
# Other options not shown for brevity
smtpd_recipient_restrictions =
check_policy_service unix:postgrey/socket
I lowered the default wait time to a minute by creating
/etc/sysconfig/postgrey
and adding this:
OPTIONS=$OPTIONS" --delay=60"
Restart Postfix and Postgrey. You’ll see something like this in
maillog
to make sure it’s working:
postgrey[12582]: action=pass, reason=client whitelist, client_name=mail-qc0-f169.google.com,
client_address=209.85.216.169, sender=anand.nikhil@gmail.com, recipient=test@example.com
Miscellaneous
“warning: dict_nis_init:”
Disable NIS lookups
alias_maps = hash:/etc/aliases
“Relay Access Denied”
Usually
something
quite
simple.
Footnotes
- Email agent
infrastructure - How email
works - Securing Postfix
(Red Hat) - Relay and Access
Control - Postfix SASL Howto
- Cyrus SASL for Systems
Administrators - Postfix + SPF +
CentOS6 - Postfix FAQ
Category: Nikhil’s Notes
Category: Installation Logs
-
The Linode page on mail
servers is also a great overview ↩︎ -
Can specify IP address also:
inet_interfaces=all<br /> inet_interfaces=eth0,eth1<br /> inet_interfaces=38.9.127.1,10.0.1.23<br /> inet_interfaces=mail.tux.com<br />
↩︎ -
Default is IPv4 ↩︎
-
Can trust network classes or subnets and specific IP addresses ↩︎
-
I was a little confused about this but think I understand. Port 25
is the standard SMTP port that used for MTA-to-MTA communication. So
if you have a user who is behind an ISP connection that blocks port
25 (for spam or other reasons like bad proxying), they can still
send/submit mail to your server, even if it’s not the final
destination on the message envelope, on port 587. ↩︎ -
http://www.postfix.org/postconf.5.html#smtpd_client_restrictions ↩︎
-
From ServerFault. Postfix can
use a lot more formats for controlled envelopes. See the output of
postconf -m
. For instance, I initally used this file (Specified
withhash:/path/to/file
):
<nowiki>#</nowiki> Envelope sender Owner<br /> me@example.com me<br />
↩︎ -
Of which there are a lot
available ↩︎ -
To get started, read about the
syntax or use a
wizard, then the validation
tool. ↩︎ -
Tried to make the Python version work but ran into issues with
Python3 and theipaddr
module. ↩︎ -
http://www.postfix.org/SMTPD_POLICY_README.html#client_config
↩︎