Securing a Private Docker Registry

So when I researched this a few weeks back most of the guidance I found suggested using Basic Auth. Now nothing wrong with this method as such, it works after all. However if you’re running a registry for more than one user you obviously don’t want to have just one username/password to access it. This then means having a way to add new users easily to it + “bot” users for your servers and so on.

However there is actually a much better way, using x.509 certificates (the same method used to verify and authenticate access to the docker daemon) with your own self-signed CA. Once a user has their client key and certificate setup the authentication is transparent to them. It also provides stronger security than a simple username/password combo.

The Docker docs go through the basic process for configuring this: However they don’t go through how to create your own CA. There are a few important things to keep in mind first: The security of the entire thing rests on keeping your root CA private key secret. Failure to do so means a miscreant with your private key could easily sign any number of new client certificates gaining full access to your registry. You will also need to ensure you have a process for signing new users certificate requests (CSRs), as you certainly don’t want to be generating the private key for them and emailing it over.

You may already have a a PKI implementation within your organisation that you can reuse here (such as used for providing access to build systems, internal wikis and so fourth), if not you have a few choices: You can either generate the Root CA using OpenSSL (or another encryption toolkit) and manage it all your self or use a PKI system, such as Cloudflare’s CFSSL lib which allows to specify all the config and CSRs in json files.

If you wanted to use OpenSSL here is a quick and dirty guide on how you would go about this:

Create a new empty directory to create the root CA in then create the basic dir structure and required files:

mkdir certs crl newcerts private
chmod 700 private
touch index.txt
echo 1000 > serial

Openssl Configuration:
Copy your systems default openssl.cnf file to the cwd and name it something like: “openssl-ca.cnf”. Then edit it to setup the CA and client sections (un-commenting and editing lines where appropriate):

[ usr_cert ]
nsCertType = client
nsComment = "Docker Registry CA"
[ v3_ca ]
basicConstraints = critical,CA:true
keyUsage = cRLSign, keyCertSign

And change the default directory to the current one:

[ CA_default ]
dir = . # Where everything is kept

The usr_cert section is used when creating the client certificates, the v3_ca is used for the CA creation, it simply configs the type of cert we want it what we want it it be usable for.

Generating the Private Key:

openssl genrsa -aes256 -out private/docker_ca.key 4096

Self-signing the certificate to be usable as a CA:

openssl req -new -x509 -days 3650 -key private/docker_ca.key -sha256 -extensions v3_ca -out certs/docker_ca.crt -config openssl-ca.cnf

Now you can go about signing CSRs for the client certificates:

Sign a signing request using your CA key:

openssl ca -keyfile private/docker_ca.key -cert certs/docker_ca.crt -extensions usr_cert -notext -md sha256 -in certs/sebdangerfield.csr -out certs/sebdangerfield.crt -config openssl-ca.cnf -days 366


If when you try and authenticate with your Docker Registry Docker thinks the private key and certificate don’t match make sure your client certificate is the first one in your client.cert file (i.e. the certs making up the chain come after).

NSS error -8023 using AWS SDK for PHP

Please note this fix should also work on Fedora, CentOS and Redhat linux distros if you are seeing the NSS error -8023 when using CURL and PHP.

At work we came up against this odd error message from Amazon Web Services(AWS) SDK for PHP when using the SDK in a forked process on the AWS built AMI:

PHP Fatal error:  Uncaught exception 'cURL_Exception' with message 'cURL resource: Resource id #50; cURL error: SSL connect error (cURL error code 35). See for an explanation of error codes.' in /usr/share/pear/AWSSDKforPHP/lib/requestcore/requestcore.class.php:829

Stack trace:
#0 /usr/share/pear/AWSSDKforPHP/sdk.class.php(1035): RequestCore->send_request()
#1 /usr/share/pear/AWSSDKforPHP/services/swf.class.php(1305): CFRuntime->authenticate('TerminateWorkfl...', Array)
#2 ....php(189): AmazonSWF->terminate_workflow_execution(Array)
#3 ....php(83): daemon->checkSWFExecutions()
#4 ....php(350): daemon->run()
#5 {main}
  thrown in /usr/share/pear/AWSSDKforPHP/lib/requestcore/requestcore.class.php on line 829

Now, cURL error 35 means “A problem occurred somewhere in the SSL/TLS handshake. You really want the error buffer and read the message there as it pinpoints the problem slightly more. Could be certificates (file formats, paths, permissions), passwords, and others.” which is a bit vague and didn’t really help. After setting the CURLOPT_VERBOSE flag in the AWS SDK for PHP we were able to see the real error message:

NSS error -8023

Continue reading