Secure relaying with Postfix TLS
3:15 PM, Sat, Dec 8 2007
The requirement: Users outside the corporate network need to be able to send email from their corporate accounts. This is needed by home users, remote office users, and traveling users. This post is all about outgoing (sending) mail from users. Incoming mail is handled by entirely separate protocols, usually IMAP or POP, which are easier to configure.
Back in the Sendmail days, it was normal for mail servers to run as open relays, meaning they would accept and attempt to deliver mail for any host on the Internet. This practice was convenient but did not survive the advent of spam. Today, any mail server that acts as an open relay will quickly end up on the open relay blocking system and will not be able to send mail to other hosts. Relays must be secure, which means mail servers must be configured to handle different email sources, destinations, and connection types differently.
The rules of secure relaying are simple. Assume that our domain name is
example.com. Assume we have an internal, trusted network, and we have the external, untrusted Internet. Which mail should the mail server accept?
- Mail addressed to
firstname.lastname@example.org be accepted and processed
- Mail which originates on the internal network (ie, corporate network) to
email@example.com, should be sent out to the appropriate mail exchangers. This means that the MTA must make decisions based on the connecting client's IP address.
- Mail sent from
firstname.lastname@example.org, from the untrusted Internet, from an authenticated connection, should be relayed
All other mail should be rejected, but not bounced.
This example will be based on the Postfix MTA, version 2.2.9. The latest version is Postfix 2.4.6, which added support for Dovecot as the Simple Authentication and Security Layer (SASL) server. Postfix prior to 2.3 did supported only Cyrus Project's SASL support. We are using SuSE 10.1's MTA, which is Postfix 2.2.9, so we must use Cyrus SASL. This is unnecessarily complex, when Dovecot by itself can serve both IMAP and SASL, but is the only option until we upgrade from SuSE 10.1 on the server. (In late April 2008, we will switch to Ubuntu Server 8.04 Long Term Support LTS "Hardy Heron".)
The goal is to have Postfix deny relaying of mail, except to users who have authenticated using password authentication. Of course, sending unencrypted passwords over the net is not safe and should not be done so we'll also need to enable Transport Layer Security (TLS) within Postfix.
SASL is a simple protocol which allows a server to make authentication requests to a SASL server. Think of it as PAM over a network. One server, such as Postfix, needs to authenticate a user, so Postfix connects to a SASL server to find out if the user is authorized. PAM could do the same thing but PAM requires linking in a library and is more Linux-specific. SASL operates over sockets so the SASL client (Postfix in this case) can connect to a SASL server which might be on a different computer, in a different architecture. In this case we will be using the Cyrus SASL server, which has good support within Postfix. We're using cyrus-sasl-2.1.21-18, which comes with SuSE 10.1. The latest version is Cyrus SASL 2.1.22.
Configuration can be broken into these steps:
- Verify the packages are installed
- Configure Cyrus to serve SASL
- Configure Postfix to make authentication requests to SASL
- Configure Postfix to use TLS
- Test the TLS configuration on Postfix using OpenSSL
- Test the server to make sure it is not an open relay
- Configure the Mozilla Thunderbird email client to use this setup
Step 1: verify the packages: The necessary packages are Postfix, compiled with Cyrus SASL libraries, and Cyrus SASL. In our case, these packages were provided in the Suse 10.1 distribution.
Step 2: configure Cyrus to serve SASL: We'll be using plain old Linux system users. Cyrus can use many other user sources, including LDAP and a database, but for this simple installation, we're using
/etc/passwd, without even using PAM. The Cyrus SASL library puts together the path to find its configuration file. In our case, it is looking for
/usr/lib/sasl2/smtpd.conf. This file should contain:
pwcheck_method: saslauthd mech_list: plain login
mech_list is necessary to prevent Postfix from reporting other authentication methods which SASL does not actually support. This file must be named
Step 3: Configure Postfix to make authentication requests to SASL: Edit the Postfix
main.cf file, and add:
smtp_sasl_auth_enable = no smtpd_sasl_auth_enable = yes smtpd_use_tls = yes # we'll configure this later smtpd_sasl_security_options = noanonymous smtp_use_tls = no smtpd_recipient_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination
At this point we can test sending mail. Hint: Many ISPs block port 25 connection, to prevent "spam zombies" from sending outgoing spam. You can make Postfix listen on additional ports by adding a line like:
10025 inet n - n - - smtpd
master.cf. This will make Postfix also listen on 10025, so if users are being blocked on port 25, they can make a simple configuration change in their email software and get through on the higher port.
main.cf file says
smtpd_use_tls = yes, but we haven't configured any SSL keys for the server. What happens in this situation?
telnet chiralsoftware.net 10025 Trying 188.8.131.52... Connected to chiralsoftware.net. Escape character is '^]'. 220 chiralsoftware.net ESMTP Postfix ehlo example.com 250-chiralsoftware.net 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250-AUTH LOGIN PLAIN 250 8BITMIME starttls 454 TLS not available due to local problem
This is not good.
smtpd_use_tls = yes could be set, and the administrator could think that it's working, and clients could connect, and all the while plaintext passwords would be going over the net. Even worse, an installation could be set up correctly, and then if the keys were deleted by accident, the system would silently switch into a non-secure mode, and most clients will fall back to non-encrypted authorization sessions. To fix this, add:
smtpd_tls_auth_only = yes
Now Postfix will only accept authentication in connections which are TLS encrypted.
Step 4: Configure Postfix to use TLS: This is the most complicated part of the setup. Note that Postfix TLS configuration changed somewhat between 2.2 and 2.3, and we're configuring for 2.2, so this will be somewhat different if you're using the current version. The ideas are the same.
We're using a GoDaddy certificate. GoDaddy became a root authority by acquiring Starfield Technologies, which purchased the Valicert Class 2 Policy Validation Authority. This presents a problem: all browsers, and OpenSSL, come with the major CA root certificates, but not with GoDaddy's new root certificate. To get around this we need to install a bundle of intermediate certificates which link the Valicert certification, which everyone has, to the GoDaddy certificate authority (CA) certificate, which then has signed our site certificate.
Here's the process with GoDaddy: First, we created a key pair using the Java keytool command. This command creates a keystore, which contains the public key / private key pair. The keystore is secured with a passphrase. After generating the key pair, we again use
keytool to generate a signing request. We purchase a certificate credit from GoDaddy. GoDaddy gives instructions for submitting the public key of the key pair for signing with GoDaddy's Certificate Authority public key. When that is done, GoDaddy emails back a link to a zip file which is downloaded, and which contains the signed site certificate, and GoDaddy's intermediate certificates bundle. The signing request contained only the public key data. Private key data never leave the server.
keytool again and followed the instructions to install this key and signed certificate and intermediate certificate into our Tomcat keystore file. Then it is a simple matter to have Tomcat use those keys to enable secure connections. All works, and now you can access chiralsoftware.com with either http: or https: connections. We need this level of security to protect our employees' access to our internal web applications, and to protect customer access to their applications during development.
The Java-format keystore is the only copy of the keys we have. We need to take the same private key and certificates from the Java keystore and put them into a form which is usable for Postfix. Like most Unix software, Postfix uses the OpenSSL library. Postfix requires key data in a PEM file (entirely different from a Java keystore) and it requires a private key with no passphrase. The PEM file must include:
- Our secret key
- Our signed certificate
- GoDaddy's intermediate certificate, which is signed by the old Vericert root certificate
Extracting the secret key from our keystore requires a trick. The
keytool created the key and put it in the keystore, so
keytool must be able to get it out, right? Wrong.
keytool can extract public certificates, but it has no ability to extract private keys. Why Sun did it this way makes little sense. It's perfectly easy to write a key extraction program in a few lines of Java, and any Java application which wants to be a TLS-protected server will need access to private key data. If
keytool can't do it, GetKey.java can. To use
GetKey find the alias of the key you want in the keystore, using our favorite
% keytool -list -keystore tomcat.keystore Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 3 entries tomcat, Jun 16, 2007, PrivateKeyEntry, Certificate fingerprint (MD5): F0:A0:..... intermed, Jun 16, 2007, trustedCertEntry, Certificate fingerprint (MD5): D5:2F:..... cross, Jun 16, 2007, trustedCertEntry, Certificate fingerprint (MD5): 82:6D:.....
We need the
tomcat private key, the
tomcat certificate, and the
intermed intermediate certificate. That's one private key (requires
GetKey) and two certificates (can be extracted with plain old
Download the GetKey command, and start extracting:
java -jar GetKey.jar /tomcatpath/tomcat.keystore my-passphrase tomcat > /etc/postfix/example.pem
That gives us the private key. The next two items we need are public certificates, so we extract them with
keytool. We grab the aliases for
tomcat (keytool will only get tomcat's public certificate) and GoDaddy's intermediate certificate, which has the alias
intermed in this keystore.
% keytool -exportcert -keystore tomcat.keystore -storepass my-passphrase -alias tomcat -rfc >> /etc/postfix/example.pem % keytool -exportcert -keystore tomcat.keystore -storepass my-passphrase -alias intermed -rfc >> /etc/postfix/example.pem
Set the path to the certificate file in
smtpd_tls_cert_file = /etc/postfix/example.pem smtpd_tls_key_file = /etc/postfix/example.pem
and I got this error in the log:
Dec 9 00:24:18 server postfix/smtpd: warning: connect to private/tlsmgr: No such file or directory Dec 9 00:24:18 server postfix/smtpd: warning: problem talking to server private/tlsmgr: No such file or directory
To fix that, find the line:
tlsmgr unix - - n 1000? 1 tlsmgr
master.cf and uncomment it if it is commented out.
Now everything should be ready.
Step 5: test TLS using the OpenSSL command line utility: OpenSSL ships with a very handy CLI utility. You'll have this available on any Linux or OS X system. It can also be installed on Microsoft Windows. Let's test the server:
openssl s_client -starttls smtp -connect example.com:25
It displays detailed information about the TLS connection. After that it shows the following SMTP commands:
ehlo foo 250-chiralsoftware.net 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-AUTH LOGIN PLAIN 250 8BITMIME quit 221 Bye
Note that it offers
AUTH LOGIN PLAIN. If we telnet in, without using openssl, we see instead:
% telnet example.com 25 Trying 192.168.1.99... Connected to example.com. Escape character is '^]'. 220 example.com ESMTP Postfix ehlo somename 250-example.com 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250 8BITMIME
It does offer
STARTTLS, but does not offer
AUTH LOGIN PLAIN. Perfect. We have tested both positive and negative cases.
Sending mail through a mail client, such as Mozilla Thunderbird, works at this point. I'll show the configuration at the end.
Step 6: Test the server to make sure it is not an open relay: If your server, by some chance, functions as an open relay, it will quickly be used as a spam gateway. Use the Abuse.net open relay test service to test your server. This open relay testing service tries over a dozen different ways to relay mail through the server. If it can relay mail, your server is misconfigured and should be fixed promptly.
Step 7: Configure a mail client, such as Mozilla Thunderbird: Go to Edit / Account Settings. Select "Outgoing Server (SMTP)". Add or edit an outgoing server, like this:
In this example, I'm using 10025, which is not the default SMTP port. As explained above, many ISPs block port 25 from customer connections, so it's handy to have Postfix listening on some non-standard port, in addition to port 25.
Conclusion: We have configured a secure way for our corporate users to send mail when they are outside the corporate network. Our configuration relied on Postfix, Cyrus SASL and OpenSSL, components which are standard in any Linux server distribution. We also used
keytool and a short Java program called
GetKey to extract keys from a Java keystore file. These keys were signed by GoDaddy.
This was a fairly complicated process, mainly due to getting the TLS data into the right format and location. This whole process would be simpler if all the various network servers could find key and certificate data in a common format and common location. All Java programs can use the keystore file, so if we were using a Java mailer such as Apache James, we could have it share key data with Tomcat. Also, some future release of Java, perhaps based on OpenJDK, might be able to use OpenSSL for its keys and encryption, which could allow it to share key data with all the other software which uses OpenSSL, such as Apache HTTPD and many others.
In a future article we could explore more advanced mail configuration, such as virtual domains and keeping user data in a database. We could also explore improving security by issuing certificates to users, so that we would have mutual authentication. Mutual authentication would give us more assurance than a password.