Docker’s API is completely unprotected by default except for filesystem permissions on its Unix socket. You should set up TLS when exposing the Docker API over TCP so Docker Engine and your clients can verify each others’ identity. Otherwise anyone with access to the TCP port could browse your Docker containers, start new ones, and run actions as
root on your system.
Configured TLS will require clients to present a valid certificate that’s signed by the server’s certificate authority. To get it working, you need to create SSL certificates, then set up Docker Engine to require TLS connections. Docker CLI clients must also be adjusted to expect a TLS server.
Exposing the TCP Socket
You can expose Docker’s TCP socket by using the
-H flag to define an extra endpoint when starting the
dockerd process. This flag can be repeated multiple times; in this example, both the Unix socket and TCP socket will be available:
/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375
Port 2375 is conventionally used for unencrypted Docker connections. Port 2376 should be used instead once TLS has been set up.
You can configure Docker to use these flags automatically by modifying your Docker service definition. Add an override in
/etc/systemd/system/docker.service.d/override.conf that changes the
[Service] ExecStart=/usr/bin/dockerd -H ...
Reload systemd to apply the change:
sudo systemctl daemon-reload
Creating Your Certificate Authority
Begin by creating a Certificate Authority (CA) for your TLS configuration. You’ll use this CA to sign your certificates; the server will refuse to communicate with clients that present a certificate from a different CA.
Use OpenSSL to generate private and public CA keys on the machine hosting your Docker server:
# Generate the private key openssl genrsa -aes256 -out ca-private.pem 4096 # Generate a public key from the private key openssl req -new -x509 -days 365 -key ca-private.pem -sha256 -out ca-public.pem
You’ll be prompted to supply a passphrase, email address, country code, state and city names, and organizational name to include with your public key. Enter the information in your terminal, pressing enter after each line to progress forwards and create the key.
Generating a Server Key and Certificate Signing Request
Next create a server key and a certificate signing request:
# Generate the server key openssl genrsa -out server-key.pem 4096 # Generate a certificate signing request openssl req -subj "/CN=example.com" -sha256 -new -key server-key.pm -out request.csr
The certificate signing request (CSR) contains all the information needed to produce a signed certificate. It’s important to check the common name in the CSR is correct for your server. This is specified in the
CN field as
example.com above; you should set it to the Fully Qualified Domain Name (FQDN) for your server.
Setting Up Certificate Extensions
Using this CSR would permit connections to the server via its FQDN. You need to specify certificate extensions if you want to add another domain or use an IP address. Create an extensions file with
extendedKeyUsage fields to set this up:
echo subjectAltName = DNS:sub.example.com;IP=192.168.0.1 >> extfile.cnf echo extendedKeyUsage = serverAuth >> extFile.cnf
This example would additionally permit connections via
Generating a Signed Certificate
Now you’re ready to combine all the components and generate a signed certificate:
openssl x509 -req -days 365 -sha256 -in request.csr -CA ca-public.pem -CAkey ca-private.pem -CAcreateserial -extfile extfile.cnf -out certificate.pem
This takes the certificate signing request, adds your extension file, and uses your CA’s keys to produce a signed OpenSSL certificate. You’ll need to supply the CA’s passphrase to complete the process.
This certificate is set to expire after a year. You can adjust the
-days flag to obtain a useful lifetime for your requirements. You should arrange to generate a replacement certificate before this one expires.
Generating a Client Certificate
Next you should generate another certificate for your Docker clients to use. This must be signed by the same CA as the server certificate. Use an extensions file with
extendedKeyUsage = clientAuth to prepare this certificate for use in a client scenario.
# Generate a client key openssl genrsa -out client-key.pem 4096 # Create a certificate signing request openssl req -subj '/CN=client' -new -key client-key.pem -out client-request.csr # Complete the signing echo extendedKeyUsage = clientAuth >> extfile-client.cnf openssl x509 -req -days 365 -sha256 -in client-request.csr -CA ca-public.pem -CAkey ca-private.pem -CAcreateserial -extfile extfile-client.cnf -out client-certificate.pem
Preparing to Configure Docker
server-key.pem files into a new directory ready to reference in your Docker config. Afterwards, copy the
client-key.pem files to the machine which you’ll connect from.
You can delete the certificate signing request and extension files in your working directory. Be careful not to lose your private keys as they’re non-recoverable. Without them you’ll be unable to validate certificates or generate renewals.
Configuring the Docker Daemon
Now you can start the Docker daemon with TLS flags referencing your generated certificate and keys. The
--tlskey parameters specify paths to the respective OpenSSL resources generated above.
/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2376 --tlsverify --tlscacert=ca-public.pem --tlscert=certificate.pem --tlskey=server-key.pem
--tlsverify flag enables enforcement of TLS connections. Clients without a matching certificate will be blocked from accessing Docker’s TCP socket.
Configuring the Docker Client
Activate TLS on the client by supplying TLS flags when you use the
docker command. You must also add the
-H flag to specify the remote Docker socket address to connect to. From the client’s perspective,
--tlsverify means the command will only connect to servers with a TLS certificate signed by the same certificate authority as its own.
docker -H tcp://0.0.0.0:2376 --tlsverify --tlscacert=ca-public.pem --tlscert=client-certificate.pem --tlskey=client-key.pem ps
Supplying these flags each time you use the CLI gets repetitive very quickly. If you’ll mostly be working with the same TLS-protected host, set the
DOCKER_TLS_VERIFY environment variables in your shell profile. Copy your certificates files to
key inside your
~/.docker directory. These correspond to Docker’s
--tls flags and define a default certificate for the client.
export DOCKER_HOST=tcp://0.0.0.0:2376 export DOCKER_TLS_VERIFY=1
You can simplify working with multiple hosts using a mixture of local, remote, unsecured, and TLS connections by setting up Docker contexts. This feature lets you switch between targets using Docker CLI commands.
The Docker client also supports alternative verification modes. Using a mixture of
tlsverify flags activates varying TLS enforcement levels.
tls set, Docker will authenticate the server using the default CA pool. Adding the
tlsverify flags without a client key will enforce the server uses the given CA without any other checks. Omitting
tlsverify but including the other three keys will verify the client’s certificate without authenticating the server’s CA.
Protecting Docker’s TCP socket with TLS certificates lets you expose the API more safely by preventing connections from unauthorized clients. Actors who port scan your network will be barred from connecting to Docker, giving you a layer of protection that stops your machine being compromised with root-level privileges.
Once you’ve generated your certificates, you can use them to authenticate with the Docker CLI or your own HTTP clients. Curl will accept them as
--cacert flags, for example.
TLS is only one component of a secured Docker API instance. It provides encryption and an assurance that clients are trusted but is not a granular access control mechanism.
If you want to limit what individual clients are allowed to do, you should set up a Docker Engine authorization plugin. Plugins can contact an external service to determine whether a particular API request is allowed to proceed. As an alternative, you could use a reverse proxy in front of your TCP socket to enforce access control before requests reach Docker.