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/certs
directory exists, create if not - Create a self-signed root CA with a common name
Docker ($FQDN) CA
. FQDN ishostname
plus the first suffix from/etc/resolv.conf
search 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.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
- 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)