Sunday, November 20, 2016

TLS for Docker in Ubuntu 16.04

This is my first blog ever. So you may find some mistakes. Please let me know where I can improve in the comment section.

I do not have any prior experience or knowledge about SSL. So I had to got through a lot of trouble to secure my docker platform. If you are using the same OS (Ubuntu 16.04), I hope this blog will be able to save your time. For details information please check the link in the first source(below).


Generate CA private and public key:

openssl genrsa -aes256 -out ca-key.pem 4096
choose your password for the ca-key.pem file. Now use this private key to generate the public key i.e. the digital certificate for the CA using the following command:
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
You will be asked for different info. The most important info is "Common Name (e.g. server FQDN or YOUR name)". To find the FQDN of your server use the following command-
hostname -f  
    If you use an environment variable for the 'CN' then make sure that in the certificate the CN name is exactly what you wanted. After generating the certificate you can verify the 'CN' name by using the following command:
openssl x509 -in cert.pem -noout -text
This will give you detail information about the certificate.


Generate Server private key and public key:

 Generate the private key for the server
 openssl genrsa -out server-key.pem 4096
Generate CSR(Certificate Signing Request) for the server
openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
Replace the variable $HOST with the DNS name of your Docker Daemon's host. Now create the certificate for the server which is signed by our CA. Since TLS connection can be made via IP addres as well as DNS name, they need to be specified when creating the certificate. For example, to allow connection using 0.0.0.0 and 127.0.0.1:
echo subjectAltName = IP:0.0.0.0,IP:127.0.0.1 > extfile.cnf
Now we will use this extfile.cnf and the server.csr to create a public key for the server which is signed by our CA
sudo openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey \ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf

Generate Client private key and public key:

  Generate the private key for the client-
openssl genrsa -out key.pem 4096
Create client CSR
openssl req -subj "/CN=$client" -new -key key.pem -out client.csr
Here $client is the FQDN or the DNS name of the client machine.
Create the key config file for the client certificate
echo extendedKeyUsage = serverAuth,clientAuth > extfile.cnf
If you put only clientAuth, you will get an error("x509: certificate specifies an incompatible key usage") while using the client to communicate with the daemon. This error occurs because of the way go library handles certificate.

Sign the client public key
sudo openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey \ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf

After generating both server and client certificate you can remove the server.csr and client.csr file
rm -v client.csr server.csr

Modify the file permission of the private keys and the certificates
chmod -v 0400 ca-key.pem key.pem server-key.pemchmod -v 0444 ca.pem server-cert.pem cert.pem

  Now you can make the docker daemon only accept connections from clients providing a certificate trusted by our CA:

dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem \
--tlskey=server-key.pem -H=0.0.0.0:2376

From the client you can communicate with the daemon using proper certificate
docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \-H=$HOST:2376 version

Secure by default:

In order to secure the client connection by default, copy all the required certificates to ~/.docker directory and set DOKCER_HOST and DOCKER_TLS_VERIFY variable.


mkdir -pv ~/.dockercp {ca,cert,key}.pem ~/.docker/export DOKCER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1
Now you can talk with the daemon using the client on 2376 port. Try
docker ps
If you want to store your keys in another location you can use the DOCKER_CERT_PATH varialble
export DOCKER_CERT_PATH=~/your_perferred_locationdocker --tlsverify ps
You can now use curl to make secure API request as below

curl https://$HOST:2376/images/json \--cert ~/.docker/cert.pem \--key ~/.docker/key.pem \--cacert ~/.docker/ca.pem

Sources:
https://docs.docker.com/engine/security/https/
https://groups.google.com/forum/#!topic/consul-tool/C_5LZMR3yHQ