Client Certificates with mod_nss

Once server side certificates have been set up, setting up client side certificates requires some additional configuration, especially if you want to use them as the source of identity in your applications.

First, the application needs the additional directive in its /etc/httpd/conf.d specific file.

   NSSVerifyClient require

Did that work for you? NO…me neither…

OK, so actually, it was slightly more involved. First, I had to figure out how to turn on NSS debugging. That is done by editing the file

/etc/httpd/conf.d/nss.conf

and setting the value for

LogLevel debug

Once that worked, I saw messages in /var/log/httpd/error_log that looked like:

[Fri Mar 30 11:43:54 2012] [debug] nss_engine_kernel.c(309): Changed client verification type will force renegotiation
[Fri Mar 30 11:43:54 2012] [info] Requesting connection re-negotiation
[Fri Mar 30 11:43:54 2012] [debug] nss_engine_kernel.c(404): Performing full renegotiation: complete handshake protocol
[Fri Mar 30 11:43:54 2012] [debug] nss_engine_kernel.c(418): Re-negotation request failed: returned error -12176

So I enabled renegotiation. Again, editing /etc/httpd/conf.d/nss.conf

NSSRenegotiation on
NSSRequireSafeNegotiation on

And then client side certs worked for me. They probably won’t work for you, though.

I cheated.

In between, I set up a Dogtag PKI instance, generated a new server side certificate, a client side certificate for my browser, and installed the CA and Server side certificate in my web server.

I don’t think all of that was necessary.

A variation would have been to generate a client side certificate and sign it with the self-sign CA key that my web server was already using. The bottom line is that the browser and the server need to both trust the CAs used for the client and server certs.

Ok, so we’ve established a secure channel. This doesn’t mean much if we don’t know who the user is at the application level. The NSS layer follows the SSL approach of using the option: FakeBasicAuth to pass information from the Certificate through to the application. I uncommented the line:

NSSOptions +FakeBasicAuth +ExportCertData +CompatEnvVars +StrictRequire

You also need to tell NSS which value from the certificate should be passed as the user. In my case, it is the Subject:uid value, so I configured mine as:

NSSUserName SSL_CLIENT_S_DN_UID

The overall difference in the nss.conf file is:

--- /etc/httpd/conf.d/nss.conf.orig	2012-03-29 12:59:06.319470425 -0400
+++ /etc/httpd/conf.d/nss.conf	2012-03-30 16:21:24.836124822 -0400
@@ -17,7 +17,7 @@
 # Note: Configurations that use IPv6 but not IPv4-mapped addresses need two
 #       Listen directives: "Listen [::]:8443" and "Listen 0.0.0.0:443"
 #
-Listen 8443
+Listen 443
 
 ##
 ##  SSL Global Context
@@ -71,17 +71,17 @@
 #
 # Only renegotiate if the peer's hello bears the TLS renegotiation_info
 # extension. Default off.
-NSSRenegotiation off
+NSSRenegotiation on
 
 # Peer must send Signaling Cipher Suite Value (SCSV) or
 # Renegotiation Info (RI) extension in ALL handshakes.  Default: off
-NSSRequireSafeNegotiation off
+NSSRequireSafeNegotiation on
 
 ##
 ## SSL Virtual Host Context
 ##
 
-
+
 
 #   General setup for the virtual host
 #DocumentRoot "/etc/httpd/htdocs"
@@ -92,7 +92,7 @@
 # LogLevel is not inherited from httpd.conf.
 ErrorLog /etc/httpd/logs/error_log
 TransferLog /etc/httpd/logs/access_log
-LogLevel warn
+LogLevel debug
 
 #   SSL Engine Switch:
 #   Enable/Disable SSL for this virtual host.
@@ -198,7 +198,8 @@
 #   o OptRenegotiate:
 #     This enables optimized SSL connection renegotiation handling when SSL
 #     directives are used in per-directory context. 
-#NSSOptions +FakeBasicAuth +ExportCertData +CompatEnvVars +StrictRequire
+NSSOptions +FakeBasicAuth +ExportCertData +CompatEnvVars +StrictRequire
+NSSUserName SSL_CLIENT_S_DN_UID
 
     NSSOptions +StdEnvVars
 

Now a little bit of Python/WSGI specifics
For my WSGI application, I add in a value that says “pass on the authentication info to the application”

WSGIPassAuthorization On

Then the WSGI app can access the value via:

class Application(BaseApplication):
    @webob.dec.wsgify
    def __call__(self, req):
    context['remote_user'] = req.environ.get('REMOTE_USER')

Here is what my file /etc/httpd/conf.d/keystone.conf looks like to protect the suburl “main”:

WSGIScriptAlias /main /var/www/wsgi/httpdmain.py
WSGIScriptAlias /admin /var/www/wsgi/httpdadmin.py

< Location "/main" >
   NSSRequireSSL
   NSSVerifyClient require
   WSGIPassAuthorization On
< / Location >

One thought on “Client Certificates with mod_nss

  1. You should not need ‘WSGIPassAuthorization On’. The REMOTE_USER variable isn’t blocked by mod_wsgi. You only need that directive if needing to pass through HTTP_AUTHORIZATION.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>