~scolby33/scolbyblog

2a412aa13939236371d643c849101974e63ae771 — Scott Colby 5 months ago 20af50f
Add post about blocking docker swarm ingress.
1 files changed, 25 insertions(+), 0 deletions(-)

A content/docker_swarm_and_iptables.md
A content/docker_swarm_and_iptables.md => content/docker_swarm_and_iptables.md +25 -0
@@ 0,0 1,25 @@
Title: Running a Localhost-Only Service on Docker Swarm
Date: 2021-05-05

I have been using Docker Swarm to run services on my home server in order to nicely have containers start at boot. My Swarms all only contain one node, and, early on, I had difficulty getting the Swarm ingress routing mesh working, so I have been using `mode: 'host'` for all exposed ports.

The host-mode networking simply binds to ports on the host, but, unfortunately, always binds to `0.0.0.0` (all available addresses). It is not possible to change the interface that a Swarm service binds to:

- [https://github.com/moby/moby/issues/26696](https://github.com/moby/moby/issues/26696)
- [https://github.com/moby/moby/issues/32299](https://github.com/moby/moby/issues/32299)

Like lots of little features in Docker, there are years-old open issues and basically no interest in fixing them. I could spend the time to implement this functionality, but from reading those issues, I don't have high hopes of a pull request being accepted anyway.

So, here's the hacky solution that gets me what I need. Unless configured not to, the Docker daemon adds a `DOCKER-USER` chain to iptables. I've always used UFW on my systems because I didn't need complicated firewalling, but this seems to coexist with UFW peacefully.

1. Install `iptables-persistent`: `apt install iptables-persistent`
2. Create `/etc/iptables/rules.v4` with the following content, replacing the value after `--dport` with whichever port should only be accessible from the localhost:
```
*filter
:DOCKER-USER - [0:0]
-I DOCKER-USER ! -i lo -p tcp -m tcp --dport 5000 -j DROP
COMMIT
```
3. Flush and start `netfilter-persistent`, reload UFW, and restart docker: `netfilter-persistent flush; netfilter-persistent start; ufw reload; systemd restart docker.service`

I don't love this for several reasons. It feels fragile to have two separate things managing my persistent firewall rules. It decreases my opinion of the Docker project to leave small but important issues like this sitting for years. But, this works for now.