Serve TLS certificate dynamically per Django view instead of via nginx/gunicorn - django

I'm using Django's request.get_host() in the view code to differentiate between a dynamic number of domains.
For example, if a request comes from www.domaina.com, that domain is looked up in a table and content related to it is returned.
I'm running certbot programmatically to generate the LetsEncrypt certificate (including acme challenge via Django). I store the cert files as base64 strings in PostgreSQL.
That works perfectly fine, but I can't figure out how to 'apply' the certificate on a dynamic per-domain basis.
I know that this is normally done using TLS termination, nginx or even in gunicorn. But that's not dynamic enough for my use-case.
The same goes for wildcard or SAN certificates (not dynamic enough)
So the question is:
Given I have valid LetsEncrypt certs, can I use them to secure Django views at runtime?

Django works as a wsgi server. Django gets an http request and does some work of its own. Then it hands it over to middleware and then to your views.
I'm fairly certain that the generic work django does at the start, that it already requires a regular http request, not a "binary blob of unreadable encrypted stuff".
Perhaps gunicorn can handle https termination, but I'm not sure.
Normally, nginx or haproxy is used. Also because it is something that needs to be really secure.
I'm using haproxy now, which has a handy feature that you can just point it at a directory full of *.pem certificate files and it will read them and use them. So if you could write the certs to such a dir and make sure haproxy is reloaded every time a certificate gets changed, you could be pretty close to a dynamic way of working.

Related

Redirecting API requests in Django Rest Framework

