Docker is an excellent way to manage and separate your infrastructure concerns. You get most of the advantages of splitting workloads into virtual machines while avoiding most of the disadvantages that go along with it. As Docker continues to mature, they have added a pile of functionality to make your life easier from an Engineering and Operations standpoint.
One of the recent features finally allows simple remote access to your Docker daemon, with high strength security through the use of TLS and Client Certificates.
TLS is Transport Layer Security, basically a standard way of enabling encryption on any type of socket. TLS is the successor to SSL, and is improved in every practical way. One of the features of TLS is the ability to verify both sides (client and server) of the connection, rather than just one side (server) as typically used for things like HTTPS.
Both your Client and Server certificates must be signed by a "known" certificate authority (CA). In this case, your CA acts as the arbiter of trust, and any client with a signed certificate will be permitted access. Docker (currently) has no mechanisms for users or passwords, and does not currently support CRLs (Certificate Revocation Lists), so you need to protect those certificates and be prepared to rebuild your TLS infrastructure from scratch (with new keys for every server and client) if a Client Certificate is compromised.
Now, most of us aren't OpenSSL experts that can sign certificates as easily as ordering a coffee. With this in mind, I created DockerCertManager, which simplifies the creation and management of your keys and certificates. For the sake of brevity, I will assume that you are using DockerCertManager.
Your first step is to initialize your Certificate Authority. You should only need to do this once, and DockerCertManager will try to prevent you from overwriting an existing CA.
./DockerCertManager initca
You will now have a Certificate Authority. Be very careful to protect ca-key.pem, as it any certificate signed by it will be trusted by your client and server. the ca.pem is your public key and can be safely distributed. Next, you need a Server certificate. For this example, we'll use docker01.example.com.
./DockerCertmanager server docker01.example.com
This will create your private key and certificate file, and recommend how to name and install them. I suggest copying them to /etc/docker on your server with the names key.pem, cert.pem, and ca.pem, and chmod them to 400. Next, you'll need to add the configuration to your Docker startup environment file. On Ubuntu it's /etc/default/docker, and you'll edit your DOCKER_OPTS to look like so:
DOCKER_OPTS="--dns 192.168.1.1 --tlsverify -H=unix:///var/run/docker.sock -H=0.0.0.0:4243 --tlscacert=/etc/docker/ca.pem --tlscert=/etc/docker/cert.pem --tlskey=/etc/docker/key.pem"
Applying your configuration requires a restart of the Docker daemon, which will shut down any containers you may have running. After that, you can create a client certificate. Your Client Certificate identifies a user or account, but please note that as of today Docker does not include any concept of users, so all certificate holders are equal. For our example, we'll set up our friend Joe User who uses the username joeuser.
./DockerCertManager client joeuser
Your client certificates will now be created, along with a suggestion on how to install them. Docker will simplify things by looking for specific filenames in ~/.docker, so I suggest you follow the suggestion. Copy your client certificate, key, and CA public key into ~/.docker as key.pem, cert.pem and ca.pem, and chmod them to 400.
You should now be ready to test your TLS connection to your server. In our example, you can issue commands to our server by adding --tlsverify and -H hostname:4243 to our docker command, like such:docker --tlsverify -H docker01.example.com:4243 version
You can also use an environment to specity to always use --tlsauth and/or a specific host:
export DOCKER_HOST=docker01.example.com:4243
export DOCKER_TLS_VERIFY=1
docker version
So there you have it! You should have a secure, encrypted channel to interact dcirectly with your Docker daemons on remote hosts! It doesn't (currently) get any better than this!
With all that done, the caveats list...
First, Docker authorization is either yes or no, there are no access levels. Anyone with a signed certificate is a fully trusted administrator of your docker hosts. You have to keep that CA key private.
Second, Docker doesn't support certificate revocation lists. If a client certificate gets leaked, or an employee leaves, or whatever, you can't just remove that key's access. Your only option is to build your certificates from the ground up, starting with a new Certificate Authority.
Third, to use Docker's built-in automatic filenames, you're restricted to a single set of certificates. That means that if you have multiple environments (think dev/test/prod), you're either using the same certificate for all of them or you're changing your docker config per-environment. In this case, I recommend you use shell aliases:
alias dockerprod="docker --tlsverify --tlscacert=/home/joeuser/.docker/prod-ca.pem --tlscert=/home/joeuser/.docker/prod-cert.pem --tlskey=/home/joeuser/.docker/prod-key.pem"
... And similar for other environments.