DEV Community

Miro
Miro

Posted on

Simple Docker HTTP socket certificates

I have a couple of Docker servers and I wanted to be able to manage them remotely. Reading about it, adding -H tcp://0.0.0.0:2375 argument when starting docker daemon was simple enough solution but it also came with a warning that it is not secure.

In addition, there is a Protect the Docker daemon socket guide which covers how to expose http socket in a secure manner but it is also marked as an "Advanced topic" so I decided to make a simple shell script out of it.

Tested on a clean Debian 10 after Docker installation. Full docker-certs.sh is at the bottom of the post.

Usage

docker-cert.sh [options]
OPTIONS:
  --all           Same as --ca --server --client --service combined, default behaviour
  --ca            Creates only self-signed Root CA
  --server        Creates a server certificate, requires CA to be created first
  --client        Creates a client certificate with a default name, requires CA to be created first
  --client=Name   Creates a client certificate with provided name, requires CA to be created first
  --service       Updates /lib/systemd/system/docker.service with socket listener and tls verification

Note: all files will be created in /etc/docker/certs dir

Running it without any options like sudo docker-cert.sh will execute default behaviour, same as with the --all option. Script will do the following:

  1. Check if /etc/docker/certs directory exists, create if not
  2. Create a self-signed root CA with a common name Docker ($FQDN) CA. FQDN is hostname plus the first suffix from /etc/resolv.conf search line, or just hostname if search line is not found.
  3. Create a server certificate with a common name Docker ($FQDN) Server, signed by the root CA
  4. Create a client certificate with a common name Docker ($FQDN) Client, signed by the root CA
  5. Update /lib/systemd/system/docker.service to append socket listener and certificates to the start command. Like the following:
-H tcp://0.0.0.0:2376
--tlsverify
--tlscacert=/etc/docker/certs/ca.pem
--tlscert=/etc/docker/certs/server.pem
--tlskey=/etc/docker/certs/server.key 
  1. Reloading systemctl daemon, systemctl daemon-reload
  2. Restart docker service, systemctl restart docker

There are checks in place so if a file that is to be created already exist, script will exit. In the same way if docker.service contains -H tcp:// in the ExecStart line, script will exit.

Additional clients

Additional clients can be created afterwards by running sudo docker-cert.sh --client=Another which will create client_another.key and client_another.pem files, and common name will be Docker ($FQDN) Client Another

Accessing remotely from Windows

Once certificates are created and docker daemon is running, it is time to test connectivity from a client windows machine. Tutorial suggests

docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
  -H=$HOST:2376

or setting environment variables like DOCKER_HOST or DOCKER_CERT_PATH for a directory where certificates are stored.

First approach looks cumbersome to type every time, and second approach doesn't work for multiple servers with different certificates.

Batch file

I opted to create a small batch file to interact with docker.

Let's say that my docker server is named dock1, domain suffix is local. I've created certificates using the script so I have Docker (dock1.local) Server certificate.

After that I copied ca.pem, client.key, client.pem from server to my windows machine into the %USERPROFILE%\.docker\dock1 folder.

Created a batch file named dock1.bat with the following content:

@echo off
set DATADIR=%USERPROFILE%\.docker\%~n0
if exist "%DATADIR%\host.var" (set /p DOCKERHOST=<"%DATADIR%\host.var") else (set DOCKERHOST=%~n0)
docker --tlsverify --tlscacert="%DATADIR%\ca.pem" --tlscert="%DATADIR%\client.pem" --tlskey="%DATADIR%\client.key" -H=%DOCKERHOST%:2376 %*

and put that batch file somewhere in the PATH.

(broken into multiple lines for readability):

set DATADIR=%USERPROFILE%\.docker\%~n0

if exist "%DATADIR%\host.var" (
  set /p DOCKERHOST=<"%DATADIR%\host.var"
) else (
  set DOCKERHOST=%~n0
)

docker
--tlsverify
--tlscacert="%DATADIR%\ca.pem"
--tlscert="%DATADIR%\client.pem"
--tlskey="%DATADIR%\client.key"
-H=%DOCKERHOST%:2376
%*

So when I run dock1 info from the command line that translates into the following command (broken into multiple lines for readability):

docker
--tlsverify
--tlscacert="C:\Users\user\ca.pem"
--tlscert="C:\Users\user\client.pem"
--tlskey="C:\Users\user\client.key"
-H=dock1:2376
info

and running it displays docker system-wide information.

It works for all docker commands, likedock1 images, dock1 ps, dock1 run .... etc.

More fun with batch file

Ok, so the batch file above has some additional lines and variables. Let me explain them:

%~n0 - this is the name of the file without extension. So if have file named dock1.bat it will have dock1 value. If I name it dock365.bat it will have dock365 value.

set DATADIR=%USERPROFILE%\.docker\%~n0 - I'm using it to set the path where certificates are stored to a folder named the same as the batch file within my user profile > .docker folder.

if exist "%DATADIR%\host.var" (set /p DOCKERHOST=<"%DATADIR%\host.var") else (set DOCKERHOST=%~n0) - if hostname of docker server is different from the batch file name, this allows me to put real hostname into the file which will be read into the variable.
So if host.var file does not exist, %DOCKERHOST% variable will contain name of the batch file without extension (dock1 for example). But if I want to access docker using dock1.local hostname, I can put dock1.local into the host.var file and then the %DOCKERHOST% variable will contain dock1.local value.

set /p DOCKERHOST=<"%DATADIR%\host.var" - set variable to a value being read from the input file.

%* - passing all arguments that batch file received.

What this batch script allows me is to have one txt file, let's name it docker_batch.txt, instead of the batch file. And then a symbolic link for each docker server I have. Created like this:

mklink dock1.bat docker_batch.txt
mklink dock365.bat docker_batch.txt

Pretty cool.

docker-certs.sh

Top comments (0)