Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you manage X509 authentication on a Django app behind a Nginx proxy?

I would like to use a X509 client authentication for a secure API access.

X509 certificates are generated by our CA (memberca).

The aim is that :

  1. Client connect the API using SSL
  2. People with a valid X509 certificate from our memberca can access it
  3. We check that the certificate hasn't be revoked using a CRL.

I think, I may be able to manage that directly in the Nginx configuration. How should I do that ?

Can I do it with Django ? So how should I do then ? Can I mach with either solution the certificate to a user ?

For instance can Nginx map some certificate data with a WSGI header so I can match my user ?

like image 305
Natim Avatar asked Sep 07 '12 12:09

Natim


1 Answers

I already configured a similar setup.

First, I used Apache2 to authenticate the user.

You need to enable mod_ssl, and have to define (globally) :

  • SSLCertificateFile : points to your PEM encoded server certificate ;
  • SSLCertificateKeyFile : points to your PEM encoded server key ;
  • SSLCertificateChainFile : points to your PEM list of CA certificates ;
  • SSLCACertificatePath : points to the folder containing all your PEM CA certificates ;
  • SSLCACertificateFile : points to your CA certificate (should have the same value as SSLCertificateChainFile) ;
  • SSLCARevocationPath : points to the folder containing all your CRL ;
  • SSLCARevocationFile : points to the list of revoked certificates (your ca-bundle.crl)
  • SSLCARevocationCheck chain.

Now your server is ready to verify a client X.509 certificate.

If you don't want to use apache2 as frontal web server, you can configure it as a reverse proxy by enabling mod_proxy.

You just need to define a virtualhost like this :

<VirtualHost *:443>
    ServerName test.example.com:443
    ServerAdmin [email protected]

    RequestHeader set Front-End-Https "On"

    # Here I define two headers, Auth-User and Remote-User
    # They will contain the key SSL_CLIENT_S_DN_CN which is the name of the
    # client certificate's owner.
    <If "-n %{SSL_CLIENT_S_DN_CN}">
        # If the key doesn't exist, it means that the certificate wasn't sent or
        # it was revoked.

        RequestHeader set Auth-User "%{SSL_CLIENT_S_DN_CN}s"
        RequestHeader set Remote-User "%{SSL_CLIENT_S_DN_CN}s"
    </If>

    # Now enable SSL, and SSL via the proxy
    SSLEngine on
    SSLProxyEngine on

    ## Require a client certificate
    # SSLVerifyClient require
    ## NB: I prefer set it to optional, in order to allow the user
    ##     to connect to my application with a degraded mode (login+password)
    ##     It's easy to detect if the user was authenticated by apache by looking
    ##     at HTTP_AUTH_USER or HTTP_REMOTE_USER

    SSLVerifyClient optional

    # Maximum depth of CA Certificates in Client Certificate verification
    SSLVerifyDepth 4

    # Now, I pass all of this to my application, which is runned in nginx for example :
    <Location />
        ProxyPass http://<applciation host>
        ProxyPassReverse http://<applciation host>
        ProxyPreserveHost on
        # Send all informations about the client/server certificates to the application
        SSLOptions +StdEnvVars +ExportCertData
    </Location>
</VirtualHost>

And now, with django, you just have to enable Remote Authentication backend as described here.

All informations extracted from the client certificate are sent to the application, so using the request object (and/or with middlewares) you can use them.

I hope it helped you.

like image 182
linkdd Avatar answered Sep 21 '22 18:09

linkdd