I'm preparing my docker-based infrastructure and one of the items (to make my live a bit more complicated) is to install a private docker registry. One of the requirements is to obtain a certificate (so I don't have an insecure registry).

Create a self-signed certificate

First, we need to establish our requirements:

  • Certificate is self-signed (for officially signed certificates, the process is different)
  • We need the certificate to be for a domain and all its subdomains
  • We might need to add an IP address too

Now that we have this isn place, let's build a configuration file (openssl accepts config files!). I'll show the file in its entirety and then explain its components:

# file: selfsigned.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req

[req_distinguished_name]
countryName = BE
country_name_default = BE
stateOrProvinceName = Belgium
stateOrProvinceName_default = Belgium
localityName = Belgium
localityName_default = Belgium
organizationalUnitName = LaurIvan
organizationalUnitName_default = LaurIvan
commonName = *.domain.com
commonName_max = 64

[v3_req]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = *.domain.com
IP.1 = 192.168.1.200

Components are:

  • The req component is like the toot of the tree of options which will make up the req command of openssl.
  • The req_distinguished_name component contains all the (default) parameters for the distinguished name (the ones you get asked when you create a CSR)
  • The v3_req has all the parameters for our certificate (CRT)
  • alt_names has all the names accepted by the certificate. This will allow us that subdomains are accepted and also a specific IP address (e.g. where my docker registry is)

Create the certificate

First, we create the private key:

sudo openssl genrsa -out domain.com.key 2048

Then, we create the CSR, with our self-signed configuration:

sudo openssl req -new -out domain.com.csr \
  -key domain.com.key \
  -config selfsigned.cnf

Lastly, we create the CRT:

sudo openssl x509 -req -days 3650 \
  -signkey domain.com.key \
  -in domain.com.csr \
  -extensions v3_req -extfile selfsigned.cnf \
  -out domain.com.crt

Initial errors

To note that I already have a self-signed certificate, but while playing around, I've got a number of errors...

First one was:

x509: certificate signed by unknown authority

I passed it by adding the certificate into my build machine's list of docker certificates:

  1. Create a docker folder with your server's address:

    sudo mkdir -p /etc/docker/certs.d/[IP Address]:[Port]/
    
  2. Copy the certificate used by the registry there:

sudo scp \
    laur@[server]:/etc/certificates/[registry_cert].crt  \
    /etc/docker/certs.d/[IP Address]:[Port]/

A second error was:

x509: cannot validate certificate for <ip-address> because it doesn't contain any IP SANs

For this one, I had do add the IP address in the alt_names section of the self-signed config file.

Next steps:

I have the following next steps in mind:

  1. Use a reverse proxy (working with the IP address of the registry server is not nice)
  2. Create an ansible role to do it for me in a reproducible way (and in a way I can add the certificates everywhere I need :)
  3. Use a different certificate like let's encrypt.

HTH,