Stateful Firewall and Masquerading on Linux

www.puschitz.com



This article describes how I've setup stateful firewall and masquerading on Linux. For basic Linux security, see my other article Securing Linux Production Systems - A Practical Guide to Basic Security in Linux Production Environments.

I welcome emails from any readers with comments, suggestions, or corrections. You can find my email address at the bottom of this website.

Introduction


The Netfilter in the Linux kernel is able to keep track of network packet's state and context. This means that Netfilter can distinguish packets associated with an established connection from packets that are not. For example, if you connect to a web server with your browser, the web server answers your browser's request and Netfilter knows that these incoming network packets are the response to the request you initiated with your browser. Using this feature allows you to instruct Netfilter to only accept network packets that are part of an established or related connection initiated by you but to ignore all other network packets.


To accept packets that are part of an established connection you can define the following rule:
# /sbin/iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT

The option '-A' is for append which indicates that the rule should be appended to the INPUT chain of the filter table ("INPUT" must be in capital letters!). The INPUT chain is a built-in chain which is a filtering point for handling packets that are destined for the local system where you are defining these rules.

Since the stateful firewall filter is not a built-in feature, a so called "match extension" must be invoked. The option '-m' tells Netfilter to use a match extension. In the above example I invoked the state match extension or state match module which is loaded automatically with the '-m' option. The state match module ipt_state also loads a few other kernel modules as well:
# lsmod | grep ip
ipt_state               1857  1
ip_conntrack           41369  1 ipt_state
iptable_filter          2881  1
ip_tables              19521  2 ipt_state,iptable_filter

By defining the above rule you may soon realize that it will not work for all applications. For example, in Active Mode FTP the FTP server makes a connection back to the data port on the client side (your node or firewall). This is a new connection which is related to the connection that you initiated by connecting to port 21 on the FTP server. To instruct Netfilter to also accept packets that are not part of an established connection but part of a related connection, you can define the following rule:
# /sbin/iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT


In the following stateful firewall example below I will create a custom chain called "block" which will handle INPUT, and a FORWARD chain. The FORWARD chain handles packets that are being routed through the local system. The FORWARD chain allows other nodes on your local network to use the system as a firewall and router.

To create the "block" custom chain, use the option '-N':
# /sbin/iptables -N block

It may also be useful to always flush all rules before setting up a new set of rules. You can do this with the option '-F':
# /sbin/iptables -F

Example


Basic Stateful Firewall Example

To summarize what has been covered in the introduction, the first iptables commands for building a stateful firewall would look as follows:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT

So far we have specified that all established and related network packets should pass the first filter rule. To use the system as a firewall and gateway for other servers on the local network, Netfilter needs to be told to accept all new network packets coming from the local network. An easy way of doing this is to accept all new network packets that are NOT coming from the DSL line "ppp0", i.e. all network packets that come from the local network through any other interfaces should be accepted:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
If you have a cable modem connected to the interface "eth0", you need to replace "ppp0" with "eth0". The option '-i' specifies the incoming interface. Since the system should be protected from the Internet, the above rule only accepts new network packets (-m state --state NEW) that are not coming via the DSL line (-i ! ppp0).


To log all network packets that don't pass the above rules, Netfilter can be told to create logs. In the following example LOG connects to the syslogger kernel facility:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
/sbin/iptables -A block -j LOG
The logs can be found on Red Hat in /var/log/messages and on SUSE in /var/log/firewall, respectively.


To drop all network packets that don't pass the rules that have been defined so far, the base target "DROP" can be specified. This will cause all network connections to time out. If you want to send back ICMP error packets as response, replace the base target "DROP" with "REJECT".
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
/sbin/iptables -A block -j LOG
/sbin/iptables -A block -j DROP

Now Netfilter just needs to be told to jump to the custom chain "block" from the INPUT chain:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
/sbin/iptables -A block -j LOG
/sbin/iptables -A block -j DROP
/sbin/iptables -A INPUT -j block

Now test the stateful firewall and ensure it's working.

To list the rules, run:
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
block      all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain block (1 references)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere            state NEW
LOG        all  --  anywhere             anywhere            LOG level warning
DROP       all  --  anywhere             anywhere
#


Giving an IP Address Access to the Stateful Firewall

To give an IP address access to the stateful firewall server, you can add the following rule:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
/sbin/iptables -A block -s xx.xx.xx.xx/32 -j ACCEPT
/sbin/iptables -A block -j LOG
/sbin/iptables -A block -j DROP
/sbin/iptables -A INPUT -j block


Stateful Firewall and IP Masquerading

IP Masquerading can be used to give all servers on a local network access to the Internet. There is a subtle difference between Source NAT (SNAT) and Masquerading. SNAT requires a specific address to refer packets, whereas Masquerading can also be used with source addresses that are assigned dynamically by DHCP. Also, with Masquerading, connections are forgotten if the interface goes down.

To enable IP Masquerading, add the following rule to the Stateful Firewall example:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
/sbin/iptables -A block -j LOG
/sbin/iptables -A block -j DROP
/sbin/iptables -A INPUT -j block
/sbin/iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
This rule specifies that all packets that are going out through the "ppp0" interface should change the source IP address to the IP address of the firewall server. The POSTROUTING chain handles all packets immediately prior to leaving the firewall system.

To enable IP masquerading you also need to activate it in the kernel by setting ip_forward to 1. You can do this by adding the following entry to the /etc/sysctl.conf file:
  net.ipv4.ip_forward=1
To have this setting become effective immediately, execute the following command:
# sysctl -p

NOTE: If you have a DSL line and you do masquerading, it is recommended to add the following additional rule:
/sbin/iptables -F
/sbin/iptables -N block
/sbin/iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
/sbin/iptables -A block -j LOG
/sbin/iptables -A block -j DROP
/sbin/iptables -A INPUT -j block
/sbin/iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
/sbin/iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
For more information on this rule, see http://en.tldp.org/HOWTO/IP-Masquerade-HOWTO/mtu-issues.html.

Log Example

Here is an example of a Netfilter log entry in the /var/log/messages file:
  Aug 18 06:05:04 localhost kernel: IN=ppp0 OUT= MAC= SRC=xx.xx.xx.xx DST=yy.yy.yy.yy LEN=78 TOS=0x00 PREC=0x00 TTL=120 ID=22337 PROTO=UDP SPT=137 DPT=137 LEN=58
SRC is the IP address where the request came from.
DST is the target IP address, in this case the IP address of the firewall.
SPT is the port number on SRC where the request came from.
DPT is the port number on the firewall where the request was sent to. In this example the port number is 137. For various port numbers, see also /etc/services.

References

The netfilter/iptables project
IPTABLES Firewall Examples
RH253 Course Material


Copyright © 2007 PUSCHITZ.COM

The information provided on this website comes without warranty of any kind and is distributed AS IS. Every effort has been made to provide the information as accurate as possible, but no warranty or fitness is implied. The information may be incomplete, may contain errors or may have become out of date. The use of this information described herein is your responsibility, and to use it in your own environments do so at your own risk.