Securing Docker With TLS Certificates
By default, Docker has no authentication or authorization for its API, instead relying on the filesystem security of its UNIX socket,
/var/run/docker.sock, which by default is only accessible by the root user.
This is fine for the basic use case of only accessing the Docker API on the local machine via the socket as the root user. However if you wish to use the Docker API over TCP, you'll want to secure it so you don't have to give out root access to anyone that happens to poke you on the TCP port.
Docker supports using TLS certificates (both on the server and the client) to provide proof of identity. When set up correctly it will only allow clients and servers with a certificate signed by a specific CA to talk to eachother.
While not providing fine grained access permissions, it does at least allow us to listen on a TCP socket and restrict access with the bonus of also providing encryption.
In this post, I will detail what is required to secure Docker running on a CoreOS server. I will assume you already have a CoreOS server set up and running. If not, check out this previous Deis blog post covering CoreOS and VirtualBox.
I will cover two ways of creating certificates.
The first involves using
openssl to create a CA and then sign a key and certificate pair. The second way uses the paulczar/omgwtfssl Docker image, which automates the certificate creation process for you.
Either way, you'll want to start off by creating the directories for both the server and client certificate sets, like so:
Here, we're creating the keys and certificates on the server itself. Ideally, you would do this on your laptop or via config management, and never store the CA key on a public server.
Let's look at the OpenSSL way first.
openssl to create and sign a CA key and certificate:
Then, move the CA certificate to the
Next, create the
Put this inside:
This is a configuration file for OpenSSL.
Once that's done, we need a configuration file for the Docker server. So create the
/etc/docker/ssl/openssl.cnf file and put this inside:
Add any DNS or IPs that you might use to access the Docker server with. This is critical, as the Golang SSL libraries are very strict.
Next, create and sign a certificate for the client:
Then do the same for the server:
If all that looks too complex, you may want to take another approach.
The paulczar/omgwtfssl image is a small (< 10MB) Docker image built specifically for creating certificates for situations like this. Let's see how to use it.
First we'll create our client certificates and use a Docker volume binding to put the CA and certificates into the
~/.docker directory, like so:
Next we'll move them and fix the ownership:
Now we can create the server certificates using the same CA. We do this with a second volume binding to
/etc/docker/ssl, like so:
Since this is a server certificate we need to pass the IP and DNS that the server may respond to via the
Once this is done, we have our TLS certificates created, owned by the correct user, and in the correct locations.
Using the TLS certificates with Docker
Now we need to tell Docker to use the TLS certificate and verify the client. You can do this by creating a drop-in systemd unit that modifies the existing Docker systemd unit.
Create the file
/etc/systemd/system/docker.service.d/ and add the following text:
If you want to restrict local users from using the Docker UNIX socket, remove the second
-H argument. If you already have a custom drop-in unit, you can add the
--tls* arguments to it.
Reload systemd and the Docker service:
Now, when you try to access Docker via the TCP port you should get a TLS error:
This is because the Docker client doesn't know to use TLS to communicate with the server.
So, let's set some environment variables to enable TLS for the client and use the client key we created, like so:
Check it worked by running:
If you got a message like that, TLS is set up correctly and working!
In this post we looked at two ways of generating TLS key and certificate pairs that allow you to secure Docker with authentication and encryption. This allows us to use the Docker API over TCP with a significantly reduced security risk.
This post originally appeared on Paul Czarkowski's blog.