Thursday, December 29, 2016

Using Docker with your own Certificate Authority

You should be able to find several free Certificate Authorities in the wild. The most popular are probably Let's Encrypt or CACert. However, if for any reasons (restrictions, use of localhost, ability to automate...), you prefer to manage your own Certificate Authority, you will probably consider OpenSSL as a nice toolkit to build it. This blog presents a few useful commands. They are not intended, by any mean, to replace the official documentation but it might help to speed-up your initial setup and different scenarios.

If you own a CA, you could use it a lot with Docker... or not. I mean it could help. For instance, you could use it to secure a vault to store and share secrets. You could rely on a PKI to authenticate clients ans servers. You could also use it to encrypt HTTP protocol, including the access to your private registry. This blog will explore the latter scenario to illustrate how you can leverage your newly created Certificate Authority.
Important Note:
The example below relies on Fedora/Enterprise Linux; if you plan to use Debian, another Linux distribution, Windows or OSX, you should have to adapt the procedure to make it work.
2nd Important Note:
If you are managing keys and certificates, it is important you don't share any key/password. The example below is just "an example". Change the passwords from xxxxxx to something more reliable.

Your Certificate Authority

OpenSSL provides a Perl script named that helps with creating and managing a CA. Obviously you can manage all the tasks manually but it's way easier with that script. To install the script run the command below (use yum instead of dnf if you are on a EL distribution):
sudo dnf -y install openssl-perl
Once done, you can simply create a new CA with the command below:
sudo /etc/pki/tls/misc/ -newca
CA certificate filename (or enter to create)

Making CA certificate ...
Generating a 2048 bit RSA private key
writing new private key to '/etc/pki/CA/private/cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [XX]:FR
State or Province Name (full name) []:Ile-de-France
Locality Name (eg, city) [Default City]:Paris
Organization Name (eg, company) [Default Company Ltd]:Resetlogs
Organizational Unit Name (eg, section) []:Security
Common Name (eg, your name or your server's hostname) []
Email Address []

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:xxxx
An optional company name []:Resetlogs
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            Not Before: Dec 27 21:28:03 2016 GMT
            Not After : Dec 27 21:28:03 2019 GMT
            countryName               = FR
            stateOrProvinceName       = Ile-de-France
            organizationName          = Resetlogs
            organizationalUnitName    = Security
            commonName                =
            emailAddress              =
        X509v3 extensions:
            X509v3 Subject Key Identifier:
            X509v3 Authority Key Identifier:

            X509v3 Basic Constraints:
Certificate is to be certified until Dec 27 21:28:03 2019 GMT (1095 days)

Write out database with 1 new entries
Data Base Updated

Among many things, this script does, there are 2 important things:
  • It creates a private root key stored in /etc/pki/CA/private. You can (1) check it is usable by running the command below, (2) save it as it is required to sign your certificate requests and (3) secure it as anyone that can access the key should be able to sign a requests:
sudo openssl pkey -in /etc/pki/CA/private/cakey.pem
  • It creates a root self-signed certificate in /etc/pki/CA that you will be able to distribute and use to authenticate the Certificate Authority you've just created. You can verify the certificate with the command below:
openssl x509 -noout -text -in /etc/pki/CA/cacert.pem

Certificate Request

Any Certificate Requests should be generated on the server that need them, or at least close to them. This would prevent the Certificate Authority from accessing the private key the request also generates. In order to create the request, create a configuration file like the one below:
cat req.conf
default_bits           = 2048
distinguished_name     = req_distinguished_name
prompt                 = no
output_password        = xxxxxx
req_extensions         = req_ext

C                      = FR
ST                     = Ile-de-France
L                      = Paris
O                      = Resetlogs
OU                     = Security Team
CN                     =
emailAddress           =

subjectAltName         = DNS:localhost,IP:
Once you've prepared your request, simply run openssl like below:
openssl req -newkey rsa:2048 -days 365 -config req.conf -out newreq.pem \
   -keyout newkey.pem
You should be able to display the content of the request before you send it to the CA:
openssl req -in newreq.pem -noout -text
You may need to access an unprotected version of the key. If that's the case, you could have used the -nodes option when building it. You can also generate an unprotected version of it with the command below:
openssl rsa -in newkey.pem -out newkey-unprotected.pem

Signing the Certificate Request

In the request above, subjectAltName is used to designate the name of the server the certificate refers to. This is "now" the standard way to perform that task. When you sign the Certificate Request, you should reference subjectAltName again.

By default, does not handle that extension. You could obviously use the "openssl ca" command to address that limit. However, if you want to continue working with, you can also modify /etc/pki/tls/openssl.cnf as explained in this blog; I slightly changed it to match the configuration that comes with OpenSSL on my Fedora (25):
  • At the top of the file, below the "HOME =" line, add:
SAN = ""
  • At the bottom of the [ usr_cert ] section, add:
subjectAltName = $ENV::SAN
Once that configuration set, the subjectAltName used when signing is inherited from the SAN environment variable. So, to sign the request, all you need to do is to push the newreq.pem file to the Certificate Authority Server and generate the certificate, as root, by running the command below:
SAN="DNS:localhost,IP:" /etc/pki/tls/misc/ -sign
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            Not Before: Dec 28 21:57:44 2016 GMT
            Not After : Dec 28 21:57:44 2017 GMT
            countryName               = FR
            stateOrProvinceName       = Ile-de-France
            localityName              = Paris
            organizationName          = Resetlogs
            organizationalUnitName    = Security Team
            commonName                =
            emailAddress              =
        X509v3 extensions:
            X509v3 Basic Constraints: 
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
            X509v3 Authority Key Identifier: 

            X509v3 Subject Alternative Name: 
                DNS:localhost, IP Address:
Certificate is to be certified until Dec 28 21:57:44 2017 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
Signed certificate is in newcert.pem
You can check the content of the certificate as well as the signature chain and the Subject Alternative Name with the command below:
openssl x509 -noout -text -in newcert.pem

Docker HTTPS-Enabled Registry

Lets put the certificate file (newcert.pem) as well as the unprotected version of the key (newkey-unprotected.pem) in a directory named `pwd`/certs. To start a new HTTPS-Enabled Docker Registry, you can simply run the command below:
docker run -d -p 5000:5000 --restart=always --name registry \
  --security-opt label=disable -v `pwd`/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/newcert.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/newkey-unprotected.pem \

Register the CA Certificate as a Trusted Authority in your browser:

You should be able to access the registry REST API from HTTPS and, even if you are using one of the subjectAltName of the certificate rather than the CommonName, i.e. from localhost rather than, it should succeed:

Next Steps...

You might soon find several benefits from using your own CA with Docker even if starting does not always look so trivial. As you can see from above, it's not very complex either. It is also worth mentioning, you may find other methods to deal with managing a CA, including Easy-RSA or using the openssl ca command directly. The latter has several useful options like "-passin" to fully automate the whole management process. For now, you've just configured and secured a very basic environment. New ideas might come soon...

No comments:

Post a Comment