Quickstart from source¶
This guide provides instructions for running your own certificate authority by installing django-ca from source. This method requires a lot of manual configuration and a lot of expert knowledge, but is a good choice if you use an exotic system or other options do not work for you for some reason. If you’re looking for a faster and easier option, you might consider using docker-compose.
Note
All commands below assume that you have a shell with superuser privileges.
This tutorial will give you a CA with
A root and intermediate CA.
A browsable admin interface, protected by TLS (using certificates signed by your CA).
Certificate revocation using CRLs and OCSP.
(Optional) ACMEv2 support (= get certificates using certbot).
Requirements¶
This guide assumes that you have moderate knowledge of running servers, installing software and how TLS certificates work.
The guide assumes you use a dedicated server to set up your certificate authority and does not account for potential conflicts with other software like ports, directories or container names.
A certificate authority needs plain HTTP for CRL and OCSP access and HTTPS for ACMEv2. Using standard ports are strongly recommended (clients might fail otherwise). If you do not need any of the mentioned features, you can use all other features from the command line and do not need a web server.
Setup DNS¶
Before you set up your certificate authority, you need a domain name that points to the server where you install django-ca. Since the domain is encoded in CA certificates, it cannot be easily changed later.
This guide assumes that ca.example.com
is the name that points to the server where you are setting up your
certificate authority.
Required software¶
To run django-ca, you need Python. You will also need at least a supported database and a web server (like NGINX or Apache) to serve static files.
In our guide, we are going to run PostgreSQL as a database, Redis as a cache and NGINX as a front-facing web server . Please refer to your operating system installation instructions for how to install the software on your own.
On Debian/Ubuntu, simply do:
root@host:~# apt update
root@host:~# apt install python3 python3-venv python3-dev \
> gcc libpq-dev postgresql postgresql-client \
> redis-server nginx uwsgi uwsgi-plugin-python3
Environment¶
To make the guide less error-prone, we export the domain name for your certificate authority to
$HOSTNAME
. In all commands below assume that you have set the environment variable like this:
root@host:~# export HOSTNAME=ca.example.com
Installation¶
With this guide, you will install django-ca to /opt/django-ca/
, with your local configuration residing
in /etc/django-ca/
. You also need to create a system user to run the uWSGI application server and Celery
task worker:
root@host:~# mkdir -p /opt/django-ca/src/ /etc/django-ca/
root@host:~# adduser --system --group --disabled-login --home=/opt/django-ca/home/ django-ca
root@host:~# adduser django-ca www-data
Get the source¶
You can clone django-ca from git or download an archive from GitHub. In the example below, we extract the source to
/opt/django-ca/src/
and create a symlink without a version so that you can roll back to old versions
during an update:
root@host:~# cd /opt/django-ca/src/
root@host:/opt/django-ca/src/# wget -O django-ca-1.20.1.tar.gz \
> https://github.com/mathiasertl/django-ca/archive/refs/tags/1.20.1.tar.gz
root@host:/opt/django-ca/src/# tar xf django-ca-1.20.1.tar.gz
root@host:/opt/django-ca/src/# ln -s /opt/django-ca/src/django-ca-1.20.1 django-ca
Create a virtualenv¶
In our setup, we create a virtualenv to install the Python environment. Several tools building on virtualenv exist (e.g. pyenv or virtualenvwrapper) that you might want to try out.
Warning
Always run pip in a virtualenv or it will update system dependencies and break your system!
root@host:~# python3 -m venv /opt/django-ca/venv/
root@host:~# /opt/django-ca/venv/bin/pip install -U pip setuptools
root@host:~# /opt/django-ca/venv/bin/pip install -U PyYAML
root@host:~# /opt/django-ca/venv/bin/pip install -U -e /opt/django-ca/src/django-ca[postgres,acme,celery,redis]
PostgreSQL database¶
Create a PostgreSQL database and make sure to use a randomly generated password and keep it for later configuration:
root@host:~# openssl rand -base64 32
...
root@host:~# sudo -u postgres psql
postgres=# CREATE DATABASE django_ca;
CREATE DATABASE
postgres=# CREATE USER django_ca WITH ENCRYPTED PASSWORD 'random-password';
CREATE ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE django_ca TO django_ca;
GRANT
Add SystemD services¶
SystemD services are included with django-ca. You need to add three services, one for the uWSGI application
server (django-ca
), one for the Celery task worker (django-ca-celery
) and one for the Celery task
scheduler (django-ca-celerybeat
):
root@host:~# ln -s /opt/django-ca/src/django-ca/systemd/systemd.conf /etc/django-ca/
root@host:~# ln -s /opt/django-ca/src/django-ca/systemd/*.service /etc/systemd/system/
root@host:~# systemctl daemon-reload
root@host:~# systemctl enable django-ca django-ca-celery django-ca-celerybeat
Note that the services will not yet start due to missing configuration.
If you use an installation directory other then /opt/django-ca
, set INSTALL_BASE
in
/etc/systemd/systemd-local.conf
(see SystemD configuration) and add a SystemD override for
WorkingDirectory=
.
Configuration¶
django-ca will load configuration from all *.yaml
files in /etc/django-ca/
in alphabetical order.
These files can contain any Django setting, Celery
setting or django-ca setting.
If you (mostly) followed the above examples, you can symlink conf/source/00-settings.yaml
to
/etc/django-ca
and just override a few settings in /etc/django-ca/10-localsettings.yaml
. To create
the symlink:
root@host:~# ln -s /opt/django-ca/src/django-ca/conf/source/00-settings.yaml /etc/django-ca/
And then simply create a minimal /etc/django-ca/10-localsettings.yaml
- but you can override any other
setting here as well:
# django-ca local settings file. For more information:
# https://django-ca.readthedocs.io/en/latest/settings.html for more information.
# Default hostname to use when generating CRLs and OCSP responses
CA_DEFAULT_HOSTNAME: ca.example.com
# Enable optional ACMEv2 support. Set to false to completely disable ACMEv2 support.
#CA_ENABLE_ACME: true
# Secret key used by this installation. Generate e.g. with "openssl rand -base64 32".
SECRET_KEY: ""
# Database configuration
DATABASES:
default:
ENGINE: django.db.backends.postgresql_psycopg2
HOST: localhost
PORT: 5432
NAME: django_ca
USER: django_ca
PASSWORD: random-password
SystemD configuration¶
When you added SystemD services you also created a symlink for
/etc/django-ca/systemd.conf
. If settings there do not suit you, you can override them in
/etc/django-ca/systemd-local.conf
.
Add manage.py shortcut¶
As optional convenience, you can create a symlink to a small wrapper script that allows you to easily run
manage.py
commands. In the examples below the guide assumes you created this symlink at
/usr/local/bin/django-ca
, but of course you can name the symlink anything you like:
root@host:~# ln -s /opt/django-ca/src/django-ca/conf/source/manage /usr/local/bin/django-ca
root@host:~# django-ca check
System check identified no issues (0 silenced).
Setup Database and static files¶
Populate the database and setup the static files directory:
root@host:~# django-ca migrate
root@host:~# FORCE_USER=root django-ca collectstatic
The collectstatic
command needs to run as root.
Start¶
You can now finally start the uWSGI application server and the Celery worker (omit django-ca
service if
you do not intend to run a web server):
root@host:~# systemctl start django-ca django-ca-celery django-ca-celerybeat
Create admin user and set up CAs¶
Because we created a shortcut above above, we can use
django-ca
to use django-ca from the command line.
Custom management commands are documented in Command-line interface. You need to create a user (that can log into the admin interface) and create a root and intermediate CA:
root@host:~# django-ca createsuperuser
...
root@host:~# django-ca init_ca --pathlen=1 Root "/CN=Root"
root@host:~# django-ca init_ca --acme-enable --parent="Root" Intermediate \
> "/CN=Intermediate"
There are a few things to break down in the above commands:
The subject (
/CN=...
) in the CA is only used by browsers to display the name of a CA. It can be any human readable value and does not have to be a domain name.The first positional argument to
init_ca
, (“Root”, “Intermediate”) is just a human readable name used to identify the CA within the command-line interface and web interface. Unlike the CommonName, it must be unique.The
--pathlen=1
parameter for the root CA means that there is at most one level of intermediate CAs.
Setup NGINX¶
A web server is required for the admin interface, certificate revocation status via OCSP or CRLs and ACMEv2 (the protocol used by Let’s Encrypt/certbot integration).
Warning
While theoretically possible, do not use a local CAs ACMEv2 interface to get certificates. Any misconfiguration might make it impossible to retrieve a certificate!
In this setup, we’ll create certificates using the CA we created above. If you want to use Let’s Encrypt certificates instead, you can have a look at our Quickstart with docker-compose for an example.
Create a private/public key pair for NGINX to use:
root@host:~# openssl genrsa -out /etc/ssl/$HOSTNAME.key 4096
root@host:~# openssl req -new -key /etc/ssl/$HOSTNAME.key -out /tmp/ca.csr -utf8 -batch
root@host:~# django-ca sign_cert --ca=Intermediate --csr=/tmp/ca.csr --bundle --webserver --subject /CN=$HOSTNAME \
> > /etc/ssl/$HOSTNAME.pem
Create DH parameters:
root@host:~# mkdir -p /etc/nginx/dhparams/
root@host:~# openssl dhparam -dsaparam -out /etc/nginx/dhparams/dhparam.pem 4096
django-ca includes a template for envsubst(1) that you can use. The template assumes that you
have set $HOSTNAME
:
root@host:~# envsubst < /opt/django-ca/src/django-ca/nginx/source.template \
> > /etc/nginx/sites-available/django-ca.conf
root@host:~# ln -fs /etc/nginx/sites-available/django-ca.conf /etc/nginx/sites-enabled/
root@host:~# nginx -t
root@host:~# systemctl restart nginx
Use your CAs¶
After adding your Root CA to your system, you can use the admin interface at https://ca.example.com/admin/ with the credentials you created above to create new certificates or revoke certificates.
CRL and OCSP services are provided by default, so there is nothing you need to do to enable them.
You can use the Command-line interface for creating new CAs as well as issuing, renewing and revoking certificates. The manage.py script is available via the django-ca symlink you created above, for example:
root@host:~# django-ca list_cas
Add CA to your system¶
To get the certificate for your Root CA, you can use the admin interface or the dump_ca
command:
root@host:~# django-ca dump_ca Root > root.pem
You can add this file directly to the list of known CAs in your browser.
Distributions usually provide instructions for how to add a CA to the whole system, see for example these instructions for Debian/Ubuntu.
Use ACME with certbot¶
If you enabled ACMEv2 support, all you need to do is enable ACMEv2 for the intermediate CA using
the admin interface (or using django-ca edit_ca
). After that, you can retrieve a
certificate using a simple certbot command:
$ sudo certbot register --server https://ca.example.com/django_ca/acme/directory/
$ sudo certbot certonly --server https://ca.example.com/django_ca/acme/directory/ ...
Update¶
When updating, first check the ChangeLog for any breaking changes. Under Update you’ll also find notes on any manual steps you might need to take.
Downloading the new release works the same as before, but you have to remove the old symlink before creating the new one:
root@host:~# cd /opt/django-ca/src/
root@host:/opt/django-ca/src/# ls -l
lrwxrwxrwx ... django-ca -> /opt/django-ca/src/django-ca-1.19.1
drwxrwxr-x ... django-ca-1.19.1
root@host:/opt/django-ca/src/# wget -O django-ca-1.20.1.tar.gz \
> https://github.com/mathiasertl/django-ca/archive/refs/tags/1.20.1.tar.gz
root@host:/opt/django-ca/src/# tar xf django-ca-1.20.1.tar.gz
root@host:/opt/django-ca/src/# rm django-ca
root@host:/opt/django-ca/src/# ln -s /opt/django-ca/src/django-ca-1.20.1 django-ca
root@host:/opt/django-ca/src/# ls -l
lrwxrwxrwx ... django-ca -> /opt/django-ca/src/django-ca-1.19.2
drwxrwxr-x ... django-ca-1.19.1
drwxrwxr-x ... django-ca-1.19.2
Update the database schema and static files:
root@host:~# django-ca migrate
root@host:~# FORCE_USER=root django-ca collectstatic
Restart services:
root@host:~# systemctl restart django-ca django-ca-celery django-ca-celerybeat
Update the NGINX configuration:
root@host:~# envsubst < /opt/django-ca/src/django-ca/nginx/source.template \
> < /opt/django-ca/src/django-ca/nginx/source.template \
> > /etc/nginx/sites-available/django-ca.conf
root@host:~# nginx -t
root@host:~# systemctl restart nginx
Uninstall¶
To completely uninstall django-ca, stop related services and remove files that where created:
root@host:~# systemctl stop django-ca django-ca-celery django-ca-celerybeat
root@host:~# systemctl disable django-ca django-ca-celery django-ca-celerybeat
root@host:~# rm -f /etc/nginx/sites-*/django-ca.conf
root@host:~# rm -f /var/log/nginx/$HOSTNAME*.log
root@host:~# rm -f /usr/local/bin/django-ca
root@host:~# rm -rf /etc/django-ca/ /opt/django-ca/ /var/log/django-ca
root@host:~# rm -f /etc/ssl/$HOSTNAME.{key,pem}
Restart NGINX so that it no longer knows about the configurations:
root@host:~# systemctl restart nginx
Remove the system user:
root@host:~# deluser django-ca
Drop the PostgreSQL database:
root@host:~# sudo -u postgres psql
postgres=# DROP DATABASE django_ca;
DROP DATABASE
postgres=# DROP USER django_ca;
DROP ROLE