Saturday, March 14, 2009

iptables Tutorial

iptables Tutorial

This brief tutorial is meant to assist those unfamiliar or just getting started with iptables. All facets of iptables will not be covered, and for those looking for more information on the topic, please refer to the references I've included at the end of this article for further assistance. Specific items I will cover are:

1. Overview of what iptables is, and the components of the program.
2. What are 'Tables'?
3. Enabling the Firewall and clearing the old rules
4. Creating custom chains for logging
5. Getting started and defining a restrictive 'filter' table chain for the INPUT and OUTPUT chains.
6. Viewing and manipulating rules
7. Summary


Let's get started!

1. Overview of what iptables is, and the components of the program.
So what is netfilter and iptables? I'll use the explanation provided by Michael and Scott Shinn from their book "Troubleshooting Linux Firewalls", "netfilter comprises the kernel level code that Linux can use to conduct packet filtering, state management, NAT, packet mangling, QOS, and other neat tricks. iptables is the userland tool that can manipulate these kernel hooks to do these things." In essence, we use iptables to create and manage our firewall ruleset. Let's now look at what makes iptables.


2. What are 'tables' and 'chains' for?
iptables consists of 4 different tables (see below). Each table is used to control different functions for your iptables firewall. In some instances you will use more than one of the tables (for example, when your firewall is configured as the gateway device for which all network traffic will traverse), but for a single host performing basic filtering, the only table required is the filter table.

4 Tables that make up iptables:
Filter
Mangle
Raw
NAT

Within each table you have various chains which you can use to define access for. The filter table consists of 3 chains by default. Each of the filter chains are used for different purposes. For traffic passing through your firewall, say you have a multihomed firewall/server, you would define what is allowed to pass in the FORWARD chain. Since we will be creating a personal firewall, which consists of only one Network Interface Card (NIC), we will be utilizing only the INPUT and OUTPUT chains. These will define access allowed to our machine (DHCP Offer or ping requests for instance) and traffic leaving our machine (DNS lookups or Web Browsing) respectively.

Default chains for the filter table:
FORWARD : This is for traffic which will traverse your firewall. We will not be using this chain, since our firewall will not be a gateway.
INPUT : For traffic destined to the firewall. This would be used to define access rights for allowing administrators to use SSH to connect to the firewall.
OUTPUT : For traffic leaving the firewall. If this machine is your personal computer, you might want to allow http traffic outbound in this chain.

Next are targets, which triggers an action when a packet matches the rule. Targets are preceded by '-j' which tells the rule to jump to a specific chain or target. For this you could have it point to a custom chain, or one of the targets below.

Target actions which will be present in our rules:
ACCEPT - Allows the traffic matched by the rule
DROP - Traffic is dropped and no further processing is performed
LOG - Logs a packet to syslog, and the rules continue to be processed

As for all the other options available, I will discuss them when we proceed further to building our actual ruleset. I would like for those who have never worked with iptables before, to read the 'NOTE' I have posted after this paragraph. Since we will be configuring the firewall for stateful operation, instead of basic packet filtering, it is important that the connection tracking module be loaded in the kernel. If it isn't, please utilize the references I provided in order to install and enable the necessary modules.

NOTE:

Like I mentioned earlier, this tutorial assumes you have iptables installed already, and that the connection tracking module (provides the ability to perform stateful filtering) is loaded. For this tutorial, I am using Fedora 9, and didn't have to make any special changes or tweaks to the kernel, in order to add stateful filtering. For a much more exhaustive look into this, look at the references below for more information (you will want to keep this in mind if you ever configure your firewall as a passthrough device and want to utilize ftp, since ftp has its own separate module - which was not loaded for me by default). You can verify whether the module is loaded with the command below. I also included output concerning my kernel version below:

Validate whether the connection tracking module is currently loaded, in order to allow stateful filtering:

lsmod |grep nf_conntrack
nf_conntrack_ipv4 11528 3
nf_conntrack_ipv6 15864 3
nf_conntrack 51424 3 nf_conntrack_ipv4,nf_conntrack_ipv6,xt_state
ipv6 230260 20 ip6t_REJECT,nf_conntrack_ipv6


Verify Kernel Version:
uname -r
2.6.27.15-78.2.23.fc9.i686

3. Enabling the Firewall and clearing the old rules
Getting started, we will want to ensure that iptables is running. If it's not, we will go aheaad and enable iptables for runlevels 3,4,5 and then start the service.

In order to validate what runlevels iptables is configured to run at:
chkconfig --list iptables

Now to enable iptables for runlevels 3,4,5, if it is not already:
chkconfig --levels 345 iptables on

Start the iptables service, if not done so already:
service iptables start

Most firewalls, by default, allow all traffic to exit the firewall, instead of a default deny - which is considered much more secure. We will be altering the INPUT and OUTPUT chains for our filter table, so that by default all traffic is dropped. We will then proceed to allow only the services (protocol and port combination - or well know services like ssh which use tcp port 22 by default) which we require to do our jobs. This will leave us in a much more secure posture. Let's begin by setting the default behavior for our chains.

Set the default behavior of the INPUT and OUTPUT chain to drop traffic:
iptables -P INPUT DROP
iptables -P OUTPUT DROP

Next we will flush all the rules currently pre-defined for our chains. You may want to list, and/or save the contents first, and I provided the command which will allow you to do this as well.

List all current rules contained in the chains ('-L' lists the rules, '-n' provides the port numbers, instead of just the named service as contained in '/etc/services' and '--line-numbers' will display the actual number of the rules):
iptables -L -n --line-numbers

Save the current rules (I added a date here, and you can add this or something else in order to help you remember when you last backed it up):
iptables-save > old.rules.saved.03142009

The command below will flush '-F' all rules you currently have defined:
iptables -F

Now you can save your work:
iptables-save

For those who want to save the current ruleset, and make it persistent across a reboot, use the following:
service iptables save

We will we doing this throughout this paper, in order to ensure that our changes are saved, in case your machine is inadvertantely rebooted while we're working on our ruleset.


4. Creating custom chains for logging
Let's create 2 new custom chains in order to alleviate the headache of having to create 2 separate rules for logging and accepting/dropping traffic. What I am providing is a bare minimum, as there are many more options available for these custom chains. For those looking for more information on this, use the resources listed below!

Looking at the example provided below, the '-N' option is used to create a new user defined chain. The names I gave for these chains are 'LOGDROP' and 'LOGACCEPT'. After defining the new chain name, we use the append '-A' statement to add a rule to the LOGDROP and LOGACCEPT chains, set an action '-j' to LOG the traffic, and add a label to the log entry with '--log-prefix'. So when we receive traffic that matches our LOGDROP and LOGACCEPT rules, we will see the prefix of LOGDROP and LOGACCEPT in our logs.

Creating a new LOG and DROP Chain:

iptables -N LOGDROP
iptables -A LOGDROP -j LOG --log-prefix "LOGDROP: "
iptables -A LOGDROP -j DROP

iptables -N LOGACCEPT
iptables -A LOGACCEPT -j LOG --log-prefix "LOGACCEPT: "
iptables -A LOGACCEPT -j ACCEPT

You can elaborate on much of this, by creating custom chains in order to group similar rules to be processed by iptables, as well as to specify different tags to be given to log entries. I highly recommend doing this if you'll be in charge of managing a large ruleset, as it does make management of your firewall much easier, and log search times can be dramatically reduced.


5. Defining a restrictive 'filter' table chain for the INPUT and OUTPUT chains.
First we will have to determine what traffic we will want to allow/deny to and from our firewall. If your developing this for a corporate environment, you may want to create a procedure for this and put a policy in place that states something to the point that if someone in your corporate environment is using a Linux Desktop, they must have a restrictive local firewall policy configured. To go further with this, you could even send all of this log data to a centralized syslog server, which would help in log correlation - say in the event of a virus outbreak.

