sorenpoulsen.com header

Quick Minimal Firewall on Ubuntu With Iptables

Let's create a minimal set of iptables rules and a two-liner script that can restore the rules on restart.

To stay minimal, we are going to use iptables directly, instead of the Uncomplicated Firewall (ufw) tool that comes bundled with Ubuntu. While UFW has a simple syntax when adding one rule at the time, it creates a ton of iptables chains behind the scenes.

Check that UFW is inactive so it won't interfere with our direct use of iptables.

$ ufw status
Status: inactive

Check that no other application has been messing with iptables. You should see the default INPUT, FORWARD and OUTPUT chains with no rules in them.

$ iptables -L -v
Chain INPUT (policy ACCEPT 2671 packets, 791K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 2839 packets, 356K bytes)
 pkts bytes target     prot opt in     out     source               destination

Create the iptables rules

The slow approach would be to create some basic rules, using the iptables command to add one rule at the time. Then use the iptables-save command to dump the rules to a file, from where they can be restored with the iptables-restore command, when the host is restarted.

The quick approach is simply to have a ready-made dump of rules, that you can copy to any new host.

Here are two minimalist rulesets for a server and a personal computer:

Rules for a server

The following dump has a minimal set of rules that allow incoming traffic on the SSH, HTTP and HTTPS ports, but reject all other incoming traffic unless it's in response to an outgoing connection.

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [74:40104]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -j DROP
COMMIT

Rules for a Personal Computer

For a Personal Computer a typical minimalist set of rules would open incomming traffic to mDNS and SSH.

mDNS is used by network printers to announce their presence on the local network using multicast UDP packets to IP 224.0.0.251 port 5353. Letting this traffic through the firewall, makes printer setup automatic on Ubuntu.

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [4328:9240189]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -d 224.0.0.251/32 -p udp -m udp --dport 5353 -j ACCEPT
-A INPUT -j DROP
COMMIT

Create a file for the rules.

$ sudo su -
$ vim /etc/iptables.rules

Press i for insert mode and paste one of the rulesets listed above. Press ESC to exit insert mode and type ":wq" to write the file and quit.

Enable the iptables rules

Let's create a script that loads the rules when the host is restarted.

$ vim /etc/network/if-pre-up.d/iptables-restore

Press i for insert mode and paste this content.

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0

Press ESC to exit insert mode and type ":wq" to write the file and quit.

Make the file executable.

$ chmod u+x /etc/network/if-pre-up.d/iptables-restore

The script we just created is automatically executed on restart just before the network interface is brought up.

Test it

Load the iptables rules.

$ ./etc/network/if-pre-up.d/iptables-restore

Check the active rules are as expected.

$ iptables -L -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere            
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:ssh
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:http
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:https
    0     0 DROP       all  --  any    any     anywhere             anywhere            

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination        

Finally do a restart test.

$ shutdown -r now

When the host is back up check that the rules were restored.

$ iptables -L -v

{{model.usr.name}}
{{cmt.user.name}}
{{cmt.user.name}}
{{childcmt.user.name}}
{{childcmt.user.name}}