Configuring Apache for SSL Client Certificate Authentication

2 minute read

Once you have a CA configured, you need to setup the Apache Web server to use it. The process of requesting the certificate from the browser and verifying that it’s properly signed is handled by Apache, which can then pass information about the verification to your application.

SSL Client Certificate will only be verified over an SSL connection so, before configuring client auth, make sure HTTPS is working.

Enabling client auth take one additional line in your configuration:

SSLCACertificateFile /etc/ssl/certs/CA.pem

Where CA.pem is the certificate you previously generated.

Once this is in place, you can protect URLs with client certificates in much the same way you would use HTTP Basic Auth.

The magic is the Apache SSLVerifyClient directive. The settings we care about are:

  • optional - the client may present a valid certificate.
  • require - the client must to present a valid certificate.
  • none: turn validation off.

The difference between optional and require is important. Just as with HTTP Basic Authentication, if client authentication fails, the server throws an error and, unless you’ve overridden it with ErrorDocument, it’s an ugly error. On top to that, it passes no information to your app. Better to use optional and let your application gracefully handle missing/invalid certificates than to use require and have the web server handle it.

However, the Apache documentation does warn that not all browsers support optional. I’ve yet to fine a modern one that doesn’t, please comment if you do.

In addition, you want enable the SSLOptions setting StdEnvVars. Enabling the StdEnvVars option causes Apache to pass information about the client certificate being used in the environment. Later, we’ll be using that information to match certificates to users in our system. However, the Apache documentation warns that:

This per default is disabled for performance reasons, because the information extraction step is a rather expensive operation. So one usually enables this option for CGI and SSI requests only.

So, use sparingly.

Put it together in a <Location> or <Directory> section and you have:

<Location /protected>
  SSLOptions +StdEnvVars
  SSLVerifyClient require
</Location>

When a visitor hits /protected Apache will request that they supply a client certificate and the browser will prompt with possible certificates and send the selected on to the server. Any certificate sent by the client will have it’s signature checked against the CA cert. Apache will then set the environmental variable SSL_CLIENT_VERIFY with one of three values:

  1. SUCCESS - The certificate was signed by us.
  2. GENEROUS - The browser sent a certificate, but not one of ours.
  3. NONE - No certificate was sent, the browser has none available or the visitor canceled the selection dialog.

Presuming SUCCESS, we’ll then be able to use other environmental variables to discover exactly who is visiting us.

if ENV['SSL_CLIENT_VERIFY'] == 'SUCCESS'
   current_user = User.find_by_certificate(TBD)
else
   # Handle failed login
end

I’ll cover the details in a future post. And in the finally post of this series, we’ll look at tuning the Apache configuration for best results.

  1. Introduction
  2. CA Setup
  3. Apache Configuration (you’re reading it)
  4. Client Certificate Generation in Ruby
  5. Best practices

Comments