Quickstart with docker-compose¶
This guide provides instructions for running your own certificate authority using docker-compose. This is the quickest and easiest way to run django-ca, especially if you do not care to much about custom configuration or extending django-ca.
This tutorial will give you a CA with
A root and intermediate CA.
A browsable admin interface, protected by TLS (using Let’s Encrypt certificates).
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 Docker (at least version 19.03.0) and Docker Compose (at least version 1.27.0). You also need certbot to acquire Let’s Encrypt certificates for the admin interface. OpenSSL is used to generate the DH parameter file. On Debian/Ubuntu, simply do:
user@host:~$ sudo apt update
user@host:~$ sudo apt install docker.io docker-compose certbot openssl
For a different OS, please read Install Docker, Install docker-compose and Get certbot.
If you want to run docker(-compose) as a regular user, you need to add your user to the docker
group and
log in again:
user@host:~$ sudo adduser `id -un` docker
user@host:~$ sudo su `id -un`
Get initial certificates¶
Use certbot to acquire initial certificates. This must be done before you run docker-compose, as both bind to port 80 (HTTP).
user@host:~$ sudo certbot certonly --standalone -d ca.example.com
...
user@host:~$ sudo ls /etc/letsencrypt/live/ca.example.com
README cert.pem chain.pem fullchain.pem privkey.pem
Get configuration¶
Warning
Upgrading from before django-ca 1.19? Please see the update notes before proceeding.
Note that the files for older versions in the documentation for 1.19 or later have already been updated.
Docker-compose needs a configuration file, docker-compose.yml
. You
can also download the file for other versions from github.
Note
Because of how docker-compose works, it is better to put the file in a sub-directory and not directly into
your home directory. We assume you put all files into ~/ca/
from now on.
You can also get versions for specific versions of django-ca from the table below, which also shows bundled third-party Docker images.
Version |
Redis |
PostgreSQL |
NGINX |
---|---|---|---|
7 |
12 |
1.20 |
|
6 |
12 |
1.20 |
|
6 |
12 |
1.20 |
|
6 |
12 |
1.20 |
|
6 |
12 |
1.18 |
Add docker-compose.override.yml
¶
The default docker-compose.yml
does not offer HTTPS, because too many details (cert location, etc.)
are different from system to system. We need to add a docker-compose override file to open the port and map the directories with the certificates
into the container. Simply add a file called docker-compose.override.yml
next to your main
configuration file:
services:
webserver:
volumes:
- /etc/letsencrypt/live/${DJANGO_CA_CA_DEFAULT_HOSTNAME}:/etc/certs/live/${DJANGO_CA_CA_DEFAULT_HOSTNAME}/
- /etc/letsencrypt/archive/${DJANGO_CA_CA_DEFAULT_HOSTNAME}:/etc/certs/archive/${DJANGO_CA_CA_DEFAULT_HOSTNAME}/
- ./dhparam.pem:/etc/nginx/dhparams/dhparam.pem
- ./acme/:/usr/share/django-ca/acme/
ports:
- 443:443
This will work if you get your certificates using certbot
or a similar client. If your private key in
public key chain is named different, you can set NGINX_PRIVATE_KEY
and NGINX_PUBLIC_KEY
in your
.env
file below.
Add .env
file¶
Some settings in django-ca can be configured with environment variables (except where a more complex
structure is required). Simply create a file called .env
next to docker-compose.yaml
.
For a quick start, there are only a few variables you need to specify:
# The hostname for your CA.
# WARNING: Changing this requires new CAs (because the hostname goes into the certificates).
DJANGO_CA_CA_DEFAULT_HOSTNAME=ca.example.com
# PostgreSQL superuser password (required by the Docker image), see also:
# https://hub.docker.com/_/postgres
#
# Generate a secure password e.g. with "openssl rand -base64 32"
POSTGRES_PASSWORD=mysecretpassword
# NGINX TLS configuration
NGINX_TEMPLATE=tls
NGINX_PRIVATE_KEY=/etc/certs/live/ca.example.com/privkey.pem
NGINX_PUBLIC_KEY=/etc/certs/live/ca.example.com/fullchain.pem
Generate DH parameters¶
The TLS configuration also requires that you generate a DH parameter file, used by some TLS ciphers. You can generate it with:
user@host:~/ca/$ openssl dhparam -dsaparam -out dhparam.pem 4096
Customization¶
Although the defaults are fine for most scenarios, django-ca and Django itself support a wide range of settings to customize your installation. Django has its settings documented under Settings, django-ca settings are documented under custom settings.
Just like when using the plain Docker container, you can configure django-ca using either environment
variables (set in e.g. docker-compose.override.yml
) or using an extra YAML configuration file. For more
details on how to configure the Docker container, refer to Initial configuration.
Note
In our docker-compose setup, django-ca is used in both the backend
and frontend
containers. Make
sure you configure both of them.
Configuration using a YAML configuration file¶
Using an extra configuration file is the most flexible way to configure django-ca, as it allows you to update even complex settings. It has the added advantage that docker-compose will not recreate the containers if you update the configuration.
As with the normal docker container, django-ca will read configuration files in
/usr/src/django-ca/ca/conf/
in alphabetical order, but it will also read files in the subfolder
/usr/src/django-ca/conf/ca/compose/
, which provides configuration specific to our docker-compose setup.
To add a configuration file, first add a volume mapping in your docker-compose.override.yml
:
services:
backend:
volumes:
- ./localsettings.yaml:/usr/src/django-ca/ca/conf/compose/99-localsettings.yaml
frontend:
volumes:
- ./localsettings.yaml:/usr/src/django-ca/ca/conf/compose/99-localsettings.yaml
webserver:
# same as before...
… and then simply add a file called localsettings.yaml
in your current directory, for example:
# (Example) Configure a custom SMTP server
#
# See also:
# https://docs.djangoproject.com/en/4.0/topics/email/
SMTP_HOST: smtp.example.coma
# Set a custom default key size for new certificate authorities
CA_DEFAULT_KEY_SIZE: 2048
Configuration using environment variables¶
If you want to use environment variables for configuration, we recommend you first add them to your
docker-compose.override.yml
, for example to configure a different SMTP server for sending out emails:
services:
backend:
environment:
DJANGO_CA_EMAIL_HOST:
frontend:
environment:
DJANGO_CA_EMAIL_HOST:
webserver:
# same as before...
and in your .env
file, set the variable:
DJANGO_CA_EMAIL_HOST=smtp.example.com
Recap¶
By now, you should have at least four files in ~/ca/
:
user@host:~/ca/$ ls -A
docker-compose.yml docker-compose.override.yml .env dhparam.pem
Start your CA¶
Now, you can start django-ca for the first time. Inside the folder with all your configuration, run docker-compose (and verify that everything is running):
user@host:~/ca/$ docker-compose up -d
...
Creating ca_backend_1 ... done
Creating ca_webserver_1 ... done
user@host:~/ca/$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------
ca_backend_1 ./celery.sh -l info Up
ca_cache_1 docker-entrypoint.sh redis ... Up
ca_db_1 docker-entrypoint.sh postgres Up
ca_frontend_1 /bin/sh -c ./uwsgi.sh Up
ca_webserver_1 /docker-entrypoint.sh /bin ... Up 0.0.0.0:443->443/tcp,...
By now, you should be able to see the admin interface (but not log in yet - you haven’t created a user yet). Simply go to https://ca.example.com/admin/.
Verify setup¶
You can run the deployment checks for your setup, which should not return any issues:
user@host:~/ca/$ docker-compose exec backend manage check --deploy
System check identified no issues (4 silenced).
user@host:~/ca/$ docker-compose exec frontend manage check --deploy
System check identified no issues (4 silenced).
Create admin user and set up CAs¶
Inside the backend container, manage
is an alias for manage.py
.
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:
user@host:~/ca/$ docker-compose exec backend manage createsuperuser
user@host:~/ca/$ docker-compose exec backend manage init_ca --pathlen=1 \
> Root /CN=Root
user@host:~/ca/$ docker-compose exec backend manage init_ca \
> --path=ca/shared/ --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
--path=ca/shared/
parameter for the intermediate CA means that you can use the admin interface to issue certificates. Without it, the web server has no access to the private key for your CA.The
--pathlen=1
parameter for the root CA means that there is at most one level of intermediate CAs.
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. You cannot issue certificates with the “Root” CA, since you did not pass
--path=ca/shared/
when creating it.
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 can be invoked via docker-compose exec backend manage
, for example:
user@host:~/ca/$ docker-compose exec backend manage list_cas
<serial> - Intermediate
<serial> - Root
To sign a certificate from the command line, simply invoke the sign_cert
command.
user@host:~/ca/$ docker-compose exec backend manage sign_cert \
> --ca=Intermediate --subject="/CN=example.com"
Please paste the CSR:
-----BEGIN CERTIFICATE REQUEST-----
...
To pass data from stdin, for example to sign a certificate non-interactively by passing the CSR from stdin,
you need to pass the -T
parameter to docker-compose exec:
user@host:~/ca/$ openssl genrsa -out example.com.key 4096
Generating RSA private key, ...
user@host:~/ca/$ openssl req -new -key example.com.key -out example.com.csr \
> -utf8 -batch -subj /CN=example.com
user@host:~/ca/$ cat example.com.csr | docker-compose exec -T backend \
> manage sign_cert --ca=Intermediate --subject="/CN=example.com"
Add CA to your system¶
To get the certificate for your Root CA, you can use the admin interface or the dump_ca
command:
user@host:~/ca/$ docker-compose exec backend manage 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 docker-compose exec backend manage 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/ ...
Backup¶
To backup your data, you need to store the PostgreSQL database and the private key files for your certificate authorities.
If possible for you, you can first stop the frontend
and backend
containers to make absolutely sure
that you have a consistent backup:
user@host:~/ca/$ docker-compose stop frontend
user@host:~/ca/$ docker-compose stop backend
Create a database backup:
user@host:~/ca/$ docker-compose exec db pg_dump -U postgres postgres > db.backup.sql
Backing up Docker volumes is not as straight forward as maybe it should be, please see the official documentation for more information.
You should always backup /var/lib/django-ca/certs/
from both the backend
and the frontend
container.
Here is an example that should work for the backend
container.:
user@host:~/ca/$ docker run -it --rm --volumes-from `basename $PWD`_backend_1 \
> -v `pwd`:/backup ubuntu tar czf /backup/backend.tar.gz /var/lib/django-ca/certs/
user@host:~/ca/$ tar tf backend.tar.gz
var/lib/django-ca/certs/
var/lib/django-ca/certs/ca/
var/lib/django-ca/certs/ca/1BBB69C1D3B64AB5EF39C2946015F57A0FB04107.key
var/lib/django-ca/certs/ca/shared/
var/lib/django-ca/certs/ca/shared/secret_key
...
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.
Warning
Updating from django-ca 1.18.0 or earlier? Please see Update from 1.18 or earlier.
Remember to backup your data before you perform any update.
In general, updating django-ca is done by getting the latest version of docker-compose.yml and then simply recreating the containers:
user@host:~/ca/$ curl -O https://.../docker-compose.yml
user@host:~/ca/$ docker-compose up -d