You are currently looking at an older section of the wincent.dev website.
Please check the new version of the site at https://wincent.dev/ for updated content.

wincent knowledge base

« pstree mirror | Main | Upgrading to MySQL 4.1.20 on Red Hat Enterprise Linux 3 »

May 31, 2006

SSH tunneling and secure IMAP, POP3 and SMTP

During the time in which I was totally without Internet access last week I used iStumbler to see if there were any open, public wireless networks around which I could use to get on the Net.

Turns out that there was one which was occasionally available, but with a very weak signal and I could only ever get a pitiful few bytes per second over it.

When I first found this network I foolishly decided to check my email despite the fact that it uses inherently insecure protocols which transmit usernames and passwords in plaintext. I realized that the person running this open network could easily snoop them, so I decided to log into my server securely via SSH, immediately change the email passwords that had been transmitted as plaintext, and not try checking my email again until I had worked out a way to secure the protocols.

It turns out that the open network never gave me enough performance to actually make checking my email possible, but at least it prompted me to tighten things up. There are three principal ways to secure email traffic: SSH tunneling; using secure protocols like IMAPS, POP3S and SMTP with SSL; and using the stunnel tool. None of these provide end-to-end encryption for the content of your emails — you'll need to encrypt them before sending them in order to get that — but they do secure the connection between your machine and your local mailserver, thus protecting your passwords and preventing them from being snooped by a casual attacker.

SSH tunneling

This is the easiest of the three methods, but it requires you to have an SSH account on your mailserver. Basically all you have to do is use the -L switch to ssh to tunnel the appropriate ports. You would execute a command like this on your local machine:

sudo ssh -L 143:wincent.dev:143 \
         -L 110:wincent.dev:110 \
         -L 25:wincent.dev:25   \
         robinson@example.com

The sudo is necessary because root privileges are required to open any ports below 1024. The port numbers above are the standard port numbers for IMAP, POP3 and SMTP respectively. If you don't want to run the ssh process with root privileges you'll need to choose local port numbers that are higher than 1024; for example, you could just add 10000 to each of the numbers:

sudo ssh -L 10143:wincent.dev:143 \
         -L 10110:wincent.dev:110 \
         -L 10025:wincent.dev:25   \
         robinson@example.com

These commands basically say, "Log in via SSH to my account, robinson on the host wincent.dev; forward any network traffic that's sent to the local ports on this machine to the remote ports on the host". The traffic is encrypted over the SSH connection. (Needless to say, the account "robinson@example.com" doesn't actually exist; I'm just using it for illustration purposes. I guess it'll only be a question of a few weeks before I start seeing spam directed to that non-existent address...)

You then need to change the settings in your local email client. Instead of connecting to wincent.dev port 143, it will need to connect to localhost port 143 (or port 10143 if you followed the second example above). The forwarding is transparent and the email client doesn't need to know anything more; to the email client it looks just like a normal, plaintext IMAP, POP3 or SMTP connection.

The advantages of this method are that it is easy to set up and requires no modifications to the server. The downside is that it requires each email user to have an SSH account on the server and you have to make sure SSH is up and running with port forwarding activated before firing up your email client.

See the ssh man page for more information about SSH tunneling.

Using stunnel

I didn't actually try using this method but it seems like a viable option. Basically, the stunnel process serves as a wrapper for the insecure protocol. For example, you fire up stunnel on the server and get it to listen to port 993 (the standard port for IMAPS), and it forwards incoming, encrypted connections to the real IMAP process running on the server (not encrypted). You then set up your local mail client to communicate with SSL to the appropriate ports.

I chose not to use this method because I didn't want to have to meddle too much with the server configuration (for example, configuring the stunnel-wrapped servers to automatically start up at the correct runlevels). Furthermore, it is not clear to me whether it is possible to offer both secure and insecure access at the same time (which would be useful, for example, if you wanted to allow users to access via either IMAP or IMAPS during the transition period).

See the stunnel man page on your server (if it has one) for more information.

Update: February 2007

I later went ahead to explore the use of Stunnel. It co-exists peacefully with the STARTTLS solution.

Secure protocols: IMAPS, POP3S and SMTP with SSL

In the end this is the way I chose to go. It doesn't require users to have SSH accounts on the server, and it allows you to run both IMAPS and IMAP, POP3S and POP3, at the same time during a period of transition.

Basically, you set up an IMAPS server listening on port 993, a POP3S server running on port 995, and an SMTP server on port 25 which is capable of providing encrypted transport using TLS.

It is the most difficult to set-up, but also the most architecturally elegant. On the whole, I think it's worth the extra effort. The following summary applies to Red Hat Enterprise Linux ES 3.

Setting up the certificates

I already had an SSL certificate for use with Apache and my server running at wincent.dev (actually the same machine as the mail server). I wasn't interested in using a self-signed certificate (self-signed certificates are free but not trusted by connecting clients; they do serve to encrypt your mail transport but they cause unsightly warning messages that don't exactly inspire trust). I was curious to know whether the same certificate could be used for email transport. The answer is yes.

I needed to assemble three components for this to work:

  1. The private key for the certificate (most likely stored alongside the certificate).
  2. The certificate itself (most likely stored in /etc/httpd/ or one of its subdirectories).
  3. The certificate of the signing certificate authority (also most likely stored alongside the other certificate).

The order of these components is important. All three must be pasted, in order, into a file at /usr/share/ssl/certs/imapd.pem. The file should be owned by root and readable only by root.

The need to include the signing certificate authority's certificate is explained here:

Now for a small caveat - this all assumes your certificate was signed by a root certificate authority. In some cases, the CA which signed your certificate is not a root CA, but is a CA signed by a CA (or signed by a CA who was signed by a CA who is a root CA, etc.) This is often known as a chained certificate, or a ca-bundle.

What makes things tricky is that the remote client will look at your certificate, and try to verify it against the root CAs it knows about. If there is an intermediate CA between you and the CA the client knows about, it will need this certificate to sucessfully verify your certificate. As such, the server needs to not only provide clients with its own certificate, but also those of the intermediate CAs.

In the UW-IMAP server, this is achieved by appending all intermediate certificates to the file containing your own certificate, with the highest-level certificate last. (The root certificate is not required, as the client already has it.)

You can confirm that the certificate is valid and verifiable by running a command like this:

sudo openssl verify imapd.pem

If it is valid and the certificate chain can be successfully resolved you'll see a message like this:

imapd.pem: OK

Otherwise you'll see something like this:

imapd.pem: /C=GB/O=Comodo Limited/OU=Comodo Trust Network/OU=Terms and Conditions of use: 
http://www.comodo.net/repository/OU=(c)2002 Comodo Limited/CN=Comodo Class 3 Security Services CA
error 2 at 1 depth lookup:unable to get issuer certificate

If you get the order of the certificates in the file wrong then you'll see messages like this in your /var/log/maillog when you try to connect to your IMAPS server:

Unable to load private key from /usr/share/ssl/certs/imapd.pem

These errors are evidently caused because the ordering confuses the server and it is no longer sure to which certificate the private key applies to, and it ends up guessing incorrectly. As stated in this page:

If you get the error messages above, chances are the key and certificate do not match. Make sure you aren't using the default server.key file. You should also check the httpd.conf file to make sure that the directives are pointing to the correct private key and certificate.

You can check to make sure that you your private key and certificate are in the correct format and match each other. To do this, give the commands below to decrypt the private key in one terminal window and decrypt the certificate in the other. What you will be comparing are the Modulus and the Exponent of each key. If the modulus and exponent from the key matches the set from the certificate, you have just confirmed that your certificate and key are correctly paired.

After checking the modulus and exponent of my key and certificate I was sure that I was using the right components, so I tried changing the order of elements in the file and it started working.

You can use the same file for POP3S:

touch ipop3d.pem
chmod 400 ipop3d.pem
cp imapd.pem ipop3d.pem

For secure SMTP you can use the same file as a base, but you'll need to make some alterations:

touch sendmail.pem
chmod 400 sendmail.pem
cp imapd.pem sendmail.pem

Specifically, you'll need to prepare a separate file (call it ca.crt) containing only the certificate authority's certificate. You can then remove that portion from the sendmail.pem file. Sendmail will look for the certificate authority certificate in a separate file.

Switching on IMAPS support

Red Hat Enterprise Linux already has built-in IMAPS support. Now that your certificate is in place all you have to do is turn it on:

sudo chkconfig imaps on

You can verify that both IMAP and IMAPS are turned on (via xinetd) using:

sudo chkconfig --list

Switching on POP3S support

The same is true for POP3S. Support is already in place; you just have to turn it on:

sudo chkconfig pop3s on

You can verify that both the pop3s and ipop3 services are turned on (via xinetd) using:

sudo chkconfig --list

Configuring Sendmail to support TLS

Unlike IMAPS and POP3S, adding TLS support to SMTP requires a bit more work. First of all, you must make sure that your domain name MX records are updated to point to the correct host name (in this case, wincent.dev). This is because third parties connecting to your mail server may issue a STARTTLS command and it is important that the host name matches the name in the certificate. And being an MX record, you have to make sure that your host name is a proper A record (pointing to an IP address) and not just a CNAME alias (pointing to a domain name).

The basic sequence of events looks like this:

  1. Make sure that your secure host is a proper A record and not a CNAME alias in your DNS.
  2. If you're extra cautious, you'll wait for this change to propagate through the DNS and confirm that you existing secure access (HTTPS, IMAPS, POP3S) continues to work.
  3. Update MX records for all of your hosted domains to point to the secure host.
  4. Wait for the change to propagate through the DNS and confirm that incoming and outgoing mail delivery still work.
  5. Install your Sendmail certificate (described above).
  6. Configure Sendmail (see below).
  7. Restart Sendmail with sudo service sendmail restart.
  8. Use telnet to confirm that STARTTLS is offered by the mail server.
  9. Test that incoming and outgoing mail delivery still work (try with and without SSL).

The items you'll need to add to your Sendmail configuration are the following:

define(`confCACERT_PATH',`/usr/share/ssl/certs')dnl	
define(`confCACERT',`/usr/share/ssl/certs/ca.crt')dnl
define(`confSERVER_CERT',`/usr/share/ssl/certs/sendmail.pem')dnl
define(`confSERVER_KEY',`/usr/share/ssl/certs/sendmail.pem')dnl

To test that STARTTLS is correctly offered by the mail server you can do a telnet wincent.dev 25 and see if it lists STARTTLS in response to ehlo wincent.dev. When testing mail delivery it is a good idea to watch the mail log with sudo tail -f /var/log/maillog to make sure that SSL is being applied when you expect.

For more information about STARTTLS see these documents from the Sendmail website, as well as the text for RFC 2487.

Configuring your mail client

Now you just need to point your email client to the new host name (in my case, wincent.dev), the new ports (993 and 995), and turn on SSL support for SMTP (no change to the port). It is important to change the host name as well as the ports because the certificate is tied to the host name.

Posted by wincent at May 31, 2006 8:17 PM