Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Secure Django admin with self-signed SSL client certificate in Nginx.

Secure Django Admin with SSL Client Certificate in Nginx

If you need a good way to secure your Django admin site (e.g., http://example.com/admin/), this is it.

Reference: https://gist.github.com/mtigas/952344

1. Creating self-signed certificates

Find a directory to put your certificates, e.g., /etc/nginx/certs/.

Generate server certificate:

openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt

Generate client certificate:

openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr

# self-signed
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

Convert client key to PKCS (for browsers):

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

Make sure you enter an export password. Otherwise, you may be unable to import it to your browser.

2. Configure Nginx

/etc/nginx/sites-available/yoursite:

# the upstream component nginx needs to connect to
upstream django {
    #server 127.0.0.1:8000; # for a web port socket
    server unix:///tmp/uwsgi_yoursite.sock;
}

# configuration of the server
server {
    # the port your site will be served on
    listen      80;

    # the domain name it will serve for
    server_name .example.com; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    location /media  {
        alias /path/to/your/site/media;
    }

    location /static {
        alias /path/to/your/site/static;
    }

    location /favicon.ico {
        alias /path/to/your/site/static/img/favicon.ico;
    }

    # admin is served at port 443, return 404 on port 80
    location /admin {
        return 404;
    }

    # send all other requests to the Django server
    location / {
        include     /etc/nginx/uwsgi_params;
        uwsgi_pass  django;
    }

    # redirect error pages to Django, should provide Django views for these
    error_page 404 = /_error/404/;
    error_page 403 = /_error/403/;
    error_page 500 = /_error/500/;
    error_page 502 503 = /static/_503.html;
}

server {
    listen 443;
    ssl on;
    ssl_certificate /etc/nginx/certs/ca.crt;
    ssl_certificate_key /etc/nginx/certs/ca.key;
    ssl_client_certificate /etc/nginx/certs/ca.crt;
    ssl_verify_client on;

    location /media  {
        alias /path/to/your/site/media;
    }

    location /static {
        alias /path/to/your/site/static;
    }

    location /admin {
        include     /etc/nginx/uwsgi_params;
        uwsgi_param HTTP_X_FORWARDED_PROTO $scheme;
        uwsgi_pass  django;
    }

    # redirect other requests to http
    location / {
        return 301 http://$host$request_uri;
    }
}

3. Import client certificate

Download (with scp command) the .p12 file to your client computer.

Take Chrome browser on Mac OS for example, go to [Settings] -> [Show advanced settings]. Click [Manage certificate] button in HTTPS/SSL section. In the menu bar of Keychain Access, click [File] -> [Import Items]. Then select the .p12 file.

Restart the browser, access https://example.com/admin/, it should prompt you to choose a certificate. Select the one you just imported and you should be able to see the Django admin page successfully.

Copy link

ghost commented Jun 16, 2014

Please correct line

openssl req -new -key client -out client.csr

client -> client.key, so it would look like

openssl req -new -key client.key -out client.csr

As otherwise it generates an error

Error opening Private Key client
140139203737232:error:02001002:system library:fopen:No such file or directory:bss_file.c:404:fopen('client','re')
140139203737232:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:406:

@eliangcs
Copy link
Author

eliangcs commented Aug 17, 2014

@pete-experimenter corrected it. Thanks!

@twined
Copy link

twined commented Nov 8, 2014

ssl_client_certificate /etc/nginx/certs/ca.crt;

Should be

ssl_client_certificate /etc/nginx/certs/client.crt;

? Fixed it for me at least!

@eliangcs
Copy link
Author

eliangcs commented Nov 12, 2014

@twined: I'm using ca.crt but it works fine?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment