USING IPTABLES INSTEAD OF UFW IN A BASIC SERVER SETUP & FOR DOCKER
Quick tips to set up a Linux server firewall

Development Cluj Napoca, June 8, 2017, by Cristian Hainic
Estimated reading time:
Tags: DevOps


This article shows why and how to use iptables instead of ufw to set up a Linux server. Contrary to general belief, I hold that doing so requires spending not more, but roughly the same amount of time with the former that you would with the latter.

 

Problem

 

Most tutorials out there walking people through their initial VPS configuration recommend using ufw to set up their basic server firewall. UncomplicatedFirewall is a frontend wrapper around iptables aimed at making it easier for users to set up Linux firewalls. Although its commands and syntax are, indeed, easy to learn and handle, ufw did not behave correctly in my personal case. For example, on a fresh bare-metal server, I got an Ubuntu 16.04 instance running and head off to configuring ufw:

 

$ sudo ufw show added 
Added user rules (see 'ufw status' for running firewall): 
ufw allow 80/tcp 
ufw allow 443/tcp 
ufw allow 25/tcp 
ufw allow 22/tcp 
$ sudo ufw enable 
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y

 

As you can see from above, I allowed the most common ports to be accessed, including the standard 22 port for SSH connections (and yes, I checked in /etc/ssh/sshd_config to make sure SSH listens to port 22). However, the moment I pressed 'y' the whole server froze, leaving me with no option but to hard-reboot in order to continue work.

 

After several failed attempts to get ufw to work, I started searching around and discovered that an old kernel had similar problems with using ufw in OpenVZ templates. My server was not running under a hypervisor and had a new kernel (Linux 4.4.38-std-1). Not having the time, patience, and required low-level expertise needed to further debug server stalling, I simply resorted to replacing ufw with iptables altogether.

 

Solution

 

Upon comparing ufw to iptables, for which it acts as a frontend tool, I learned of two advantages of UncomplicatedFirewall that may be of general interest to users: (1) its ability to detect installed apps such as OpenSSH and perform operations such as allow directly on them without worrying what ports they use, and (2) its more readable command syntax. Of course, (1) is not of much help if you want to configure your firewall before installing the apps for which you are configuring it. As far as (2) is concerned, here's the iptables equivalent of ufw 'allow' commands for incoming traffic:

 

          

ufw

      

          

iptables

      

          

sudo ufw allow ssh

      

          

sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

      

          

sudo ufw allow http

      

          

sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

      

          

sudo ufw allow https

      

          

sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

      

          

sudo ufw allow 25

      

          

sudo iptables -A INPUT -p tcp --dport 25 -j ACCEPT

      

 

The iptables syntax is easy to understand once you know what all the abbreviations stand for: -A appends a new rule to the list of rules for incoming (INPUT) traffic, -p tells iptables to match packages coming via the tcp protocol (you can replace tcp with another protocol), while –dport further filters these packages down to only those pointed towards the port specified. Finally, with -j (jump to target rule) we tell iptables what to do with those packages. If you'd want to reject them, you'd simply replace ACCEPT with REJECT.

 

Complicated? I think not.

 

To be fair, if you do decide to use iptables in the initial configuration of your server, please bear in mind these two other commands which you need. First of all, and before configuring anything, make sure you don't lock yourself out of your connection by accepting the current established connection with this command: sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT. Then, whenever you have finished defining rules for your incoming connections, block everything else: sudo iptables -A INPUT -j DROP. This appends a rule to ignore all connections that don't fall into one of the filters specified before in the rules chain for INPUT.

 

Of course, this is only a peek into a possible rules chain with iptables. It will only do for an initial server firewall configuration. But I hope to have provided a simple alternative to ufw. For further configuration with iptables you can dive into the dedicated Wiki page for iptables on ubuntu.com.

ufw and Docker

Another place where ufw doesn’t perform as expected is when used in conjunction with Docker. To be honest, it’s not ufw’s fault, but for somebody using ufw to configure their firewall it definitely feels like it is.

 

The root of the problem is that if you set up some docker container ports to some local host ports, you would expect to be able to control the access to the local ports using ufw.

 

For example, if you were to run a redis container like this docker run --name “test_redis” -d -p 6379:6379 redis:3.2-alpine redis-server you would then expect to be able to limit access to this container with something like ufw allow from 10.5.0.0/24 to any port 6379. There’s no error, but then you get a call from clients saying that the site doesn’t work anymore, and you figure out that your (completely firewalled but otherwise unsecured) redis server has had its password changed by someone and you can’t access anything on it.

 

Trying to connect to this redis server from outside the internal network somehow still works, even though your ufw rule is safe and intact.

 

The reason for this isn’t a malfunction of ufw, but rather a misunderstanding of how ufw works. ufw sets up a different set of rules / chains / filters (you can see them if you run iptables on a ufw enabled machine). Docker adds port redirection directly through iptables, on its own chain, which bypasses ufw altogether.

 

There are two ways to go around this problem:

  1. Bind the local port of Docker to a specific interface (this will effectively limit access from outside of that network). Something like docker run --name “test_redis” -d -p 10.5.0.1:6379:6379 redis:3.2-alpine redis-server

  2. You could start the docker daemon (docker machine) with --iptables=false and manage docker network stuff yourself in iptables / ufw (check this article: https://fralef.me/docker-and-iptables.html)

 

Bottom line

 

ufw is nice for very basic operations, and it uses iptables behind-the-scenes to do them. If your needs exceed basic or you have issues with ufw operations, (carefully) proceed to iptables.

 

 

Article coauthored by Andrei Avram

Image of Cristian Hainic
About the author
Cristian Hainic 
 ‚ÄčI don't like talking about myself. But, if you must know, I love metal music, craft beer, and my family.

18 Comments



Leave a Comment