Beyond SSL client cert authentication: authorization

In a previous article, I tried to make the case for using a private certificate authority to authenticate access to internal tools with SSL client certificates.

This approach is perfect to secure access to the likes of kibana, riemann-dash, graphite or similar tools.

If you start depending more and more on client-side certificates, you’re bound to reach the point when you need to tackle authorization as well.

While well-known, it is perfectly feasible to do so while keeping your private CA as a single source of internal user management.

I will be assuming a private CA authenticates clients for sites accessing app.priv.example.com and that three SSL client certificates exist: alice.users.example.com, bob.users.example.com, charlie.users.example.com (as mentionned above, see here for a quick way to get up and running).

Now since our certificates bear the names of clients what we need to do is retrieve the certificate’s name. Assuming that you have a web application exposed through HTTP which nginx proxies over to, here are the relevant bits that need to be added.

proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Issuer $ssl_client_i_dn;

Let’s go over them one by one:

As far as configuration is concerned, this is all that is needed. There are more variables that you can tap into if necessary refer to the nginx http_ssl module documentation for an exhaustive list. If you rely on the apache webserver, similar environment variables are available as documented here.

Within applications, you’ll receive the identity of clients in this format and can thus be retrieved with a regexp:

CN=bob.users.example.com

It’s now dead simple to tie in to your application. Here is a simple ring middleware which attaches the calling user to incoming requests.

(defn wrap-ssl-client-auth [handler]
  (fn [request]
    (let [ssl_cn   (get-in request [:headers "X-Client-DN"])
          [_ user] (re-find #"CN=(.*)\.users\.example\.com$" ssl_cn)]
      (handler (assoc request :user user)))))