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.
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:
- Check if
/etc/docker/certsdirectory exists, create if not
- Create a self-signed root CA with a common name
Docker ($FQDN) CA. FQDN is
hostnameplus the first suffix from
/etc/resolv.confsearch line, or just hostname if search line is not found.
- Create a server certificate with a common name
Docker ($FQDN) Server, signed by the root CA
- Create a client certificate with a common name
Docker ($FQDN) Client, signed by the root CA
/lib/systemd/system/docker.serviceto 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
- Reloading systemctl daemon,
- 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
-H tcp:// in the
ExecStart line, script will exit.
Additional clients can be created afterwards by running
sudo docker-cert.sh --client=Another which will create
client_another.pem files, and common name will be
Docker ($FQDN) Client Another
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.
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
client.pem from server to my windows machine into the
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, like
dock1 run .... etc.
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
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.
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
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
Claim your page on DEV before someone else does
Level up every day