One of the most important things when creating secure Web applications is setting up good firewall. UFW, or Uncomplicated Firewall, is a command line tool that allows you to configure firewall settings easily compared to using iptables. In this tutorial we will prepare Docker container contains basic Nginx code with using UFW to showcase use case in real life.
I also provided more resources in the end of this article.
docker compose up, docker ps, docker build and docker run.Let's first install the UFW. To check whether UFW is installed or not we can run:
sudo ufw status
If it's not installed we can simply install it:
sudo apt install ufw
To check if installation was successful, we can run sudo ufw status again.
Let's start with the docker-compose.yml file. In this example we will just host static html with Nginx and docker.
docker-compose.yml:
services:
nginx:
image: nginx:1-alpine
ports:
- 8082:80
volumes:
- ./index.html/:/usr/share/nginx/html
nginx.nginx service, in our case it's nginx:1-alpine.8082 is the public port accessible by the outside world. 80 is the container's port we are listening on.index.html will be replaced to /usr/share/nginx/html inside container.Let's create our index.html file next to docker-compose.yml:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>
To start our application we can run:
docker compose up
You should be able to reach the application at http://yourip:8082.
Now, let's test UFW with our example. Firstly let's disable UFW.
sudo ufw disable
Depending on your needs, you might want to enable or disable IPv6. In latest versions of Ubuntu, IPv6 is enabled by default. To change it, edit the configuration file at /etc/default/ufw.
We can simply change IPV6 to yes to allow IPV6.
There are lots of configuration files that allow us to fine tune UFW:
Before enabling UFW, be aware it blocks SSH by default — allow SSH first:
sudo ufw allow ssh
Let's also allow http and https:
sudo ufw allow http
sudo ufw allow https
Start UFW:
sudo ufw enable
Now UFW will run automatically whenever we start our server.
Let's block all incoming connections so we can manually decide which ones to allow:
sudo ufw default deny incoming
You'd expect the website to be blocked now — but Docker by default modifies iptables, bypassing UFW's deny rules. We need to fix this.
Thanks to Feng's stack overflow answer, we can modify /etc/ufw/after.rules and add the following 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 -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
Restart UFW and docker:
sudo systemctl restart ufw &&
sudo systemctl restart docker
Now port 8082 should be blocked. To allow specific ports:
sudo ufw allow 8082
We've learned how to: