Technology Lab / Information Technology

Taking e-mail back, part 4: The finale, with webmail & everything after

Setting up and securing Roundcube and going forward into a self-hosted future.

Certificate-based authentication with your own CA

We've played things pretty by-the-book so far with our use of cryptographic certificates in this guide—we got a signed personal cryptographic certificate way back in part two, and we're using real SSL-TLS certs signed by a real Certificate Authority (StartCom). However, for this next bit, we're going to become our own certificate authority and mint some of our own certificates.

This might sound confusing at first, but you've actually got access to the same cryptographic tools as any of the main certificate authorities do—the difference between you and a real certificate authority is that a real CA is "trusted" (by virtue of having passed a number of certification requirements). They're the tippy-tops of the whole system of trust that underlies SSL-TLS. The fact that many of them have either proven untrustworthy or at least easily compromised is the horribly smelly elephant in the room and the major reason why SSL-TLS is a giant, rickety house of cards; but, hey, we're not going to be able to fix that in this article, so just do what everyone else does and ignore the elephant and let's press on.

We want to use something more secure than just regular passwords to log onto our fresh Roundcube instance: we want to use cryptographic certificate-based authentication. To do that, we need to be able to issue certs to each of our webmail users, and to do that, we need to establish a private root certificate authority.

It's actually pretty darn easy to do: we just need to make a key and then use it to sign a root certificate, and boom, we're a CA! So, let's generate that key file. Enter the following commands:

cd /etc/ssl/private
openssl genrsa -des3 -out yourdomain.key 4096

You'll be prompted for a password with which to encrypt the key, so supply something strong (and make sure to remember it!). After the key is generated, we'll create a root certificate and sign it with that key:

openssl req -new -x509 -days 3065 -key yourdomain.key -out yourdomain.crt

You'll first be prompted for the private key password you just created so that the system can decrypt the key and use it to sign the certificate. Then, you'll be presented with a number of prompts for information to be embedded into the certificate. If you were a real CA, this information would be vitally important; since you're just creating a self-signed root certificate, you can fill in pretty much whatever you'd like. When the process completes, you'll have a root certificate file that's valid for 3,065 days.

Once you've got a root certificate, the next step is to generate personal certificates for each of your virtual mail users. Since we've been bombing along with three virtual users ("," "," and ""), we'll create three personal certificates. If you've got more, create more.

The process starts out similar to creating the root certificate: you'll create a private key and encrypt it the same way, but then we'll do something different and use the key to create a certificate signing request (or CSR), which we'll then combine with our CA certificate and private key to create a functional signed certificate.

A quick note: if you're not the only person who's going to be accessing your webmail—like, if you've got a buddy that you've granted an account—it's better to let each person create a private key and CSR for themselves. If you hold the private keys, you've got the potential to impersonate them—or at least generate a certificate and say that you're them. When they submit a CSR to you, you can sign off on it without having to know their private key.

Regardless of who does it, you'll want to first generate a private key like before, encrypt it, and then use it to create a CSR:

openssl genrsa -des3 -out you.key 4096
openssl req -new -key you.key -out you.csr

As with the root CA certificate, the CSR creation process will ask you for the details to embed into the resultant certificate; unlike the root CA process, this one is actually important. You'll want to make sure to enter your actual name as the Common Name and your actual e-mail address as the e-mail address. Don't worry about providing a challenge password or an optional company name; we won't be using those attributes.

Creating our certificate signing request.

This will spit out a CSR file, which we'll then sign using our root CA certificate and private key:

openssl x509 -CA yourdomain.crt -CAkey yourdomain.key -CAcreateserial -days 720 -req -in you.csr -out you.pem

And voila, you have a valid (if self-signed) personal certificate file and key! The easiest way to get the certificate and private key installed on your main computers (or your smartphone, for that matter) is to package the certificate and key into a single file, like this:

openssl pkcs12 -export -out you.pfx -inkey you.key -in you.pem -certfile yourdomain.crt

