UFW Docker eperience
Disclamer: I am not a network expert. I am a full stack software engineer that has interests in infrastucture and networking. So i try to learn every day. If there is anything wrong or missundestood please contact me!
I installed ufw-docker on a system to test around with docker and ufw in
my homelab. I wanted to learn how docker works with iptables and how i can block
traffic to opended ports via docker. Core question was: how to only allow some
users with a special ip to consume my service?
So i created an new virtual ubuntu machine and installed docker (compose) and
ufw first.
I found a solution with ufw-docker that did not really worked out for me in
the first place. So i tried to understand how it works / does not work for me.
Lab Setup - VM
The full installation guide to install the recent version of docker on a
ubuntu maschine, follow the instructions on the
docker site. Here in short:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# Install the latest version of docker and docker-compose
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Now install and activate ufw if not already happened. I used a virtual machine
on my proxmox host to test this. So I had to allow SSH before activating the
firewall.
sudo apt-get install ufw
sudo ufw allow 22/tcp
sudo systemctl restart ufw
Lab Setup - Docker Compose
After installing everything I created the test compose.yml that contains the
setup for my tests. It's a very simple setup.
networks:
ufw_test:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-ufw-test
ipam:
config:
- subnet: 192.168.33.0/24
gateway: 192.168.33.1
services:
webserver:
container_name: webserver
image: bitnami/nginx
ports:
- 8080:8080
networks:
ufw_test:
ipv4_address: 192.168.33.2
curl:
container_name: curl
image: curlimages/curl
command:
- sleep
- infinity
networks:
ufw_test:
ipv4_address: 192.168.33.3
The avoid random network creation with docker compose i defined my own network
to have the full control. Spolier: this makes it easier when using ufw to allow
specific requests through the network.
ufw_test the networks name and it is a bridge to our host network. As
additionals driver option com.docker.network.bridge.name: br-ufw-test i
defined to force use a static name for the network. In addition i defined the
network configuration via ipam. After starting docker compose you can see the
interface with ip a
$ ip a
...
59: br-ufw-test: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:6a:90:42:8b brd ff:ff:ff:ff:ff:ff
inet 192.168.33.1/24 brd 192.168.33.255 scope global br-ufw-test
valid_lft forever preferred_lft forever
inet6 fe80::42:6aff:fe90:428b/64 scope link
...
This compose file defines two containers:
- webserver: is a nginx container that opens it's port on 8080. It serves
the nginx default page. The container is part of the predefined network and
has the static ip
192.168.33.2. - curl: is a helper container that simply sleeps. It contains
curlto test requests from inside the docker network. The container is part of the predefined network and has the static ip192.168.33.3.
This then is the network topology of my test lab:
Problem
I assumed i wanted to limit the access to the webserver container to
- every ip in the container network
ufw_test - allow my computer to only access the webserver ip on port 8080.
Simply disabling the container access with ufw does not end in the wanted result:
ufw deny 8080/tcp
The reason is that docker uses iptables to route traffic to the opened ports of a container. You can see this with the following command:
iptables -L ...
As you can see the forwording rule hits before the ufw rule so our deny rule becomes ignored.
ufw-docker
When searching i found ufw-docker which promises to add additional configuration to provide configuration to ufw rules. To install it you have to download the script and installed:
sudo wget -O /usr/local/bin/ufw-docker \
https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
sudo chmod +x /usr/local/bin/ufw-docker
ufw-docker install
This is the entry the script adds to the iptables config:
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
-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-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
-A DOCKER-USER -j RETURN
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER
In my tests i reached the problem that i still can reach the port 192.168.33.2:8080.
HOW TO USE UFW_DOCKER
I thought i missconfigured something. So i digged deeper in the configuration of ufw-docker and found the following lines:
-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
The comment on the website was:
The following rules allow the private networks to be able to visit each other. Normally, private networks are more trusted than public networks.
This made me thinking. If i install a server in a network and use ufw, then it
is not quite the case that i trust everyone. I do not know who is also part of
the network so i want ufw to lock everything up. So i deleted that lines. With
the wanted result, that i could not reach the webserver port when run the
following curl command: ... But in addition i also wasn't able to curl it
within the docker network. That was not what i wanted to accieve. ...
To allow traffic flow within the network i allowed routing on the named network
ufw route allow in on br-ufw-test out on br-ufw-test
allow routing inside the container network
$ ufw route allow in on br-ufw-test out on br-ufw-test
$ ufw route allow in on eth0 to 192.168.33.2 port 8080 proto tcp
Fazit: If someone else also got this problem, found a different easier solution or sees an error in my solution: let me know and /contact me.
Comments (0)
No comments yet. Be the first to comment!