Linux Netfilter
Contact
Please send suggestions to mattw@csh.rit.edu.
A few days before the release of this document, another
tutorial(broken link)
was released. The author of this document has an excellent description of the
kernel configuration along with details of the actual filters. However,
I believe my packet flow diagrams give a better idea of how NAT and filtering
interact. I also think my rc.nat_firewall script is the most complete script
to be released.
Abstract
The NetFilter package included in the Linux 2.4.x kernel is a replacement for
ipchains. This document will show you how the netfilter software operates
along with how-to construct a secure a trusted machine which sits between
your home network and the public Internet.
Background
Starting in the 2.4.x kernel series, ipchains was depreciated in favor of
Netfilter. To use Netfilter you will need to upgrade to a 2.4.x kernel. This document was written and tested using 2.3.99-pre9, 2.4.0, 2.4.2.
Options
The following options need to be enabled (*) in the kernel or made into modules (M):
Networking options:
Network Packet filtering (CONFIG_NETFILTER)=*
IP: Netfilter Configuration
Connection Tracking (CONFIG_IP_NF_CONNTRACK)=*
IP Tables Support (CONFIG_IP_NF_IPTABLES)=*
Limit Match Support (CONFIG_IP_NF_MATCH_LIMIT)=*
Netfilter MARK match support (CONFIG_IP_NF_MATCH_MARK)=*
Multiple Port Match Support (CONFIG_IP_NF_MATCH_MULTIPORT)=*
TOS Match Support (CONFIG_IP_NF_MATCH_TOS)=*
Connection Tracking Support (CONFIG_IP_NF_MATCH_STATE)=*
Unclean Match Support (CONFIG_IP_NF_MATCH_UNCLEAN)=*
Owner Match Support (CONFIG_IP_NF_MATCH_OWNER)=*
Packet Filtering (CONFIG_IP_NF_FILTER)=*
Reject Target Support (CONFIG_IP_NF_TARGET_REJECT)=*
Mirror Target Support (CONFIG_IP_NF_TARGET_MIRROR)=*
Full NAT (CONFIG_IP_NF_NAT)=*
Masquerade Target Support (CONFIG_IP_NF_TARGET_MASQUERADE)=*
Redirect Target Support (CONFIG_IP_NF_TARGET_REDIRECT)=*
Packet Mangling (CONFIG_IP_NF_MANGLE)=*
TOS Target Support (CONFIG_IP_NF_TARGET_TOS)=*
Mark Target Support (CONFIG_IP_NF_TARGET_MARK)=*
Log Target Support (CONFIG_IP_NF_TARGET_LOG)=*
NOTE: If you are using a 2.4.2 or greater kernel, turn on the following
feature to support active ftp connection tracking. This will allow you to ftp
from a DOS shell or web browsers that do not support passive ftp.
Connection Tracking (CONFIG_IP_NF_CONNTRACK)=*
FTP Protocol Support (CONFIG_IP_NF_FTP)=*
Feature Dependencies
Technically, the Netfilter Firewall could be installed on a standalone
machine with only one network card, however, our example uses two
network cards to create a home gateway.
The Netfilter Firewall
Netfilter provides flexibility for manipulating packets as they flow
through the gateway. Depending upon the origin and destination of packets,
they may be passed, blocked or redirected to another IP/port.
The logic path for Netfilter follows:
DNAT: Destination Network Address Translation
Routing Decision #1: Is the packet destined for a local process
or external process?
Forward Filter: Packet Filtering
SNAT: Source Network Address Translation
Input Filter: Packets destined for a local process
Output Filter: Packets sourced from a local process
Routing Decision #2: Was the original packet in initiated via a local
process?
DNAT #2: For packets associated with a local process, apply the proper
destination NAT mapping.
Layer2 Function: Add appropriate MAC addresses to packet.
The Forward Chain
Packets routed between networks follow this path: DNAT Prerouting -> Forward ->
SNAT Postrouting
This applies to all traffic not destined directly for a local process.
For example, a SYN packet from internal network 192.168.42.0/24 destined for
external network 129.21.60.0/24
passes through the prerouting DNAT transparently without address translation.
Filtering is applied in the forward filter and source address translation
occurs in SNAT postrouting. Return ACK packets follow the same path except
destination address translation occurs in the DNAT prerouting module.
Since Netfilter is a stateful firewall, it maps outgoing connections to an
anticipated return path, which means we won't have to apply additional NAT
policies for the returned ACK's. In addition, the forward filter always sees
network addresses from behind the NAT module (internal addresses).
The forward filter is used to filter both inbound and outbound packets.
Most inbound packets will usually be on the return data path of an
established connection and cannot establish a new connection (unless you are running a service on the firewall). Therefore,
blocking return packets is unnecessary, with the exception of maybe limiting
ICMP echo-replies. An administrator could use the forward filter to prevent
internal users from connecting to certain external sites or other networks
attached to the firewall.
The forward filter applies mainly to host-to-host connections. Packets
destined directly for the firewall are routed into the local process loop
and pass through the INPUT / OUTPUT filters.
The Input / Output chains
Packets with a source address inside the home network destined for the
firewall or to another internal network directly connected to it,
will traverse the input and output filters. External connections seeking
a service on the firewall also follow this path.
There are two different cases for using the local process loop. The first
case deals with packets originating or destined for a network directly
connected to the firewall. An inbound packet will traverse the DNAT Prerouting and
Input modules, be serviced by the local process, and then get routed into
the Output -> DNAT -> SNAT Postrouting modules.
In the second case, packets coming from a network not directly connected to
the firewall. An inbound packet will traverse the DNAT Prerouting and Input
modules, be serviced by the local process, and then get routed into the
Output-postrouting -> Output -> DNAT -> SNAT Postrouting modules. It appears
that the Output-postrouting filter provides flexibility for local verses
non-local packets.
Downloads
Netfilter is included in the kernel sources found in the /usr/src/linux
directory.
The kernel source code may be obtained via anonymous FTP at:
ftp.us.kernel.org or
kernel.csh.rit.edu
The user space tool, iptables, is available at:
netfilter.kernelnotes.org
Documentation
A HOW-TO on compiling a kernel may be found atThe Linux Documentation Project.
Information on the Linux Netfilter software may be found at:
netfilter.filewatcher.org.
Installation Procedure
After compiling and installing a kernel with the above configuration, install the user space iptables tool.
Installing Iptables Tool
Download iptabes-1.2.tar.bz2:
Unpack this by:
bunzip2 iptables-1.2.tar.bz2
tar xvf iptables-1.2.tar
Change into the iptables-1.2 directory and install by:
cd iptables-1.2
make
make install
Next remove any old ipchains modules from the current /lib/modules/2.4.0 directory.
cd /lib/modules/2.4.0/
rm -r ipchains.o ipfwadn.o
depmod -a
Configuration of Netfilter
Netfilter combines three network functions (forwarding, filtering, NAT)
into one rule set using iptables. The series of commands that are used to set
up iptables can be put into a shell script for ease of use.
The below script includes code to bring up a backup PPP line when
my primary WAN (DSL line) goes down.
Create a new file named /usr/sbin/rc.nat_firewall that contains the
following rules, which include NAT and firewall filtering.
The complete file looks like this:
#!/bin/sh
#netfilter chain set
IPTABLES=/usr/local/sbin/iptables
LOGGING=0
#change the shell's default delim. so we can do parsing.
OIFS="$IFS"
IFS=.
WANDEV=eth0
WANADDR=`/sbin/ifconfig $WANDEV | grep 'inet addr:' | awk '{print $2}' | cut -c6-20` set -- $WANADDR A="$1" B="$2" C="$3" D="$4"
WANNET=$1.$2.$3
WANBCAST=$WANNET.255
WANNET=$WANNET.0/24
INTDEV=eth1
INTADDR=`/sbin/ifconfig $INTDEV | grep 'inet addr:' | awk '{print $2}' | cut -c6-20` set -- $INTADDR A="$1" B="$2" C="$3" D="$4"
INTNET=$1.$2.$3
INTBCAST=$INTNET.255
INTNET=$INTNET.0/24
STATICDEV=eth2
STATICADDR=`/sbin/ifconfig $STATICDEV | grep 'inet addr:' | awk '{print $2}' | cut -c6-20` set -- $STATICADDR A="$1" B="$2" C="$3" D="$4"
STATICNET=$1.$2.$3
STATICBCAST=$STATICNET.255
STATICNET=$STATICNET.0/24
PPPDEV=ppp0
PPPSTAT=`/sbin/ifconfig -a | grep $PPPDEV`
if ["$PPPSTAT" = ""]
then
PPPSTAT="0"
else
echo "PPP is up"
PPPSTAT="1"
PPPADDR=`/sbin/ifconfig $PPPDEV | grep 'inet addr:' | awk '{print $2}' | cut -c6-20` set -- $PPPADDR A="$1" B="$2" C="$3" D="$4"
PPPNET=$1.$2.$3
PPPBCAST=$PPPNET.255
PPPNET=$PPPNET.0/24
fi
IFS=$OIFS #reset the IFS environment var.
#################################################
#Flush all filters and NAT tables.
$IPTABLES -t nat -F PREROUTING
$IPTABLES -t nat -F POSTROUTING
$IPTABLES -t nat -F OUTPUT
$IPTABLES -F INPUT
$IPTABLES -F OUTPUT
$IPTABLES -F FORWARD
if ["$LOGGING" -eq 1]
then
$IPTABLES -A FORWARD -j LOG --log-level 7 --log-prefix FORWARD
$IPTABLES -A INPUT -j LOG --log-level 7 --log-prefix INPUT
$IPTABLES -A OUTPUT -j LOG --log-level 7 --log-prefix OUTPUT
$IPTABLES -t nat -A POSTROUTING -j LOG --log-level 7 --log-prefix POSTROUTING
$IPTABLES -t nat -A PREROUTING -j LOG --log-level 7 --log-prefix PREROUTING
$IPTABLES -t nat -A OUTPUT -j LOG --log-level 7 --log-prefix OUTPUT-ROUTING
fi
#Turn NAT on.
$IPTABLES -t nat -A POSTROUTING -s $INTNET -o eth0 -j MASQUERADE
$IPTABLES -t nat -A POSTROUTING -s $STATICNET -o eth0 -j MASQUERADE
$IPTABLES -t nat -A POSTROUTING -s $INTNET -o ppp0 -j MASQUERADE
$IPTABLES -t nat -A POSTROUTING -s $STATICNET -o ppp0 -j MASQUERADE
#Default Policy
$IPTABLES -P INPUT DROP
$IPTABLES -P FORWARD ACCEPT
$IPTABLES -P OUTPUT ACCEPT
#INPUT Filter
#drop fragments & invalid packets
$IPTABLES -A INPUT -f -j DROP
$IPTABLES -A INPUT -m state --state INVALID -j DROP
#unclean match target (not stable in NETFILTER package)
$IPTABLES -A INPUT -m unclean -j DROP
#spoofing - drop packets with our address as source, except ICMP
$IPTABLES -A INPUT -s $WANADDR -i $WANDEV -p ! ICMP -j DROP
#smurf attacks - disallow ICMP to our broadcast.
$IPTABLES -A INPUT -p icmp -i $WANDEV -d $WANBCAST -j DROP
#stop syn-flood, ping-o-death, & fast port scanning
$IPTABLES -A INPUT -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
$IPTABLES -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
$IPTABLES -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p tcp --tcp-flags ALL RST -m multiport --dports 80,10000 -j ACCEPT
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p tcp --tcp-flags ALL FIN -m multiport --dports 80,10000 -j ACCEPT
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p tcp --tcp-flags ALL SYN -m multiport --dports 80,10000 -j ACCEPT
#ICMP
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p icmp --icmp-type 0 -j ACCEPT
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p icmp --icmp-type 3 -j ACCEPT
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p icmp --icmp-type 4 -j ACCEPT
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p icmp --icmp-type 8 -j ACCEPT
$IPTABLES -A INPUT -i $WANDEV -m limit --limit 1/s -p icmp --icmp-type 11 -j ACCEPT
#PPP protection, when it's up.
if [ "$PPPSTAT" -eq 1 ]
then
echo "Configuring PPP interface with IPCHAIN rules."
$IPTABLES -A INPUT -s $PPPADDR -i $PPPDEV -p ! ICMP -j DROP
#smurf attacks - disallow ICMP to our broadcast.
$IPTABLES -A INPUT -p icmp -i $PPPDEV -d $WANBCAST -j DROP
#stop syn-flood, ping-o-death, & fast port scanning
$IPTABLES -A INPUT -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
$IPTABLES -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
$IPTABLES -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p tcp --tcp-flags ALL RST -m multiport --dports 80,10000 -j ACCEPT
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p tcp --tcp-flags ALL FIN -m multiport --dports 80,10000 -j ACCEPT
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p tcp --tcp-flags ALL SYN -m multiport --dports 80,10000 -j ACCEPT
#ICMP
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p icmp --icmp-type 0 -j ACCEPT
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p icmp --icmp-type 3 -j ACCEPT
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p icmp --icmp-type 4 -j ACCEPT
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p icmp --icmp-type 8 -j ACCEPT
$IPTABLES -A INPUT -i $PPPDEV -m limit --limit 1/s -p icmp --icmp-type 11 -j ACCEPT
fi
#Allowing existing connections
$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT -m state --state NEW -p ICMP -j ACCEPT
#allow incoming web traffic from WAN & LANs.
$IPTABLES -A INPUT -s 0/0 -p tcp -m multiport --dport 80,10000 -j ACCEPT
$IPTABLES -A INPUT -s 0/0 -p udp -m multiport --dport 80,10000 -j ACCEPT
#allow DNS queries from LAN
$IPTABLES -A INPUT -i $INTDEV -p tcp --dport 53 -j ACCEPT
$IPTABLES -A INPUT -i $INTDEV -p udp --dport 53 -j ACCEPT
$IPTABLES -A INPUT -i $STATICDEV -p tcp --dport 53 -j ACCEPT
$IPTABLES -A INPUT -i $STATICDEV -p udp --dport 53 -j ACCEPT
#allow SMB requests from LAN
$IPTABLES -A INPUT -i $INTDEV -p tcp --dport 137:139 -j ACCEPT
$IPTABLES -A INPUT -i $INTDEV -p udp --dport 137:139 -j ACCEPT
$IPTABLES -A INPUT -i $STATICDEV -p tcp --dport 137:139 -j ACCEPT
$IPTABLES -A INPUT -i $STATICDEV -p udp --dport 137:139 -j ACCEPT
#allow from lo
$IPTABLES -A INPUT -i lo -j ACCEPT
###########################################################################
#FORWARD Filter
#all forwards are allowed.
#end rc.nat_firewall
After creating this file, make it executable:
chmod 755 rc.nat_firewall
Next, in /etc/rc.d/rc.local add the following lines:
#IP NAT
echo "Starting IP NAT"
/usr/sbin/rc.nat_firewall
I was lucky enough to also have a dial-up back up line.
To monitor the network connection use a script called
net-check.pl.
Modify the following lines:
Line 113:
sleep 40;
$fake = '/usr/sbin/rc.nat_firewall';
print "\n Refreshing NAT";
and line
Line 164:
# $fake = `/etc/rc.d/init.d/network restart`; # kills our
$fake = '/usr/sbin/rc.nat_firewall'; #restart NAT & netfilter
print "\n Refreshing NAT";
Be sure to run lilo before rebooting to initialize the new kernel and iptables:
lilo
reboot
Debugging and Troubleshooting
Local clients cannot get to outside network
-Make sure that packet forwarding is enabled by:
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/ip_dynaddr
Check the filter configuration:
cat /proc/net/ip_conntrack
iptables -L
Checking the NAT configuration:
iptables -L -t nat
Turn debugging on:
Add this line to /etc/syslog.conf:
*.debug /var/log/iptables.log:
Restart syslog::
/etc/rc.d/init.d/syslog restart:
In /usr/sbin/rc.nat_firewall change::
LOGGING=0
to:
LOGGING=1
Rerun /usr/sbin/rc.nat_firewall. The log will show up in /var/log/iptables.log
To view a continuous trace of the log, use:
tail -f /var/log/iptables.log
FTP from web browser fails
Open the options menu for the browser
Check the 'View FTP via browser' box. (name might vary slightly)
Known Caveats
An attacker directly connected to the WAN subnet may be able to send packets
into the LAN if the attacker knows addresses on the LAN subnet. However,
packets will not be returned to the attacker. This is still under
investigation and only poses a small threat to internal users.
Active FTP does not work under the 2.4.0 kernel because the module
(ip_conntrack_ftp) does not compile and link properly.
2.4.2 fixes this compilation issue and adds ftp connection tracking.