contact | essays | photos | projects | home
essays

So you want to set up a firewall...
posted on October, 26, 2011

Let's say you have a server that is on a network and you want to only allow access to specific services, such as SSH, HTTP, and DNS. And you want this firewall to persist between reboots. Oh yeah, and you want it to work with both IPv4 and IPv6. This is an easy problem!

We're going to make a few assumptions:
Creating a firewall with iptables is simple: you run the iptables command a few times, each time telling it a new rule, and it applies them, in order, to any packet that comes in. So if you first tell iptables to drop all packets and then tell it on the next command to accept some types of packets, the second command will never really run because the packets were all dropped in the first step.

First we are going to flush all of the rules that are present so we can start fresh:
iptables -F
The next rule, a very basic rule, will allow any existing and established connection:
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Then we are going to accept any connection that comes from localhost or from ourselves:
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -s 1.2.3.4 -j ACCEPT
It might be very useful to allow incoming ICMP requests. This will allow ping and traceroute on both sides of your interface -- incoming and outgoing -- to work successfully. For iptables this would look like this:
iptables -A INPUT -p icmp -j ACCEPT
For ICMP, this is the only instance where the -p flag is different between IPv4 and IPv6. Everywhere else you can pretty much replace iptables with ip6tables and it will work. That makes the IPv6 statement it look like this:
ip6tables -A INPUT -p icmpv6 -j ACCEPT
Once we've done those basic steps, we want to decide which ports we want to allow in. As listed above, we want to allow SSH, HTTP and DNS. In this example we are going to use the names of the ports, but it is very simple to replace "ssh" or "domain" with "22" and "53", respectively. Let's open up a few ports:
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport domain -j ACCEPT
iptables -A INPUT -p udp --dport domain -j ACCEPT
iptables -A INPUT -p tcp --dport http -j ACCEPT
iptables -A INPUT -p tcp --dport https -j ACCEPT
However, we want to limit people from connecting to our SSH server too quickly. To do that, we can restrict new connections on port 22 when the remote address has made more than four connections in sixty seconds:
iptables -I INPUT -p tcp --dport 22 \
         -m state --state NEW -m recent --set
iptables -I INPUT -p tcp --dport 22 \
         -m state --state NEW -m recent \
         --update --seconds 60 --hitcount 4 -j DROP
Finally, after allowing in all of the above connections, we want to drop anything else:
iptables -A INPUT -j DROP
Now that we've done this, our server will only accept outside connections on ports 22, 53, 80 and 443. But when we reboot, you will quickly find that everything was lost! Saving your firewall between reboots depends on the system. On Redhat-like systems -- CentOS, RHEL, Fedora -- this capability is built in:
/etc/init.d/iptables save
On Debian, you have to set it up yourself:
mkdir /etc/firewall
iptables-save > /etc/firewall/ipv4.conf
Then force the firewall to reload on the next boot by editing /etc/rc.local and adding this simple line:
iptables-restore < /etc/firewall/ipv4.conf
Now you've got a simple firewall set up.

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. Any instructions provided in this essay are provided as-is with no warranty whatsoever and the author bears no liability resulting from any and all uses of this work. Use at your own risk.