IPTables Firewalling
A *Brief* Overview
Bob Sully (rcs@malibyte.net)
11 August 2001
The Problem:
Internet founded around 1969; initially a network of educational and military (ARPAnet) organizations. No significant concerns about script kiddies, denial of service attacks, viruses, Trojan horses and worms back then. These are now commonplace. More and more 'Net connections are 24/7/365 with stable IP addresses, rather than dialup. These are obviously not moving targets. The porous nature of a well-known proprietary OS makes it a tempting target.
The Solution:
Firewalling. Options:
(1) Hardware firewall - built into a device such as a router. Relatively expensive and not easily modifiable in some cases.
(2) Software firewall:
Micro$oft - Proprietary solutions - some good, some not as good. Most are expensive, some cheap. ZoneAlarm popular, fairly effective, cheap for Windows 95/98, etc. Advantages: fairly simple setup. Disadvantages: Most of the good ones are expensive. ZoneAlarm is the exception; somewhat intrusive, but effective.
Linux - IPChains (2.2 kernels and up), IPTables (2.4). Free, supplied with all current distributions. Advantages: Very solid if firewall scripts coded correctly. Again, free. One firewall box can protect a large masqueraded internal network. Firewall hardware can be minimal (Pentium 133 with 32MB RAM is fine for a home network). Disadvantage: You may have to learn to code the scripts (unless you know someone who already has) - but, hey, you run Linux because you're not lazy, right?
As this is a Linux users' group, it's assumed that you'll be using a Linux box for your firewall. There are several scenarios:
(1) Single machine.
(2) Internal network - firewall machine has two NICs. This is the most common setup for a home or small-business network. Internal machines can run servers, accessed via port-forwarding.
(3) Internal user network and a DMZ (group of servers with limited accessibility both from the Internet and from the internal network). More than one firewall box for corporate networks (more than one potential point of failure); smaller home/office network can use a pseudo-DMZ: one firewall box with three (or more) NICs.
Packet Filtering Introduction:
Packet filtering is a component of a firewall system that either permits or denies packets from passing through it based on a pre-defined policy. It examines the packet for source and destination information, as well as ports, such as telnet or ftp, and either permits it to pass or otherwise rejects it.
For example, a firewall policy (typically called rules) may be defined to permit incoming http access to a particular host, but deny access from other hosts. The packet simply traverses the list of rules until it matches a pattern -- one that either permits it or rejects it.
The advantages of packet filtering are that they are fast, flexible, and inexpensive. A Pentium-133 with 16-32 megabytes of RAM and two ethernet cards can be converted into a packet filtering firewall that can protect a large number of boxen behind it. They also typically provide a good deal of logging information which can be crucial to tracking a potential cracker.
The advantage of packet filtering is also one of its disadvantages. The packet filter does nothing to analyze the contents of the packet to determine if there is any malicious content within it. It simply routes based on a predefined set of rules. Another disadvantage is that it operates based on the information contained in the packet header, and can make no decisions based on the user accessing the remote resource. There is no built-in authorization mechanism.
The final disadvantage is that while it's flexible, it can be difficult to maintain. Often times on networks with many hosts to protect, the set of rules become unwieldy and difficult to manage (this is where a configuration file for the script can be very helpful, as are associated files with lists of specific local rules). The mechanism of the underlying IP protocols must be understood in order for the firewall to be effective. A misunderstanding of how one of the protocols work, or even an inadvertant configuration change can lead to undesired consequences. This is obviously an issue with any security system, but even one erroneous rule in a firewall script could result in serious problems.
Packet filtering is only one component of a firewall system. For a more robust solution, combine packet filtering with an application level gateway program such as squid, snort, or hogwash. Most secure situation: IPTables on Linux firewall box. No servers (web, FTP, mail, etc.) other than SSH; if you run servers, use squid, hogwash, etc. Any Windoze boxen behind the firewall should be running up-to-date antivirus software, and if you're *real* paranoid, ZoneAlarm or similar.
Differences between IPTables and IPChains:
In previous versions of the packet filtering code, it was only possible to determine whether the SYN flag was set, indicating whether or not the packet was an established connection or the beginning of a new connection. The new packet filtering code allows you to filter on specific TCP flags, not just the SYN flag. This permits a much greater level of control over the packets that can enter or leave your network. For example, to examine all six TCP flags, checking specifically for the SYN and ACK flags to be set:
iptables -A INPUT -p tcp --tcp-flags ALL SYN,ACK -j DROP
This connection tracking mechanism provides the ability to associate all the packets of a particular connection with each other.
Stateful inspection:
Some say this is one of the biggest improvements over IPChains.
The behavior of the firewall changes based on the information contained in the packet. If the packet contains information in the header that indicates it is part of an existing connection, and it matches a rule that states it's a permissible service, then it is permitted to pass through the firewall.
In the past, it was difficult to know whether a particular connection to one of those ports were associated with a legitimate connection or was an arbitrary connection, as would be the case with a port scan or malicious crack attempt.
Some applications/protocols attempt to allocate arbitrary ports for their data to be passed through. Previously there was no alternative but to poke holes in the firewall to allow these arbitrary ports access should there be a connection that requires it at some point during a connection.
The new firewalling code allows the firewall to open ports only long enough for that particular packet on an arbitrary port to pass through it. This greatly increases the overall level of security; most commercial firewall software includes this feature. It greatly reduces the window of opportunity for the bad guys to attempt to pass malicious packets through the firewall, despite the source and destination ports and addresses being known. This allows only packets that are recognized as being part of an established connection to pass, instead of previously only depending on destination address and port. This also helps to thwart an attacker's attempt at using packets with tweaked headers that would have previously subverted a stateless packet filtering firewall.
Rules can now be created to base their routing decision on one of the following states:
NEW: This packet is trying to create a new connection. Unless you're running a server you shouldn't allow these on the input side.
RELATED: This packet is related to the existing connection, and is passing in the original direction
INVALID: This packet doesn't match any connection
ESTABLISHED: This packet is part of an existing connection
RELATED+REPLY: This packet is not part of an existing connection, but is related to one, such as an ftp-data transfer when there is already an ftp-control session to that host
As a simple example, to forward across the firewall interfaces packets that are part of a pre-existing connection might look like this:
iptables -A FORWARD -m state -state ESTABLISHED,RELATED -j ACCEPT
Address Translation:
Network Address Translation (NAT) is the process of converting one or more IP addresses into another IP address. This is most commonly seen through the use of IP Masquerading where a home or internal network is translated into an IP address that is routable on the Internet. The new packet filtering code in the 2.4 kernels include a much more robust form of address translation.
The 2.4 kernel packet filtering now supports a one-to-one, many-to-one and even many-to-many IP address and port translation. In the simplest case, an internal address may be translated into one that is addressable on the Internet. In IPChains, this may have been done using something like this:
ipchains -A forward -j MASQ -s 192.168.1.0/24 -d 0/0
This will convert all IP addresses from the 192.168.1.0 network into the address of the external interface of the box it is running on. From there, an entry is made in the internal kernel data structures that tracks the connection and associates it with the particular host from which it originated. This form of proxying is very easy to set up and provides a great solution for internal users with a limited number of Internet-routable IP addresses.
While the masquerading support is still functionally equivalent to its predecessors, additional forms of address translation are now available. The new packet mangling code provides additional forms of translation including the ability to translate the source or destination addresses of a packet, the ports associated with the connection, port forwarding and transparent proxying. Suddenly, with the addition of this improved NAT, load-balancing, fault-tolerance and even high-availability become obtainable. Specifically, the following new NAT options are available:
DNAT: This target specifies that the destination address of the packet should be modified. This is a great way to load-balance a connection request amongst several destination servers.
SNAT: This target specifies that the source address of the packet should be modified, including all future packets in this connection.
REDIRECT: This is a specialized case of DNAT that alters the destination IP address to send the packet to the machine itself. This is useful in circumstances where one wishes to redirect web traffic to a local proxy server, such as squid.
Packets that have had either their source or destination addresses translated will then be retranslated on their return trip, so they are sure to be delivered back to the host with the real IP address.
So, to develop a simple and inexpensive load balancing solution, you might use the following to have your firewall redirect some of the traffic to each of the web servers at 192.168.1.100, 192.168.1.101 and 192.168.1.102, as follows:
iptables -t nat -A POSTROUTING -i eth1 -j DNAT --to 192.168.1.100-192.168.102
Packet Mangling:
Packet mangling is the ability to change or modify packets both before they are routed and after routing has been decided, specifically, the PREROUTING and POSTROUTING subtables.
One example of packet mangling is the ability to prioritize traffic from separate queues, changing the Type of Service value in the packet before it is routed on to its final destination.
The Type of Service values can be one of the following:
Minimize-DelayFor example, to provide interactive performance to telnet while using ftp at the same time:
iptables -A PREROUTING -t mangle -p tcp --sport telnet -j TOS --set-tos Minimize-DelayAnother use of packet mangling involves the MARK target; this is used (as might be inferred) to brand a packet for use by a user-space application.
Logging:
Netfilter improves on previous versions by providing the ability to restrict the rate of matches, such as for suppressing extensive log messages, for example when a host makes a number of connections in a predefined interval. This can help to prevent some types of denial of service attacks.
The example below shows how to reduce the number of packets originating from the 192.168.1.1 host that are logged:
iptables -A INPUT -s 192.168.1.1 -m limit --limit 1/s -j LOG
It is also possible to log messages at different log levels and with different prefix notes:
iptables -A INPUT -s 203.117.0.0/16 -j LOG --log-prefix ' #China Syndrome#'
This will log all traffic coming from the 203.117.x.x subnet and prepend the string #China Syndrome# to each entry. It produces the following log entry:
Aug 5 08:58:56 gomer kernel: #China Syndrome# IN=eth0 OUT= MAC=00:20:78:11:60:9b:00:10:67:00:9a:8c:08:00 SRC=203.117.136.238 DST=64.166.193.211 LEN=78 TOS=0x00 PREC=0x00 TTL=106 ID=38365 PROTO=UDP SPT=138 DPT=137 LEN=58
Additionally, it also provides the ability to log packets based on many other criteria (MAC address, etc).
The ability to match TCP or UDP packets based on a series of source or destination ports is also now available. Previously, a rule could only match a single range of ports. This might be used to set up a filter to block telnet, ftp, and finger, for example:
iptables -A INPUT -p tcp --dport telnet,ftp,finger -j DROP
Other syntax changes:
The names of the built-in chains have changed from lower case to UPPER case, because the INPUT and OUTPUT chains now only get locally-destined and locally-generated packets. They used to see all incoming and all outgoing packets respectively.
The `-i' flag now means the incoming interface, and only works in the INPUT and FORWARD chains. Rules in the FORWARD or OUTPUT chains that used `-i' should be changed to `-o'.
TCP and UDP ports now need to be spelled out with the --source-port or --sport (or --destination-port/--dport) options, and must be placed after the `-p tcp' or `-p udp' options, as this loads the TCP or UDP extensions respectively.
The TCP -y flag is now --syn, and must be after `-p tcp'.
The DENY target is now DROP, finally.
REJECT and LOG are now extended targets, meaning they are separate kernel modules.
Chain names can be up to 31 characters.
MASQ is now MASQUERADE and uses a different syntax. REDIRECT, while keeping the same name, has also undergone a syntax change. See the NAT-HOWTO for more information on how to configure both of these.
Setting up your machine with IPTables:
Stock RedHat/Mandrake kernels come with it enabled (modular). To enable it in a custom-compiled 2.4.x kernel:
Under Networking Options:
Enable Network packet filtering
Under IP: Netfilter configuration - select the following:
<M> Connection tracking (required for masq/NAT) (NEW) - Necessary for masquerading and keeping track of connections, statefulness (new vs. Established/related, etc.).
<M> IP tables support (required for filtering/masq/NAT) (NEW) - Can't use IPTables or masquerade without it.
<M> limit match support (NEW) - Allows a new feature of IPTables which eliminates log flooding.
<M> MAC address match support (NEW) - can block or allow entry based on MAC address of sender or destination.
<M> TOS match support (NEW) - Allows filtering based on service type
<M> Unclean match support (EXPERIMENTAL) (NEW) - Allows filtering of unclean or invalid packets based on header field information.
<M> Packet filtering (NEW) - Obvious
<M> Packet mangling (NEW) - Allows alteration of some features of an outgoing or re-directed packet before it's sent on.
<M> LOG target support (NEW) - Must have Otherwise no log entries will be written.
< > ipchains (2.2-style) support (NEW) - Use IPChains instead of IPTables
< > ipfwadm (2.0-style) support (NEW) - Use ipfwadm instead of IPTables
Some debate exists in re: whether these should be modular or hard-compiled into the kernel. I compile mine in; if your kernel's getting too big, go modular.
Note the last two options: IPChains or IPfwadm scripts can be used with the 2.4.x kernel series - but IPTables can't be enabled at the same time. If you wish to use IPChains, select it and none of the other options above.
Then compile as usual.
Scripting:
A firewall script is a text file which defines the rules by which packets coming into (INPUT), out of (OUTPUT), or
forwarded across (FORWARD) your network (e.g. sent from your firewall to your internal network) are
handled.
For an example of a comprehensive script and its associated configuration files, see my IPTables
translation (with additions and modifications) of Craig Zeller's IPChains scripts at: http://www.malibyte.net/iptables/scripts/fwscripts.html.
Some basics will be outlined here.
The best way to start filtering is to block EVERYTHING in all directions:
iptables -F (flushes the entire rule set)
iptables -P INPUT DROPINPUT, OUTPUT and FORWARD are built-in chains. User-created custom chains are also possible, as below.
DROP - literally drops the packet into the bit bucket. No acknowledgment is sent that it was received.
REJECT - Doesn't let the packet through but lets the originating machine know it was received.
ACCEPT - Lets the packet in to be handled by the next rule in the list.
To do masquerading:
echo 1 > /proc/sys/net/ipv4/ip_forward
If running DHCP or PPP (IP address of external NIC not static):
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
if using static IP address for external interface:
iptables -t nat -A POSTROUTING -s $INT-NETWORK -o eth0 -j SNAT --to $EXT-INTERFACE
in both cases:
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -o $EXT-INTERFACE -s $INT-NETWORK -j ACCEPT
If you'll not be running *any* servers on your network, then some would advise the following:
iptables -A INPUT -i eth0 -m state --state ESTABLISHED, RELATED -j ACCEPT
This essentially blocks any NEW connection attempts on the external interface and only accepts packets related to a connection attempt which was initiated by a machine on your network. This is a very useful tool under the right circumstances. It also blocks INVALID (malformed) packets.
Another useful improvement in IPTables is the ease of creating/deleting user-defined customized chains. This is important given some changes in the logging rules since IPChains. Here's an example of a custom chain:
iptables -N LnD # Define custom chain
iptables -A LnD -p tcp -m limit --limit 1/s -j LOG --log-level warn --log-prefix TCP drop
iptables -A LnD -p udp -m limit --limit 1/s -j LOG --log-level warn --log-prefix UDP drop
iptables -A LnD -p icmp -m limit --limit 1/s -j LOG --log-level warn --log-prefix ICMP drop
iptables -A LnD -f -m limit --limit 1/s -j LOG --log-level emerg --log-prefix FRAG drop
iptables -A LnD -j DROP
This performs the same basic function as the following rule in IPChains, with a few improvements:
ipchains -A input -i $EXTERNAL_INTERFACE -p TCP -s 64.63.88.28 -l -j DENY
IPChains allowed operations on a packet (such as DENY) and logging on the same line (-l). This is not currently do-able in IPTables. This isn't necessarily bad...as the above custom chain allows limiting the number of log entries (-m limit -limit 1/s), in this case one per second, over time, which decreases the possibility of having your server disabled by log flooding. It also allows customization of log entries (-log-level warn -log-prefix jerk ).
Then, begin poking holes in the firewall to suit your needs. For example, to allow access to outside DNS servers for name resolution:
iptables -A OUTPUT -o $EXTERNAL_INTERFACE -p UDP --sport $UNPRIVPORTS --dport 53 -s $EXTERNAL_IP -d $ANYWHERE -j ACCEPT
iptables -A INPUT -i $EXTERNAL_INTERFACE -p UDP --sport 53 --dport $UNPRIVPORTS -s $ANYWHERE -d $EXTERNAL_IP -j ACCEPT
iptables -A OUTPUT -o $EXTERNAL_INTERFACE -p TCP --sport $UNPRIVPORTS --dport 53 -s $EXTERNAL_IP -d $ANYWHERE -j ACCEPT
iptables -A INPUT -i $EXTERNAL_INTERFACE -p TCP --syn --sport 53 --dport $UNPRIVPORTS -s $ANYWHERE -d $EXTERNAL_IP -j ACCEPT
Where:
$EXTERNAL INTERFACE = interface of external NIC (Internet side) - eth0
$EXTERNAL_IP = IP address of eth0 (e.g. 64.63.88.28)
$INTERNAL_INTERFACE = interface of internal NIC - eth1
$INTERNAL_IP = IP address of eth1 (e.g. 192.168.56.1)
$ANYWHERE = any/0
$PRIV_PORTS = 0:1023
$UNPRIVPORTS = 1024:65535
This can be done by hard-coding a series of rules together, one after the other, or (more elegantly) with a configuration file, through which sets of rules can easily be turned on/off:
# Open the configuration file
if [ -f /etc/firewall/firewall.conf.iptables ]; then
. /etc/firewall/firewall.conf.iptables
This reads in the configuration file, which contains a series of variables, such as (among many others):
DNS_CLIENT=1 # Domain Name Servers
Now, each set of rules will be activated or ignored based on the values in the configuration file:
# DNS client modes (53)
#
if [ $DNS_CLIENT -gt 0 ]; then
iptables -A OUTPUT -o $EXTERNAL_INTERFACE -p UDP --sport $UNPRIVPORTS --dport 53 -s $EXTERNAL_IP -d $ANYWHERE -j ACCEPTif [ $VERBOSE -gt 0 ]; then
echo firewall: DNS client enabledfi
fi
Repeat this (set up rulesets) for all of the other services which need to be enabled.
For examples of how to enable access to other services, see the generic example script on my web site.
Then, make sure that everything which has fallen through the filter (therefore not expressly ACCEPTed, REJECTed, or DROPped) be rejected, or dropped, and logged:
# DROP (on input), REJECT (output) and LOG anything else on the Internet interface
iptables -A INPUT -i $EXTERNAL_INTERFACE -p TCP -s $ANYWHERE -j LnDDon't like text editors? Spoiled by GUIs? Take a look at IPMenu (http://users.pandora.be/stes/ipmenu.html)
Resources:
Bartley, Dirk - Netfilter Presentation, Kalamazoo LUG (http://kalamazoolinux.org/presentations/20010417)
Russell, Rusty - Rusty's Unreliable Guides (the HOWTO by the main man)
(http://netfilter.samba.org/unreliable-guides/index.html)
Wreski, Dave - Linux Kernel Firewalling Matures: Netfilter (http://linuxsecurity.com/feature_stories/kernel-netfilter.html)
Zeller, Craig - His original IPChains configuration file and firewall scripts:
(http://www.zdi.net/Linux/firewalls.html)
Tons of example scripts available at: http://www.linuxguruz.org/iptables
Join the Netfilter mailing list: http://us4.samba.org/mailman/listinfo/netfilter
This page accessed times since 1 May 2002.
Free Hit Counter