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:
- Check if
/etc/docker/certsdirectory exists, create if not - Create a self-signed root CA with a common name
Docker ($FQDN) CA. FQDN ishostnameplus 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 - Update
/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,
systemctl daemon-reload - 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.
Top comments (0)