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.
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 CA.pl 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-perlOnce done, you can simply create a new CA with the command below:
sudo /etc/pki/tls/misc/CA.pl -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) :www.resetlogs.com Email Address :firstname.lastname@example.org 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: 9b:d3:58:97:a6:0b:f5:88 Validity Not Before: Dec 27 21:28:03 2016 GMT Not After : Dec 27 21:28:03 2019 GMT Subject: countryName = FR stateOrProvinceName = Ile-de-France organizationName = Resetlogs organizationalUnitName = Security commonName = www.resetlogs.com emailAddress = email@example.com X509v3 extensions: X509v3 Subject Key Identifier: BB:3E:C6:50:F9:CC:73:F9:E0:9C:65:96:FF:39:D2:D5:DA:BF:29:82 X509v3 Authority Key Identifier: keyid:BB:3E:C6:50:F9:CC:73:F9:E0:9C:65:96:FF:39:D2:D5:DA:BF:29:82 X509v3 Basic Constraints: CA:TRUE Certificate is to be certified until Dec 27 21:28:03 2019 GMT (1095 days) Write out database with 1 new entries Data Base UpdatedAmong 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/CAthat 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
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 [req] default_bits = 2048 distinguished_name = req_distinguished_name prompt = no output_password = xxxxxx req_extensions = req_ext [req_distinguished_name] C = FR ST = Ile-de-France L = Paris O = Resetlogs OU = Security Team CN = blue.resetlogs.com emailAddress = firstname.lastname@example.org [req_ext] subjectAltName = DNS:localhost,IP:127.0.0.1Once 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.pemYou should be able to display the content of the request before you send it to the CA:
openssl req -in newreq.pem -noout -textYou 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 RequestIn 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, CA.pl 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 CA.pl, 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 = "email:email@example.com"
- At the bottom of the [ usr_cert ] section, add:
subjectAltName = $ENV::SANOnce 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:127.0.0.1" /etc/pki/tls/misc/CA.pl -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: 88:aa:5a:00:30:56:3e:0d Validity Not Before: Dec 28 21:57:44 2016 GMT Not After : Dec 28 21:57:44 2017 GMT Subject: countryName = FR stateOrProvinceName = Ile-de-France localityName = Paris organizationName = Resetlogs organizationalUnitName = Security Team commonName = blue.resetlogs.com emailAddress = firstname.lastname@example.org X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 75:6C:E7:65:30:0B:EB:27:19:09:35:71:05:A6:60:41:CF:A9:88:74 X509v3 Authority Key Identifier: keyid:B3:81:18:0B:81:B8:A1:81:7F:37:F7:EF:90:AC:10:40:9C:AB:4C:C6 X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1 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.pemYou 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 RegistryLets 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 \ registry:2
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 blue.resetlogs.com, it should succeed: