Configuring Apache for SSL Client Certificate Authentication
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:
- SUCCESS - The certificate was signed by us.
- GENEROUS - The browser sent a certificate, but not one of ours.
- 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.
- Introduction
- CA Setup
- Apache Configuration (you’re reading it)
- Client Certificate Generation in Ruby
- Best practices
Comments