How to authenticate ssl client certificates with nginx front-end?

5,330

If you want Apache to handle the SSL verification, then you'll need to put it in front of nginx, or else do some crazy stuff with passing the client DN to Apache via a custom header or something -- an awful lot of work.

There are some facilities for doing DN checking in nginx, though; the Nginx HTTP SSL module docs give the possible variables (right down the bottom, and unfortunately the markup's stuffed so there's no header for it) but $ssl_client_s_dn should be what you're looking for, and you can use nginx's programming-like directives (if $ssl_client_s_dn ~= CN=something_or_other kind of thing) to redirect or return a 403 if you don't like the certificate presented. It'll be very different to what you're used to with Apache, but ultimately Apache's way of doing this sort of thing isn't an oil painting either, so it all depends on which ugly you prefer...

I'm curious, though, as to why you'd put nginx in front of Apache... either of them should be capable of doing the whole job of serving web pages.

Share:
5,330

Related videos on Youtube

vertti
Author by

vertti

I'm a Python developer with over 30 years of programming experience. My current focus is using Django for development of business SaaS applications, but I like other Python frameworks: Pylons, Flask, and Webcore. I also spend a lot of time dealing with security and implementing counter-measures.

Updated on September 17, 2022

Comments

  • vertti
    vertti over 1 year

    I have some URL's that I like protecting with ssl client certificates using directives like these in my apache configs:

    SSLVerifyClient require
    SSLVerifyDepth  10
    SSLRequireSSL
    SSLOptions +StrictRequire
    SSLRequire %{REMOTE_ADDR} =~ m/^127\.0\.0\.1$/ \
      or ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \
      and %{SSL_CLIENT_I_DN_CN} eq "xxx" \
      and %{SSL_CLIENT_S_DN_Email} in {"[email protected]", "[email protected]",} )
    

    There don't seem to be any nginx directives to handle this, so I assume I have to pass everything on to Apache backend.

    Which brings me to my question, how to pass encrypted ssl? All the proxying is decrypting the ssl packets at nginx level before passing them on to Apache.

    • David Betz
      David Betz about 8 years
      You should ask a question about how to do what you want with Nginx. Downgrading back to Apache is not the solution. Using Django (you said mod_wsgi in another comment) can be done in Nginx easily. I do this for MANY sites. Look into uwsgi and Nginx. If you find Apache to be faster, your config is wrong. Ask those questions.
  • vertti
    vertti over 14 years
    Ok, I'll look into the directives bit. I'm running nginx in front of Apache because I need multiple "all ssl" virtual hosts and SNI seems pretty sketchy. I still need Apache primarily because of mod_wsgi for Django apps as well as passenger for RoR and php-fpm. I had a highly tuned Apache before needing multiple SSL sites so wouldn't have changed otherwise. As it turns out nginx reduced my ssl page loads by around .3 secs or so and is fantastic on the static files so it was a great change.
  • vertti
    vertti over 14 years
    Ok, got it working using something pretty close to the direction you pointed.
  • Doktor J
    Doktor J almost 7 years
    The link in your answer is broken, it appears nginx.org/en/docs/http/ngx_http_ssl_module.html#variables is the correct location. Also, you can pass these variables to your interpreter via fastcgi_param DN $ssl_client_s_dn if you're using fastcgi, If you're using proxy_pass instead, you can define arbitrary headers, e.g. proxy_set_header nginx-ssl-client-dn $ssl_client_s_dn and have the upstream server act on those headers (e.g. PHP behind an Apache upstream server would expose it as $_SERVER['NGINX_SSL_CLIENT_DN'])