Understanding Postfix
Postfix is like a router in a network, just for email traffic. It receives emails from a sender and tries to send them on to their recipient, where the recipient can be the local postfix server or some other server.
As such, postfix has different interfaces to handle different protocols. The architecture is modular and contains different daemons each specifically handling only a few tasks. The most important daemons are the following:
- master is the main daemon that starts all the other daemons.
- smtpd is the SMTP server daemon handling incoming connections to receive emails from another mail server or client.
- smtp is the SMTP client handling outgoing connections to send email out to another mail server.
- local is the local delivery agent responsible for delivering email to the local server.
- qmgr processes and controls all messages in the mail queue.
Maps are used in postfix to control email routing. Such maps are used to accept or reject email, inform postfix about local recipients and more.
The main configuration files are main.cf which is the global configuration for all daemons and the master.cf which defines the runtime environment for the daemons. The master.cf configuration thereby overrides the configuration options from the main.cf.
SMTP Security
What do you need to secure? SMTP is by default a trusting protocol. Without any additional security configuration it will accept any emails from anybody and send it on to the recipient. This behaviour is called “open-relay” which means that the email server accepts an email from a spammer, for example, and just delivers it to the recipient. As this would cause your email server to nearly instantaneously appear on most of the block lists, this is something you want to avoid.
SMTP authentication is the best fitting solution for preventing your server acting as an open relay. But to secure the user-name and password combination while transferring it to the server, even the RFC4616 proposes to use an “adequate external data security protection, such as TLS”.
Installing Postfix and Cyrus
We need to install the postfix and cyrus (for SMTP authentication) packages on the server. Usually at this point I block incoming traffic to the postfix daemon (port 25) via iptable rules. This is to avoid someone sending through postfix as long as it is not configured. In my experience, open-relays are found very quickly once the server is online, so my advice would be to only open the port to postfix after you have configured postfix and are ready to test your configuration.
yum install postfix cyrus-sasl cyrus-sasl-lib cyrus-sasl-plain
Now sendmail is not needed anymore as it is replaced by postfix, we can remove it from the system.
yum remove sendmail
Once postfix and the required packages for SMTP authentication are installed, configuration needs to be done. The postfix configuration is mainly in the /etc/postfix/master.cf and /etc/postfix/main.cf file.
Open the /etc/postfix/main.cf with you favourite editor and edit the following lines. Those configuration options should already be in the configuration file. Some might be commented out and others have different default values.
# The internet hostname of this mail system. Example: mail.example.com myhostname = mail.example.com # The internet domain name of this mail system. Example: example.com mydomain = example.com # The domain name that locally-posted mail appears to come from, and that # locally posted mail is delivered to. Example: $mydomain myorigin = $mydomain # Optional external command that the local delivery agent should use for # mailbox delivery. By default this should be empty. mailbox_command = # The list of "trusted" remote SMTP clients that have more privileges # than "strangers" like relaying mail through Postfix. mynetworks = 127.0.0.0/8 [::1]/128 # The network interface addresses that this mail system receives mail on. # Specify "all" to receive mail on all network interfaces. inet_interfaces = all # The list of domains that are delivered via the $local_transport mail # delivery transport. Default: $myhostname, localhost.$mydomain, localhost mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain # What destination domains and/or subdomains this system will relay mail to. relay_domains = # Lookup tables with all names or addresses of local recipients. The default # (proxy:unix:passwd.byname $alias_maps) will cause postfix to access # the /etc/passwd file. local_recipient_maps =
The configuration options above mainly configure the basics for postfix like the domain and hostname this mail system is running on, the trusted networks and the network interfaces postfix is listening on.
Additionally, the following configuration items should be added to the /etc/postfix/main.cf configuration file. These settings are not absolutely necessary but will reduce the amount of spam you will receive. For a detailed description, see the Spamassassin Long delay (high latency) Trick.
# # Additional Settings # # This setting will slow down the sending from connecting clients. This trick # can reduce spam as spammers dont have time to wait. smtpd_client_restrictions = sleep 5 smtpd_delay_reject = no # Reject if the MAIL FROM domain has 1) no DNS A or MX record, or 2) a # malformed MX record or MAIL FROM address is not a fully-qualified domain smtpd_sender_restrictions = reject_unknown_sender_domain # Protect against bots/spammers that trigger lots of errors or scan for # accounts. When the error count reaches the soft-limit, delay the response by # the sleep-time but if the hard-limit is reached, postfix will disconnect. smtpd_error_sleep_time = 30 smtpd_soft_error_limit = 10 smtpd_hard_error_limit = 20
Configure SMTP-Authentication for smptd daemon
The following configuration items are not in the postfix configuration and need to be added to the /etc/postfix/main.cf configuration file. They configure the SMTP authentication settings for the smtpd daemon.
# # SMTP-AUTH configuration # # The name of the Postfix SMTP server's local SASL authentication realm. (default: empty) smtpd_sasl_local_domain = # Enable SASL authentication in the Postfix SMTP server. By default, the # Postfix SMTP server does not use authentication. smtpd_sasl_auth_enable = yes # The SASL plug-in type that the Postfix SMTP server should use for authentication. smtpd_sasl_type = cyrus # Postfix SMTP server SASL security options. noanonymous disallow methods # that allow anonymous authentication. smtpd_sasl_security_options = noanonymous # Enable inter-operability with remote SMTP clients that implement an obsolete # version of the AUTH command broken_sasl_auth_clients = yes # Do not report the SASL authenticated user name in the smtpd Received message header. smtpd_sasl_authenticated_header = no # Optional restrictions that the Postfix SMTP server applies in the context of # a client RCPT TO command, after smtpd_relay_restrictions. smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination
With this configured, every Linux account is allowed to login to postfix which uses Cyrus sasl. The sasl module then uses PAM to authenticate with the operating system.
If you don’t want to allow all shell accounts to send emails, read on. The idea is to allow only users that are member of a specific group to send mail.
The PAM module has a configuration file related to the Cyrus sasl authentication. The following line needs to be added at the top of the /etc/pam.d/smtp file.
auth required pam_listfile.so onerr=fail item=group sense=allow file=/etc/pam.d/mail_auth_group.allow
By adding this one line of configuration to the /etc/pam.d/smtp file, PAM will load the file /etc/pam.d/mail_auth_group.allow. This file will contain a list of group names that should be allowed to login. Create the file as it is named in the PAM configuration with your favourite editor and add all the group names you want to allow to authenticate. You can choose to use already existing groups like “user” or to create a new group specifically for this purpose.
Here is an example how the file with the group names could look:
# Add the groups that are allowed to authenticate # via postfix using sasl with PAM. mail_login
If you decided to use a group that does not yet exist, this group needs to be created:
groupadd mail_login
With the group created, this group needs to be added to the user you want to allow to authenticate with postfix. The following command adds the group as a supplementary group to the account:
usermod -a -G mail_login username
Configure TLS for smptd daemon
The following configuration items in /etc/postfix/main.cf are used to configure TLS support for the smtpd daemon. It also contains an SSL certificate. Such an SSL certificate can be requested by any CA. Some of them provide certificates for a very small price or even free. If you need a certificate consider CACert and StartSSL as they provide free certificates that can be used for this purpose.
# # TLS configuration # # With this, the Postfix SMTP server announces STARTTLS support to remote SMTP # clients, but does not require that clients use TLS encryption. smtpd_use_tls = yes smtpd_tls_security_level = may # Configures the server certificate file and key file as well as the CA's # intermediate certificate file. smtpd_tls_cert_file = /path/to/certificate.crt smtpd_tls_key_file = /path/to/certificate_key.key smtpd_tls_CAfile = /path/to/CA_certificate.ca # Enable logging of summary message for TLS handshake and to include # information about the protocol and cipher used as well as the client and # issuer CommonName smtpd_tls_loglevel = 0 smtpd_tls_received_header = yes # Postfix SMTP server and the remote SMTP client negotiate a session, which # takes some computer time and network bandwidth. SSL protocol versions other # than SSLv2 support resumption of cached sessions. smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache # Cached Postfix SMTP server session information expires after a certain # amount of time.RFC2246 recommends a maximum of 24 hours. smtpd_tls_session_cache_timeout = 10800s
You can alternatively provide the certificate and key combined in one file if you prefer it like that. To do that, use this configuration:
smtpd_tls_cert_file = /path/to/certificate.pem smtpd_tls_key_file = $smtpd_tls_cert_file
Configure alias addresses
As the setup is up to now, postfix would receive emails to recipients that have the linux user name as the username part in the email and the domain you configured. It would be possible to receive emails for root@example.com where root is the username on the system and example.com the domain configured in “mydomain”. Most likely you have the need to provide different email addresses. These are called aliases and can be configured using a mapping file.
In the /etc/postfix/main.cf add the following part to define the mapping file:
# Defines a lookup tables that alias specific mail addresses or domains to other local or remote address. virtual_alias_maps = hash:/etc/postfix/virtual_users
This file can now be filled with the aliases. These aliases can then be forwarded to another external email or to a local user. The format of the mapping file is the following:
# alias-address destination user / email friendlyname@example.com user1 info@example.com user1 webmaster@example.com user2 bob@example.com user2
You can assign any number of alias addresses to a user. All the email listed as an alias will be delivered to the user1 mailbox. Keep in mind that local delivery will only work if the destination user is available in the system.
After the mapping file is created execute the following command to create the lookup table database out of this file:
postmap /etc/postfix/virtual_users
Additionally there is another mapping file available outside of the postfix configuration directories. The aliases(5) provides a system-wide mechanism to redirect mail for local recipients.
Most likely you want to receive all the emails from different daemons and system components not to the user ‘root’ but in another user that has access to emails. To do this, open /etc/aliases with your favourite editor and navigate to the end of the file. You will find something like this:
#root: marc
Uncomment the line by removing the leading ‘#’ from it and replace the “marc” with the user who should receive all the messages addressed to root.
Apply the configuration
After the configuration changes we need to restart postfix to load the changed configuration. Additionally the Cyrus authentication daemon needs to be started.
$ /etc/init.d/postfix restart $ /etc/init.d/saslauthd restart
Check the startup configuration as you might want those services to start at boot time. This can be done via the following command:
$ chkconfig postfix on $ chkconfig saslauthd on
Test the SMTP authentication
To test the SMTP authentication connect with telnet to postfix as in the example below. Follow the example and type in the lines marked with “C: “. You should see a similar output (marked as “S: “) from the server as in the example.
If you don’t want to type all these commands, you can also use Mailserver-Test script which is a simple script that does all this for you:
$ telnet host.example.com 25 S: Trying 123.123.123.123... S: Connected to host.example.com. S: Escape character is '^]'. S: 220 host.example.com ESMTP Postfix C: EHLO test S: 250-host.example.com S: 250-PIPELINING S: 250-SIZE 10240000 S: 250-VRFY S: 250-ETRN S: 250-STARTTLS S: 250-AUTH PLAIN LOGIN S: 250-AUTH=PLAIN LOGIN S: 250-ENHANCEDSTATUSCODES S: 250-8BITMIME S: 250 DSN C: MAIL FROM: <sender@example.com> S: 250 2.1.0 Ok C: RCPT TO: <recipient@another-domain.com> S: 554 5.7.1 <recipient@another-domain.com>: Relay access denied C: QUIT S: 221 2.0.0 Bye Connection closed by foreign host.
What’s important here is that the “RCPT TO” line is answered from the server with a “Relay access denied”. This means that postfix did not accept the email as we have not authenticated.
To actually test the authentication, we need to prepare the username and password hash to be able to authenticate in the second run. This is done by the following command that expects the “MIME::Base64” module to be installed:
$ perl -MMIME::Base64 -e 'print encode_base64("$ perl -MMIME::Base64 -e 'print encode_base64("\000username\000password")' AHVzZXJuYW1lAHBhc3N3b3Jk0username$ perl -MMIME::Base64 -e 'print encode_base64("\000username\000password")' AHVzZXJuYW1lAHBhc3N3b3Jk0password")' AHVzZXJuYW1lAHBhc3N3b3Jk
This generated base64 encoded string is used in the authentication mechanism.
With this encoded authentication string you can authenticate in the telnet session.
$ telnet host.example.com 25 S: Trying 123.123.123.123... S: Connected to host.example.com. S: Escape character is '^]'. S: 220 host.example.com ESMTP Postfix C: EHLO test S: 250-host.example.com S: 250-PIPELINING S: 250-SIZE 10240000 S: 250-VRFY S: 250-ETRN S: 250-STARTTLS S: 250-AUTH PLAIN LOGIN S: 250-AUTH=PLAIN LOGIN S: 250-ENHANCEDSTATUSCODES S: 250-8BITMIME S: 250 DSN C: AUTH PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk S: 235 2.7.0 Authentication successful C: MAIL FROM: <sender@example.com> S: 250 2.1.0 Ok C: RCPT TO: <recipient@another-domain.com> S: 250 2.1.5 Ok C: DATA S: 354 End data with <CR><LF>.<CR><LF> C: From: <sender@example.com> C: To: <recipient@another-domain.com> C: Subject: test C: C: This is a test mail. When finished enter a . (dot) in a single line. C: . S: 250 2.0.0 Ok: queued as 1136E25820 C: QUIT S: 221 2.0.0 Bye S: Connection closed by foreign host.
Test the TLS connection
Testing the TLS connection is not as simple as connecting via telnet. The connection needs to be encrypted and the TLS handshake has to be done. To do this, use the following openssl command.
$ openssl s_client -starttls smtp -crlf -connect host.example.com:25 S: CONNECTED(00000003) S: depth=2 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority S: verify error:num=19:self signed certificate in certificate chain S: verify return:0 S: --- S: Certificate chain S: 0 s:/description=jlxxxxxxxxxxxx56/C=AT/ST=Wien/L=Vienna/O=John Doe/CN=*.example.com/S: emailAddress=webmaster@example.com S: i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 2 Primary Intermediate Server CA S: 1 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 2 Primary Intermediate Server CA S: i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority S: 2 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority S: i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority S: --- S: Server certificate S: -----BEGIN CERTIFICATE----- S: TISLYsdwcB31F81Eh8xqM5xp5ULgZY7A6krc+H4SOxbwAMNA3QrcfQmZLoUe1j3C ... S: r1jufHiIajXt8hc+LfqWLhSsZEyWHnzIAVyjwDUPHb7y5btY0cNp S: -----END CERTIFICATE----- S: subject=/description=lde8q3Auulfyay98/C=AT/ST=Wien/L=Vienna/O=John Doe/CN=*.example.com/S: emailAddress=webmaster@example.com S: issuer=/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 2 Primary Intermediate Server S: CA S: --- S: No client certificate CA names sent S: --- S: SSL handshake has read 6821 bytes and written 363 bytes S: --- S: New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA S: Server public key is 4096 bit S: Secure Renegotiation IS supported S: Compression: NONE S: Expansion: NONE S: SSL-Session: S: Protocol : TLSv1 S: Cipher : DHE-RSA-AES256-SHA S: Session-ID: AE79XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX027 S: Session-ID-ctx: S: Master-Key: C8A4XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX05AC S: Key-Arg : None S: Start Time: 1381181830 S: Timeout : 300 (sec) S: Verify return code: 0 (ok) S: --- S: 250 DSN C: EHLO test S: 250-mail.example.com S: 250-PIPELINING S: 250-SIZE 10240000 S: 250-VRFY S: 250-ETRN S: 250-AUTH PLAIN LOGIN S: 250-AUTH=PLAIN LOGIN S: 250-ENHANCEDSTATUSCODES S: 250-8BITMIME S: 250 DSN C: QUIT S: DONE
As you can see in the last part, the connection is established and the SMTP server responds to the “EHLO” greeting in the normal way.
Read more of my posts on my blog at http://blog.tinned-software.net/.
Thanks Iain for contacting me with the comment below. Even though I have also described steps to Harden the SSL configuration of your mailserver, it is always good to have information from more then one source. Thanks for providing those links.
I’ve been tinkering with CentOS for a year or so, having used Ubuntu previously. I’ve tried to set up secure server (a VM at home) and came to the point where I wanted to add mail provision. There are countless online tutorials and, whilst most of them got me to 95% of where I wanted to be, it wasn’t until I stumbled upon your series (with the detailed testing processes) that I nailed secure Postfix! The only thing I had to add was the creation of a self-signed certificate and, fortunately, I had found a very good article which supplemented yours and covered this in detail. I haven’t had chance to do anything with Dovecot yet but am confident that your article will hold my hand down that path!
As well as congratulating you, the other reason for writing is to mention the recently publicised Logjam attack. I seem to recall somewhere on your site you mentioned that comments had been disabled because of the amount of spam, otherwise I would have posted there. I don’t know if you might consider an addendum to one of the mailserver articles (maybe the one dealing with hardening the SSL configuration) or a separate article dealing with the Logjam attack and how to mitigate against it. Here are some links that I found:
Weak Diffie-Hellman and the Logjam Attack
Guide to Deploying Diffie-Hellman for TLS
I’ll keep an eye on your blog as the articles are well written and relevant to my interests.
Iain