How to protect a Windows 2016 Docker engine with TLS

Today I have started a Windows Server 2016 VM with Container support in Azure. This is pretty easy as there is a prebuilt VM with the Docker base images. But I want a secured connection from my laptop to the Windows Docker engine running in Azure.

There is a tutorial Protect the Docker daemon socket at the website of Docker which uses the openssl tool to create all the certificates etc. But how should we do this on Windows?

Just containerize what's there

I have seen the DockerTLS script in a GitHub repo from Microsoft. But this script installs OpenSSL on my machine which I don't want to.

My first thought was, let's put this script + OpenSSL into a Docker image and run it in a Windows container.

So this Dockerfile was my first attempt to just use Chocolatey to install OpenSSL, download the PowerShell script from the Microsoft GitHub repo. Done. The script can run in a safe environment and I don't have to install software on my Docker host.

DockerTLS

But there is still work to do on the host to configure the Docker engine which I wanted to automate a little more. So it would be great to have a tool that can

  • generate all TLS certs
  • create or update the Docker daemon.json file
  • Put the client certs into my home directory

But still we need a program or script with OpenSSL to do that. I thought this tool should be deployed in a Docker image and shared on the Docker Hub. And here it is:

docker run dockertls

dockertls

The script generate-certs.ps1 creates the TLS certs and copies them to the folders that would be used on the Docker host. The script would directly work on a Docker host if you have OpenSSL/LibreSSL installed.

The dockertls Docker image is created with this Dockerfile. It installs LibreSSL from OpenBSD (thanks to Michael Friis for that optimization) and copies the PowerShell script inside the image.

You can find the full source code of the dockertls image in my dockerfiles-windows GitHub repo if you want to build the Docker image yourself.

Otherwise you can just the dockertls Docker image from the Docker Hub.

Dry run

As you don't trust me or my Docker image you can do a dry run with some temporary folders where the container can copy files into without destroying your Docker host.

Just create two folders:

mkdir server
mkdir client\.docker

Now run the Windows container with the environment variables SERVER_NAME and IP_ADDRESSES as well as two volume mounts to write the certs back to the host:

docker run --rm `
  -e SERVER_NAME=$(hostname) `
  -e IP_ADDRESSES=127.0.0.1,192.168.254.123 `
  -v "$(pwd)\server:C:\ProgramData\docker" `
  -v "$(pwd)\client\.docker:C:\Users\ContainerAdministrator\.docker" `
  stefanscherer/dockertls-windows

Afterwards check the folders:

dir server\certs.d
dir server\config
dir client\.docker

You will see that there are three pem files for the server, the daemon.json file as well as three pem files for the client.

Of course you could manually copy the files and try them out. But this Docker image can do this for you as well.

Full run

You may have to create the .docker folder in your home directory.

mkdir $env:USERPROFILE\.docker

Now run the container with the correct paths on the host so it can copy all certs and configs to the right place. The script can read an existing daemon.json and update it to keep all other configuration untouched.

docker run --rm `
  -e SERVER_NAME=$(hostname) `
  -e IP_ADDRESSES=127.0.0.1,192.168.254.123 `
  -v "C:\ProgramData\docker:C:\ProgramData\docker" `
  -v "$env:USERPROFILE\.docker:C:\Users\ContainerAdministrator\.docker" `
  stefanscherer/dockertls-windows

Now you have to restart the Docker service in an administrator Shell with

restart-service docker

One last step is needed on your host. You have to open the port 2376 in your firewall so you can access the machine from the outside. But then you're done on your host.

You can recreate the TLS certs with the same command and just restart the Docker service afterwards.

Test TLS connection

Now test the connection to the TLS secured Docker service with

docker --tlsverify `
  --tlscacert=$env:USERPROFILE\.docker\ca.pem `
  --tlscert=$env:USERPROFILE\.docker\cert.pem `
  --tlskey=$env:USERPROFILE\.docker\key.pem `
  -H=tcp://127.0.0.1:2376 version

Or just set some environment variables

$env:DOCKER_HOST="tcp://127.0.0.1:2376"
$env:DOCKER_TLS_VERIFY="1"
docker version

Azure

In an Azure VM you should use your DNS name for the VM in the SERVER_NAME environment variable and your public and local IP addresses of that machine.

docker-run

You have to open the firewall port 2376 on your Windows Docker host.

For Azure you also have to add a incoming rule for port 2376 in your network security group.

Then you have to securely transfer the three client pem files from your Azure VM to your laptop.

I've done that on my old Windows 10 machine which is only a 32bit machine, but I still can work with the Windows 2016 Docker engine running in Azure.

docker-version

As always, please leave a comment if you have questions or improvements or want to share your thoughts. You can follow me on Twitter @stefscherer.

Stefan Scherer

Read more posts by this author.