Roger Dahl
Roger Dahl

Reputation: 15744

How to debug Django app that runs on top of TLS/SSL?

What is the best way to debug a Django app that runs on top of TLS/SSL?

Background:

I have a Django web app that uses X.509 client side certificates for authentication. When running under Apache, my app can only be reached via HTTPS. Clients that connect to the app provide a client side certificate which Apache validates and then forwards to the app in an environment variable. The app parses the certificate and provides access controlled content.

So far, I have only been able to debug the app under regular HTTP, with "./manage.py runserver". I have simulated an HTTPS connection by using a custom view handler middleware that kicks in, in debug mode. The view handler adds information to the request, similar to the information that would be parsed out of an actual client side certificate when run under HTTPS.

It would make debugging much easier for me if I could debug with the actual client side certificates that clients provide when connecting via HTTPS.

Upvotes: 2

Views: 1217

Answers (2)

Nils
Nils

Reputation: 5987

We use nginx in front of Django, with client certificate checking. NGINX does the SSL termination, client cert validation, and checking against revocation list. The client cert fields are passed in header variables up to the django app.

So then our django app doesn't receive the cert, it just looks at the header variables. I think the same mechanism applies in Apache.

For clients accessing the development server (e.g. './manage.py runserver'), we simply have a special case in the client. Example of a python client:

  if (proto == "https"):
    conn = http.client.HTTPSConnection( "cert."+webhost+":"+port,
                                        key_file = certfile, cert_file = certfile)
    headers = {}
  else:
    # fake client for local connections.  pass cert info in headers, as it would come
    # out of nginx
    conn = http.client.HTTPConnection( webhost+":"+port)
    headers = { 'X_SSL_CLIENT_S_DN':'/C=US/ST=California/O=yyyy/CN=zzzz',
                'X_SSL_CLIENT_I_DN':'/C=US/ST=California/O=xxxx/CN=wwww',
                'X_SSL_CLIENT_SERIAL':hex(serialnum),
                'USER_AGENT':"test client user agent",}

For unit tests, we do the same thing using the Django test client:

  from django.test.client import Client
  self.client = Client()
  response = self.client.get(url, data,
                **{
                'HTTP_X_SSL_CLIENT_S_DN':'/C=US/ST=California/O=yyyy/CN=zzzz',
                'HTTP_X_SSL_CLIENT_I_DN':'/C=US/ST=California/O=xxxx/CN=wwww',
                'HTTP_X_SSL_CLIENT_SERIAL':hex(serialnum),
                'HTTP_USER_AGENT':"test client user agent",
                })

Upvotes: 4

Roger Dahl
Roger Dahl

Reputation: 15744

I've come up with a workaround that works fairly well for me. I still debug with HTTP, but I pass the client side certificate in via an HTTP header. So, when I debug the web app with HTTP, I have the clients copy the client side certificate into an HTTP header. Before entering the views, the web app copies the certificate from the header and into the regular location in which it would be passed by Apache when using HTTPS.

The client side certificates are PEM formatted so, to be able to pass them in HTTP headers, the only thing that needs to be done is to remove the newlines on the client and reinsert them on the server.

If using this approach, note that Apache's default limit for the size of a single HTTP header field is 8190 bytes, configured with the LimitRequestFieldSize directive. For certificates that are larger than that, the configuration must be changed or the certificate must be split up and passed in multiple headers.

Upvotes: 0

Related Questions