So for me, I'm going to define a restrictive ruleset (yes, I know, I can be much more restrictive in nature if I wanted to) for both incoming and outgoing traffic. If you know the IP addresses of your DNS and DHCP servers, add them to the rules. Since the machine I am configuring is used while I travel, many of these addresses change with each location, so I chose to be liberal in what I defined as source/destination addresses.

For Loopback interface traffic, we will allow everything, and this will be added to the INPUT and OUTPUT chains.
Incoming services we want to allow: DHCP (UDP68), SSH, icmp echo-requests
Outgoing services we want to allow: DHCP (UDP67), SSH, icmp echo-requests, http, https, dns


Rules for the INPUT Chain:

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state INVALID -j LOG --log-prefix "DROP INVALID: " --log-ip-options --log-tcp-options
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p udp --dport 68 -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m state --state NEW -j LOGACCEPT
iptables -A INPUT -j LOGDROP

NOTE: So what did we just do here? Let me discuss some of this.
Rule 1: The first rule states that we want to append '-A' to our INPUT chain a rule which allows all traffic to our loopback interface.
Rule 2 & 3: The second rule specifies that if we receive a packet which is invalid, an example of this is receiving an initial packet with the FIN flag set (such as an nmap FIN scan '-sF') iptables will log it (as well as record the tcp and ip options) and drop the traffic - as specified in the 3rd rule.
Rule 4: The fourth rule is defined to allow already established connections and those related to another connection (an example of a related connection would be ftp which uses both ports 21 and 20).
Rule 5: This rule is defined to allow icmp requests to our machine
Rule 6: Rule 6 allows our client to receive a reply from the DHCP server. We will receive a DHCP Offer and Acknowledgement on UDP port 68.
Rule 7: Rule 7 allows incoming ssh, or port 22, connections and logs '-j LOGACCEPT' the connection for us as well.
Rule 8: Lastly we have a rule which jumps '-j' to our customly created chain 'LOGDROP', which effectively logs the connection and drops it.

Something to keep in mind on the last rule, and our custom chain in general, is that you may want to set connection limits/threshholds for it, or possibly not log at all on dropped packets. We have specified that all dropped packets be logged, which - depending on how much disk space you have available - can eat up a lot of disk space. If for instance, you have an attacker on your network and they know your logging all dropped packets, they could essentially target your machines, and try to perform a DoS by filling up your disk space. I wanted to state this so the reader is aware of potential drawbacks to logging all dropped traffic.

Rules for the OUTPUT Chain:

iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -m state --state INVALID -j LOG --log-prefix "DROP INVALID: " --log-ip-options --log-tcp-options
iptables -A OUTPUT -m state --state INVALID -j DROP
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -p udp --dport 67 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 22 --syn -m state --state NEW -j LOGACCEPT
iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -p tcp -m multiport --dports 80,443 --syn -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 --syn -m state --state NEW -j ACCEPT
iptables -A OUTPUT -j LOGDROP

NOTE: Here we have defined the OUPUT chain for our iptables firewall. This is to govern all traffic that leaves our local machine. Please refer to the NOTE above for an explanation on the 1st 4 rules.
Rule 5: Since DHCP servers listen on UDP 67 for client connections, rule 5 was defined to allow this connectivity. UDP 67 is used for DHCP DISCOVER and REQUEST operations.
Rule 6: In rule 6, we are allowing outgoing SSH connections from our client, since we frequently connect to other *nix devices from our workstation.
Rule 7: Allows outgoing ping's from our machine.
Rule 8: Here we allow connectivity to the web for http and https services.
Rule 9: This allows normal dns queries out from our machine in order to resolve names. By default DNS uses UDP.
Rule 10: This is defined to not only allow zone transfers (which uses TCP), but also if our DNS server receives a query over 512 bytes in size, the server will ask our machine to resend the request over TCP.
Rule 11: Lastly we have a rule which jumps '-j' to our customly created chain 'LOGDROP', which effectively logs the connection and drops it.

