Docker Swarm and Firewall

Published:

Docker Swarm and Firewall

After several inexplicable abuse letters from my hosting provider, despite careful firewall configuration, regarding open ports and unsecured databases, I finally found the cause of the problem after a long search. But first, let’s start from the beginning. Docker Swarm is a good alternative to Kubernetes if you don’t want to unnecessarily increase complexity. Docker Swarm offers not only container orchestration but also built-in networks, as well as configuration and secret management. However, using Docker Swarm on Ubuntu with the built-in UFW firewall poses a security problem. Let’s take a closer look at this.

The Problem

We want to run a MEAN stack application on our Ubuntu server.

Set Up Firewall

$ ufw allow https && ufw allow http && ufw allow ssh && ufw enable

This command sets up rules in UFW for HTTP(S) and SSH and enables the firewall.

Initialize Docker Swarm

We install Docker and start Docker Swarm with a single node.

$ docker swarm init

This command initializes a Docker Swarm cluster on the server.

MongoDB as Swarm Service

We create a MongoDB service and start it.

$ docker service create --name mongo --publish mode=host,target=27017,published=27017 mongo:3.4

We start a MongoDB service and open the default MongoDB port 27017. This port is not opened in our firewall and should only be accessible locally.

Attempt to Connect to MongoDB

If we try to connect to MongoDB with any client, we would think that the connection is not possible, but surprisingly, it is possible. We can connect to the MongoDB service. This, of course, is an explanation for the inexplicable abuse letters from my hosting provider.

Explanation

Docker Swarm places itself in front of UFW in the IP tables. Thus, the rules are already set before UFW even becomes active.

Conclusion

After a long search, I found the solution.

You need to manually edit the /etc/ufw/after.rules file and append the following rules at the end:

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER

Then restart UFW with:

$ sudo systemctl restart ufw

or

$ sudo ufw reload

Now, we can connect to the MongoDB service.

To allow access to, for example, port 80, you need to reopen the ports with:

$ ufw route allow proto tcp from any to any port 80

TL;DR

If you want to run a Docker Swarm cluster on Ubuntu with the built-in UFW firewall, you need to manually edit the /etc/ufw/after.rules file. Also a container that fix this is available on Docker Hub:

https://hub.docker.com/r/chaifeng/ufw-docker-agent/#solving-ufw-and-docker-issues