Reputation: 15744
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
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
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