Now that we have a functional and more secure local firewall defined, save the configuration once more and we're done!

Save the Configuration:
iptables-save
service iptables save

6. Viewing and manipulating rules
Now that we're done creating our ruleset, I'd like to cover some other options that are available for viewing and manipulating the rules. At some time or another you will need to make changes to your current ruleset by adding, changing or deleting rules, and these commands should come in handy.

#'L'ist all iptables rules without resolving '-n' the service names, while printing #the line numbers '--line-numbers'. This can be helpful when trying to determine #what rules to replace, or where to insert new rules in the ruleset.
iptables -L -n --line-numbers

#'A'ppend a new rule to your ruleset. This will add a new rule to the bottom of your #ruleset.
-A

#This will 'I'nsert a new rule into position '5' in the INPUT chain in our ruleset.
-I INPUT 5

#This will 'R'eplace the 5th rule in the INPUT chain. You might do this if you want #to make a change to a rule, such as adding a log prefix.
-R INPUT 5

#Add a 'N'ew chain, like we did earlier for the LOGDROP and LOGACCEPT chains.
-N

#'Z'ero the packet and byte counters for a selected chain
-Z

#'F'lush, which deletes all the chains, or a specific chain if you specify it.
-F

#Print all rules in the selected chain. If no chain is selected, all chains are #printed like iptables-save.
-S

#Much more information is contained in the 'man' pages than what I've covered thus #far! For additional questions, consult it!
man iptables


7. Summary

There has been a lot covered in this 'brief' tutorial! My goal was to provide an entry level overview of iptables. I covered how to create a ruleset for a local firewall, with some details on how to manipulate the ruleset. The references I provided below contain much more information than what I wrote about, so for those looking for methods to use for more complex environments, I strongly suggest buying one of the books below, and taking a look at the online references - which are free! If there any questions in relation to this content, please let us know!

References:
Mike Rash - No Starch Press - 2007 - "Linux Firewalls"
Michael and Scott Shinn - Addison Wesley - 2005 - "Troubleshooting Linux Firewalls"
Gregor Purdy - O'Reilly Media - August 2004 - "Linux iptables Pocket Reference"

Links:
http://www.netfilter.org/
http://iptables-tutorial.frozentux.net/iptables-tutorial.html
http://www.linuxhomenetworking.com/wiki/index.php/Quick_HOWTO_:_Ch14_:_Linux_Firewalls_Using_iptables
http://www.yolinux.com/TUTORIALS/LinuxTutorialIptablesNetworkGateway.html

Ruleset:

iptables -F
iptables -N LOGDROP
iptables -A LOGDROP -j LOG --log-prefix "LOGDROP: "
iptables -A LOGDROP -j DROP
iptables -N LOGACCEPT
iptables -A LOGACCEPT -j LOG --log-prefix "LOGACCEPT: "
iptables -A LOGACCEPT -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state INVALID -j LOG --log-prefix "DROP INVALID" --log-ip-options --log-tcp-options
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p udp --dport 68 -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m state --state NEW -j LOGACCEPT
iptables -A INPUT -j LOGDROP
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -m state --state INVALID -j LOG --log-prefix "DROP INVALID" --log-ip-options --log-tcp-options
iptables -A OUTPUT -m state --state INVALID -j DROP
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -p udp --dport 67 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 22 --syn -m state --state NEW -j LOGACCEPT
iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -p tcp -m multiport --dports 80,443 --syn -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 --syn -m state --state NEW -j ACCEPT
iptables -A OUTPUT -j LOGDROP
iptables-save
iptables service save

Enjoy,

Matt

No comments:

Post a Comment