I have a two-layer backend architecture:
a "front" server, which serves web clients. This server's codebase is shared with a 3rd party developer
a "back" server, which holds top-secret-proprietary-kick-ass-algorithms, and has a single endpoint to do its calculation
When a client sends a request to a specific endpoint in the "front" server, the server should pass the request to the "back" server. The back server then crunches some numbers, and returns the result.
One way of achieving it is to use the requests library. A simpler way would be to have the "front" server simply redirect the request to the "back" server. I'm using DRF throughout both servers.
Is redirecting an ajax request possible using DRF?
You don't even need the DRF to add a redirection to urlconf. All you need to redirect is a simple rule:
urlconf = [
url("^secret-computation/$",
RedirectView.as_view(url=settings.BACKEND_SECRET_COMPUTATION_URL))),
url("^", include(your_drf_router.urls)),
]
Of course, you may extend this to a proper DRF view, register it with the DRF's router (instead of directly adding url to urlconf), etc etc - but there isn't much sense in doing so to just return a redirect response.
However, the code above would only work for GET requests. You may subclass HttpResponseRedirect to return HTTP 307 (replacing RedirectView with your own simple view class or function), and depending on your clients, things may or may not work. If your clients are web browsers and those may include IE9 (or worse) then 307 won't help.
So, unless your clients are known to be all well-behaving (and on non-hostile networks without any weird way-too-smart proxies - you'll never believe what kinds of insanity those may do to HTTP requests), I'd suggest to actually proxy the request.
Proxying can be done either in Django - write a GenericViewSet subclass that uses requests library - or by using something in front of it, e.g. nginx or Caddy (or any other HTTP server/load balancer that you know best).
For production purposes, as you probably have a fronting webserver, I suggest to use that. This would save implementation time and also a little bit of server resources, as your "front" Django project won't even have to handle the request and keep the worker busy as it waits for the response.
For development purposes, your options may vary. If you use bare runserver then a proxy view may be your best option. If you use e.g. Docker, you may just throw in an HTTP server container in front of your Django container.
For example, I currently have a two-project setup (legacy Django 1.6 project and newer Django 1.11 project, sharing the same database) and a Caddy server in front of those, routing on per-URL basis. With a simple 9-line Caddyfile things just work:
:80
tls off
log / stdout "{common}"
proxy /foo project1:8000 {
transparent
}
proxy / project2:8000 {
transparent
}
(This is a development-mode config.) If you can have something similar, then, I guess, that would be the simplest option.

Incorrect validation on ssl

I was trying to set up ssl using certbot. My webserver is nginx. when I run the command "sudo ./certbot-auto certonly" I enter my domain, which I purchased using netfirms. The domain is pointed to my amazon ec2 instance( public ip). I get this error " Type: unauthorized Detail: Incorrect validation certificate for TLS-SNI-01 challenge." Why is this happening?
I'm assuming it's the apache plugin that you are using.
The way the apache plugin works is that it adds a temporary with a "fake" certificate and SNI hostname that solves the TLS-SNI-01 challenge. Since this server has multiple IP addresses, I'm not certain if the apache plugin is capable of determining the correct IP address to listen on for this temporary . I haven't seen any success stories that explicitly mention this scenario, at least.
Your best bet might be to switch to the webroot plugin, which works by writing files to your existing DocumentRoot. If you'd like to continue using the automatic apache configuration while using the webroot authenticator, try something like this:
./certbot-auto --authenticator webroot --installer apache -w /var/www/html -d example.com
I had a similar problem - only when trying to update an existing key.
What I noticed was that the validation error said the it found a certificate that had all the other domain names in it that I had already requested in the certificate before.
Why does the validator see the previous certificate?
From the logs it seems to set up a new VirtualHost for each domain in the new cert in order to verify that the server is the one pointed to by the DNS. Validation requests to these mini VirtualHosts are not working correctly if it is seeing the existing cert with every domain in it - I though "my virtualhosts set up is somehow causing a problem!"
I thought maybe because I have a wildcard in my virtualHost it is somehow getting picked up before the mini temporary VirtualHosts.
I had named my existing hosts with 3 digit numeric prefixes so that I could carefully order them given that Apache said it processes .conf files in alphabetical order. This would mean they would get processed BEFORE any other .conf files starting with a letter.
I renamed my .conf files by adding a 'c' prefix before the number and now it appears at though it's working because it got passed the verification phase at least now - except now I have exceeded by 20 key requests for the week so I can't complete the process just yet!! Doh!

how to retrieve a ssl certificate in django?

Is it possible to retrieve the client's SSL certificate from the current connection in Django?
I don't see the certificate in the request context passed from the lighttpd.
My setup has lighttpd and django working in fastcgi mode.
Currently, I am forced to manually connect back to the client's IP to verify the certificate..
Is there a clever technique to avoid this? Thanks!
Update:
I added these lines to my lighttpd.conf:
ssl.verifyclient.exportcert = "enable"
setenv.add-request-header = (
"SSL_CLIENT_CERT" => env.SSL_CLIENT_CERT
)
Unfortunately, the env.SSL_CLIENT_CERT fails to dereference (does not exist?) and lighttpd fails to start.
If I replace the "env.SSL_CLIENT_CERT" with a static value like "1", it is successfully passed to django in the request.META fields.
Anything else, I could try? This is lighttpd 1.4.29.
Yes. Though this question is not Django specific.
Usually web servers have option to export SSL client-side certificate data as environment variables or HTTP headers. I have done this myself with Apache (not Lighttpd).
This is how I did it
On Apache, export SSL certificate data to environment variables
Then, add a new HTTP request headers containing these environment variables
Read headers in Python code
http://redmine.lighttpd.net/projects/1/wiki/Docs_SSL
Looks like the option name is ssl.verifyclient.exportcert.
Though I am not sure how to do step 2 with lighttpd, as I have little experience on it.

Using fineuploader, how can I use a HTTPS endpoint?

I am relatively new to javascript, and I got an uploader tool called fineuploader that I was considering to use. However locally (development machine) I got it to work (vb.net), but when I put it on my external server, I noticed that there is a post done in http and directly to the server's domain name (e/g/ mydomain.com), instead of mydomain.com/testproject. The site only allows for https traffic.
Is there an easy way to change this? (so it should point at https://mydomain.com/testproject/FileUpload.aspx
The code used by fineuploader shows a parameter called 'endpoint: '/FileUpload.aspx'
Do I have to make changes in the settings of IIS for this webservice?
If you need to enable CORS (cross-domain requests), Fine Uploader supports this. You should read my blog post on how CORS support is implemented in Fine Uploader and how you can support such requests in your server-side code.

Serving protected static media from django/nginx + streaming from a 3rd party app

We want to serve protected media from django, using something similar to the django nginx x-accel-redirect setup.
The only problem is that the static files are not located on the public facing django/nginx machine, but in a internal machine that streams the file via http/rest api.
Currently we download the file on the nginx machine and serve it via nginx x-accel-redirect, but we want to optimize this part and looking for options. x-accel-redirect has known problems with files that are streamed from another source.
We are contemplating using django itself as a quasi buffer, but are open to other options as integrating something like whizzer/twisted, or maybe even having another service altogether.
What would be the best option for serving those static files and preserving security?
Use: http://www.allbuttonspressed.com/projects/django-filetransfers
Make your own Django storage backend for the internal machine's http/rest api, that returns
a File object, and pass that object to filetransfer's server_file function.
That's how I do it in Mayan EDMS https://github.com/rosarior/mayan/blob/master/apps/documents/views.py#L300
django-storages' backends could help you get started.
https://bitbucket.org/david/django-storages/wiki/Home
Update:
Django_resto appears to have an http based storage class
https://github.com/aaugustin/django-resto/blob/master/django_resto/storage.py#L62
I had success doing something similar using django-http-proxy. This assumes that the image server is at least as reliable as the django server.
Then in my urls, I simply mapped the url to the http proxy view, something like:
(r'^protected/.*$', 'httpproxy.views.proxy'),
Then configured PROXY_FORMAT accordingly.
Implement a simple one-shot signature system in the media machine, using any very thin (django is OK, as it does not need to get to the database) code layer, and x-accel-redirect in nginx.
In the auth machines, generate the correct signature only when the user is allowed to get the resource, and return a 302 to the signed media.
The signature could be time-based, expiring in a fraction of a second, so a sniffer can't use the URL again.
You could use lighttpd to handle the streaming. It has a nice module to protect resources with signatures: http://redmine.lighttpd.net/wiki/1/Docs:ModSecDownload
So I'm thinking you could have nginx just proxy to the streaming server (that's lighttpd).
It's pretty easy to cook up the signature, here's a python example: init.py#cl-27">https://bitbucket.org/ionelmc/django-secdownload-storage/src/be9b18701015/secdownload_storage/init.py#cl-27