Networking / Beginners

Setting Up a Server Firewall

You want to implement an iptables firewall on a server. You may have an external firewall already, and you want to do the fine-tuning on the server, or you have a server directly connected to the Internet. You pay careful attention to hardening your server, and are confident it could survive without a firewall. This is an extra layer of defense in case of mistakes. You want to drop all traffic that doesn't belong on your server, like all the automated brute-force attacks and worms that pummel the Internet unceasingly.

This script allows only traffic destined for the correct ports, such as port 80 for a web server, or port 25 for an SMTP server, and so on:

#!/bin/sh
##/usr/local/bin/fw_server
#for a server
#chkconfig: 2345 01 99
#define variables
ipt="/sbin/iptables"
mod="/sbin/modprobe"

#Flush all rules, delete all chains
$ipt -F
$ipt -X
$ipt -t nat -F
$ipt -t nat -X
$ipt -t mangle -F
$ipt -t mangle -X

#Zero out all counters
$ipt -Z
$ipt -t nat -Z
$ipt -t mangle -Z

#basic set of kernel modules
$mod ip_tables
$mod ip_conntrack
$mod iptable_filter
$mod iptable_nat
$mod iptable_mangle
$mod ipt_LOG
$mod ipt_limit
$mod ipt_state

#optional for irc and ftp
#$mod ip_conntrack_irc
#$mod ip_conntrack_ftp

#Set default policies
$ipt -P INPUT DROP
$ipt -P FORWARD DROP
$ipt -P OUTPUT ACCEPT
$ipt -t nat -P OUTPUT ACCEPT
$ipt -t nat -P PREROUTING ACCEPT
$ipt -t nat -P POSTROUTING ACCEPT
$ipt -t mangle -P PREROUTING ACCEPT
$ipt -t mangle -P POSTROUTING ACCEPT

#these lines are necessary for the loopback interface
#and internal socket-based services to work correctly
$ipt -A INPUT -i lo -j ACCEPT

#custom tcp allow chain
$ipt -N ALLOW
$ipt -A ALLOW -p TCP --syn -j ACCEPT
$ipt -A ALLOW -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT
$ipt -A ALLOW -p TCP -j DROP

#Accept important ICMP packets
$ipt -A INPUT -p icmp --icmp-type echo-request -j ALLOW
$ipt -A INPUT -p icmp --icmp-type time-exceeded -j ALLOW
$ipt -A INPUT -p icmp --icmp-type destination-unreachable -j ALLOW

Then, you need to add rules for the specific services you are running. For an FTP server, you need to add the ip_conntrack_ftp and ip_nat_ftp modules. Next, add these rules to allow incoming connections to your server, and the outgoing responses:

#FTP control port
$ipt -A INPUT -p tcp --dport 21 -j ALLOW
#FTP data port
$ipt -A INPUT -p tcp --sport 20 -j ACCEPT

Passive FTP transfers are a bit of pain, because they use unpredictable highnumbered ports. You may configure your FTP server to use only a limited range of ports, then specify them in your iptables rule:

$ipt -A INPUT -p TCP --destination-port 62000:64000 -j ACCEPT

SSH looks like this:

$ipt -A INPUT -p tcp --dport 22 --sport 1024:65535 -j ALLOW

IRC servers need the ip_conntrack_irc module, and this rule:

$ipt -A INPUT -p tcp --dport 6667 --sport 1024:65535 -j ALLOW

This rule is for a web server:

$ipt -A INPUT -p tcp --dport 80 --sport 1024:65535 -j ALLOW

If you are using multiple ports, such as SSL or a test port, list them all with the multiport match:

$ipt -A INPUT -p tcp -m multiport --dport 80,443,8080 --sport 1024:65535 -j ALLOW

Email servers can also use single or multiport rules, as these two examples show:

$ipt -A INPUT -p tcp --dport 25 --sport 1024:65535 -j ALLOW
$ipt -A INPUT -p tcp -m multiport --dport 25,110,143 --sport 1024:65535 -j ALLOW

DNS servers need these rules:

$ipt -A INPUT -p udp --dport 53 -j ACCEPT
$ipt -A INPUT -p tcp --dport 53 -j ALLOW

If your server needs to perform DNS lookups, add these rules:

$ipt -A OUTPUT -p udp --dport 53 -j ACCEPT
$ipt -A OUTPUT -p tcp --dport 53 -j ACCEPT

The ALLOW chain accepts only TCP packets with the SYN flag set. A subtle iptables gotcha is that the NEW state will allow TCP packets through that do not have the SYN flag set, so we must make sure that only SYN-flagged packets are allowed. SYN is always the first step in initiating a new TCP session, so if it isn't present, we don't want to accept the packet.

Opening holes in a host firewall for services is easy, as you're not hassling with NAT or forwarding. Be sure of your port numbers, and whether you need UDP or TCP. Most services have UDP and TCP ports reserved for them, but the majority only need one or the other, so check the documentation of your server to make sure. Connection requests almost always come from high-numbered source ports (i.e., 1024:65535). Anything from a privileged port is suspect, so you don't want to accept those unless you are certain that your server is supposed to accept them, such as FTP.

Be careful about getting the ACCEPT and ALLOW chains mixed up. Use the ALLOW chain only for filtering incoming SYN packets, which doesn't happen with the FTP data ports or UDP datagrams.

[Previous] [Contents] [Next]