Docker Swarm Remote API IP filtering with Caddy

Lately I’m experimenting with Docker Swarm and I was looking for an easy way to expose the Docker remote API so I could access the Swarm nodes from my home office and from CI servers. I’m using Terraform to create the Swarm hosts and since my IPs can change often I didn’t want to include them in the Terraform code.

Setup dockerd

When I provision a Swarm node with Terraform, after installing the Docker engine, I expose the Docker remote API on localhost port 2375 using the following systemd config:

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// \
  -H tcp://127.0.0.1:2375 \
  --storage-driver=overlay2 \
  --dns 8.8.4.4 --dns 8.8.8.8 \
  --log-driver json-file \
  --log-opt max-size=50m --log-opt max-file=10

Deploying the config can be done with a script like:

mkdir -p /etc/systemd/system/docker.service.d
cp docker.conf /etc/systemd/system/docker.service.d/docker.conf

systemctl daemon-reload
systemctl restart docker

The Terraform code that does this Docker engine setup can be found at stefanprodan/scaleway-swarm-terraform.

Run Caddy

Now that all the Swarm nodes are exposing the Docker remote API on localhost you can create the caddy-dockerd service.

Connect to a Swarm manager node and run:

docker service create -d -e IP=188.27.83.136 \
    --network=host \
    --name=caddy-dockerd \
    --mode global \
    stefanprodan/caddy-dockerd

The caddy-dockerd will run on every node and will expose the Docker remote API on port 7575. You can restrict access by proving multiple IPv4 or IPv6 or ranges of IPs. For more details see the Caddy ipfilter middleware documentation.

Run Caddy with Docker unix socket

If you don’t wish to expose the Docker remote API with TCP on localhost, you can mount the unix socket inside the Caddy container.

docker service create -d -e IP=188.27.83.136 \
    --network=host \
    --name=caddy-dockerd \
    --mode global \
    --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
    stefanprodan/caddy-dockerd:sock

If you opt for the Docker socket reverse proxy you need to use caddy-dockerd:sock.

This will also work without Docker Swarm:

docker run -d -e IP=86.124.244.168 \
    --net=host \
    --name=caddy-dockerd \
    --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    stefanprodan/caddy-dockerd:sock

Use Docker remote API

On your local machine you can setup the remote access like this:

$ export DOCKER_HOST=tcp://<DOCKER-IP>:7575

$ docker info

Update the IP filter

If you need to modify the IP environment variable, connect to a manager node and update the caddy-dockerd service like this:

docker service update \
    --env-add IP=188.27.83.136/30 \
    caddy-dockerd

For production systems is bad practice to expose the Docker remote API over HTTP. If you must do it, consider protecting the daemon socket by enabling TLS, more on this topic can be fond here.

If you have any questions or suggestions, please leave a comment here or on GitHub at stefanprodan/caddy-dockerd.

comments powered by Disqus