You'll be asked for the password for your encrypted key file, and you'll also be asked to specify an "export password" to wrap around the resultant you.pfx file. This is because you.pfx contains both certificate and private key and needs to be protected. Take the you.pfx file and import it into your computer's main certificate store (which you can usually do by double-clicking—though if you're using Firefox, you'll have to import the certificate separately, because Firefox keeps its own certificate store).

Enlarge / A certificate, viewed from inside OS X's certificate store, after having been imported as a PFX file.

Repeat the certificate generation and signing steps for as many local virtual users as you'll have logging on to webmail. If you're having your other users generate their own CSRs, all you'll need to do is the final step where you sign and then send them back their finalized certificates (preferably by a secure method—hand carried on a USB stick is a good idea). They'll already have their own keys, so they just need the actual certificate from you.

Activating certificate authentication in Nginx

Now we need to turn on certificate-based authentication in Nginx. This'll be a snap, since we've already got all the configuration lines written out in our Web site's Nginx virtual host file—we just need to uncomment it and make it live.

Pop open /etc/nginx/sites-available/roundcube and un-comment the following lines:

# Client auth via certs
ssl_client_certificate /etc/ssl/private/yourdomain.crt;
ssl_trusted_certificate /etc/ssl/private/yourdomain.crt;
ssl_verify_client on;
location / {
    if ($ssl_client_s_dn !~* "") {
        return 301;
    error_page 403 @fallback;

(Obviously, you want to leave the first "Client auth via certs" comment commented out, since it's a legitimate comment.)

It's definitely worth pausing a moment and focusing on what we've told Nginx we want to do here. The first three lines specify that we're going to be doing certificate checking for the entire site—anyone that wants to access the HTTPS version of your site will have to supply a cryptographic certificate, and that certificate must be valid. We're going to use your domain's self-signed CA certificate to check the submitted certificates against.

This means that clients will have to present not just any old client certificate, but one specifically signed by your CA—in other words, only certificates that you have issued.

You could stop there, which would permit anyone with a valid certificate signed by your CA to proceed. However, what if you only want certain accounts to be able to use the webmail interface? When Nginx validates a client certificate, it records a number of bits of information from that certificate and stores them temporarily in a set of variables. This gives you the option to sort through them and take various actions based on the validation result. We're using the ssl_client_s_dn variable in the root location block to match the e-mail address out of the Subject Distinguished Name variable (obviously, you'd want to replace "" with the address you actually want to match). If you want to check for more than one e-mail address, you can modify the root location regular expression like this:

if ($ssl_client_s_dn !~* "||") {

The whole Subject Distinguished Name contains more information than simply the e-mail address—if you want to see your certificate's entire subject DN, you can use the following command:

openssl x509 -noout -in you.pem -subject

You can match against anything in there—or in any of the other variables Nginx records.

But hold on—using "if" in Nginx location blocks is generally considered a bad idea, because "if" statements in Nginx can behave in ways that you might not expect. Fear not, though—in this particular case, using "if" is perfectly okay. For one, it's by far the easiest way to accomplish what we want to accomplish—we really do want a conditional check, and "if" is how you do it. More importantly, we're using our "if" in conjunction with a "return," which as noted in the documentation, is completely safe.

Speaking of that return—what else is happening in our location block? We're doing an "if not equal" check, and so if the client certificate is valid but doesn't contain the string we want in its subject DN, we respond with a 301 HTTP redirect to Dennis Nedry's "AH AH AH! YOU DIDN'T SAY THE MAGIC WORD!" video from Jurassic Park. Um...feel free to use your own redirect there. Finally, we're also taking the standard 403 FORBIDDEN response (which would be generated by the server if the client presents an invalid certificate, or no certificate at all) and kicking them over to the same annoying site via the named @fallback location.

So, let's try it out! Save the file and restart Nginx with service nginx restart, and then access your webmail site. You should be immediately challenged for a certificate. Present the correct one, and you'll be taken to your webmail's logon page like normal.

Enlarge / Responding to a certificate challenge while logging onto our webmail.

Expand full story

You must to comment.