Iptables for newbies

''Iptables is a userspace command line program that can plug into netfilter (a set of hooks inside the Linux kernel that allows kernel modules to register callback functions with the network stack). Iptables is used to set up, maintain, and inspect the tables of IP packet filter rules in the Linux kernel.''

Introduction
I found the iptables documentation available to be severely unfriendly to newbies. Most assumed a more than working knowledge of ipchains and pretty much picked up from there. Usually my approach to a new endeavor is that I want the option of getting up and going quickly, with minimal explanation. Afterwards I go back to read over more advanced options. This howto is designed with that in mind. It will show you how to setup basic functionality get you connected quickly and then it will incrementally show how to add policies and rules that can help a safe connection.

Enable Kernel Support
You'll need to compile iptables support into the kernel (either within the kernel itself or through modules). You can use either a manual configuration or sys-kernel/genkernel. Either way, go to your kernel sources and modify your configuration:

Once you get to the menu configuration you will need to enable to following depending on your kernel version and whether or not you want to also support IPv6. If you're just a newbie, then just go ahead and enable all of the options as shown below because it will be the quick and dirty way of getting everything working at the expense of increase your kernel's size.

When compiling 2.6.26-r1, be sure to add IP mangling support:

If you intend to load iptables as a module (meaning you selected M instead of * for various kernel options), don't forget to enable automatic kernel module loading:

Now, compile and install your kernel.

Emerging iptables
The kernel components you added to your kernel will not do anything without the iptables package installed. To install it, emerge net-firewall/iptables.

Rules
Iptables moves packets based on a list of rules composed of two components: a match and an effect. For every network packet that iptables inspects, it checks the list and picks the first rule that the packet satisfies. If there is no match, nothing special happens. If there's a match, the effect described for that match is executed on the packet.

Basic Configuration
The average home user might want something that accepts certain incoming connections but blocks everything else. Open in your favorite text editor and insert the following text.

You can now load the above file using the following command: Now, you can tell iptables about this file:

Although the rules have now been added to iptables, they are not saved explicitly. As a result, you should have iptables save the newly added rules using the following command:

Now try and start iptables:

The reason we start, then stop, then start again is because we haven't yet started the iptables script, so we must set the �initialized� status before stopping. Stopping essentially erases all settings and puts you back to zero. If everything works well and you don't seem to have lost access to the server, then you can add iptables to the default runlevel. This will have the iptables service start up each time you boot the computer.

GUI Alternatives
There are three programs that are designed to allow you to configure iptables easily:
 * (for KDE users)
 * (uses QT interface)
 * (uses QT interface)

Firewall Hardening (Advanced Configuration)
In the following, we're going to further secure our now functional firewall. At the end of this section, you should now have a set of tested rules and policies that will prevent not only attacks to your own computer, but also attacks from your computer to the Internet. Protecting others from the possibility of being attacked by one of our compromised computers is an essential--and often--overlooked aspect of security. It is also common Internet courtesy and probably a very important aspect for a small office/home office (SOHO) network. Normally, virus infection is only a minor nuisance to a small network and rarely results in data loss. However, since small SOHO networks are often less secure than larger corporate networks, they are a favorite target for crackers looking for a �"launchpad" for denial of service (DoS) attacks or other underhanded skullduggary.

Setting up environment variables
We will define our networks interfaces and various tools used in the script:

Working with ACCEPTs
This first part will have you work with ACCEPTs. ACCEPTs allows other computers to communicate with our server. In reality, this is very ill advised. A solid firewall policy should really DENY, then ACCEPT. But DENYing now will cause you to lose all connections while you are testing - a very bad thing to happen when you are not sure if your ACCEPT rules work at all. Therefore, while we are entering this first, it will be the second to last rule set in the final script.

iptables DROP & REJECT
Next we are going to define a couple of custom chains which will log DROP and REJECT events. This way we don't have to enter a separate line for each command entered. The logs will be sent to where your syslog default log messages are sent (usually /var/log/messages). Later I'm going to write a grep/sed script that will parse and organize these for easy viewing and set it as a daily cron job. This should be inserted immediately after the above definitions. When you are done, run the script again. It should have no affect on functionality of the network since we're just setting definitions. But it will ensure that we have no errors thusfar.

iptables flushing
Ok, now that we see our devices are being detected properly, we are going to insert a flush commands. So that when our rules are assigned it will be done cleanly. These lines should be inserted after our utilities definitions, the last one being:

Local interfaces
Now we're ready to start laying down some rules. First we are going to accept all packets from our loopback device if the ip address matches that of any of our local interfaces:

Blocking broadcasting
Now we will block broadcasts both incoming and outgoing. This can prevent DoS attacks against us, as well as preventing our clients from being used to DoS someone else. This is part of what's called "Egress Protection". It's a do unto your neighbour sort of philosophy. If all SysAdmins followed this policy, than many of the more severe and costly DoS attacks would either not have occurred or been extremely limited. Now test the script once more to ensure we have no syntax errors. Also notice that we are using our newly defined DROPl chain. This means that the dropped packets will be logged.

Block WAN to LAN
Next we are going to block WAN access to our LAN if not specifically intended for our ISP assign IP:

Tightening the internal LAN
We're going to apply the same logic to our internal LAN. In other words, any packets not originating from our predefined internal network will be rejected: Next we do some more Egress checking of outgoing packets and stop all icmp requests except for pinging:  $IPT -A OUTPUT -o $EXTIF -s ! $EXTNET -j DROPl $IPT -A OUTPUT -o $EXTIF -p icmp --icmp-type ! 8 -j DROPl $IPT -A FORWARD -o $EXTIF -p icmp --icmp-type ! 8 -j DROPl  Ok, where moving along now and we should test the script for errors.
 * 1) An additional Egress check
 * 1) Block outbound ICMP (except for PING)

Ports
Assuming an all clear we're going to start plugging some of the more bothersome port holes:  COMBLOCK="0:1 13 98 111 137:139 161:162 445 1214 1999 2049 3049 4329 6346 3128 8000 8008 8080 12345 65535" TCPBLOCK="$COMBLOCK 98 512:515 1080 6000:6009 6112" UDPBLOCK="$COMBLOCK 161:162 520 123 517:518 1427 9000 9 6346 3128 8000 8008 8080 12345 65535"  After defining the environment variables all we have to do is a simple for loop to assign rules to them all:  echo -n "FW: Blocking attacks to TCP port" for i in $TCPBLOCK; do echo -n "$i " $IPT -A INPUT  -p tcp --dport $i  -j DROPl $IPT -A OUTPUT -p tcp --dport $i  -j DROPl $IPT -A FORWARD -p tcp --dport $i -j DROPl done echo "" echo -n "FW: Blocking attacks to UDP port " for i in $UDPBLOCK; do echo -n "$i " $IPT -A INPUT  -p udp --dport $i  -j DROPl $IPT -A OUTPUT -p udp --dport $i  -j DROPl $IPT -A FORWARD -p udp --dport $i -j DROPl done echo ""  Ok, now with iptables each time we run the script it simply appends these to already existing chains...so things are probably getting a bit messy. For that reason we're going to jump to the beginning of our script....right after the enviroment variables for sed and grep, but before those of EXTIP and EXTBC and add a loop that deletes and flushes. This ensure we're working from a clean state. We didn't want to do that before because we couldn't have tested our script without either shutting down our connection or dropping our firewall completely. This script first sets all policies to DROP, than flushes and deletes our chains. In order to ensure that we can still ssh back into our server after a script restart we are going to append an INPUT chain for ssh. This should always be placed at the end of the script for now. This done in order to prevent a window from opening up while we reset rules which is a common error made:  $IPT       -P INPUT       DROP $IPT       -P OUTPUT      DROP $IPT       -P FORWARD     DROP
 * 1) COMmon ports:
 * 2) 0 is tcpmux; SGI had vulnerability, 1 is common attack
 * 3) 13 is daytime
 * 4) 98 is Linuxconf
 * 5) 111 is sunrpc (portmap)
 * 6) 137:139, 445 is Microsoft
 * 7) SNMP: 161,2
 * 8) Squid flotilla: 3128, 8000, 8008, 8080
 * 9) 1214 is Morpheus or KaZaA
 * 10) 2049 is NFS
 * 11) 3049 is very virulent Linux Trojan, mistakable for NFS
 * 12) Common attacks: 1999, 4329, 6346
 * 13) Common Trojans 12345 65535
 * 1) TCP ports:
 * 2) 98 is Linuxconf
 * 3) 512-515 is rexec, rlogin, rsh, printer(lpd)
 * 4)   [very serious vulnerabilities; attacks continue daily]
 * 5) 1080 is Socks proxy server
 * 6) 6000 is X (NOTE X over SSH is secure and runs on TCP 22)
 * 7) Block 6112 (Sun's/HP's CDE)
 * 1) UDP ports:
 * 2) 161:162 is SNMP
 * 3) 520=RIP, 9000 is Sangoma
 * 4) 517:518 are talk and ntalk (more annoying than anything)
 * 1) Deny than accept: this keeps holes from opening up
 * 2) while we close ports and such

CHAINS=`cat /proc/net/ip_tables_names 2>/dev/null` for i in $CHAINS; do   $IPT -t $i -F done for i in $CHAINS; do   $IPT -t $i -X done $IPT -A INPUT  -i $INTIF1 -p tcp --dport 22 --syn -m state --state NEW -j ACCEPT 
 * 1) Flush all existing chains and erase personal chains

Sysctls
Right afterwards we are going to activate the sysctl's for tcp_syncookies, icmp_echo_ignore_broadcasts, rp_filter, and accept_source_route. Heretofore many of the rules we've been "testing" haven't been able to actually work. In essence we were simply doing syntax error tests. Now our rules will be "for real":  echo 1 > /proc/sys/net/ipv4/tcp_syncookies echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 1 > $f done for f in /proc/sys/net/ipv4/conf/*/accept_source_route; do echo 0 > $f done for f in /proc/sys/net/ipv4/conf/*/accept_redirects; do echo 0 > $f done echo 1 > /proc/sys/net/ipv4/ip_forward  Now we're going to add ftp connection tracking so that we won't get PASV errors when emerging packages:  MODULES="ip_nat_ftp ip_conntrack_ftp" for i in $MODULES; do echo "Inserting module $i" modprobe $i done 
 * 1) Source Address Verification
 * 1) Disable IP source routing and ICMP redirects
 * 1) Opening up ftp connection tracking

Basic Service NAT
Now back to end of our script, we are going to open up services for systems behind our firewall. I have included services such as IRC, MSN, ICQ, and NFS, FTP, domain, and time. And some others. The important thing to note is that these will ONLY be availabe BEHIND the firewall. So this will not enable someone to ftp into your LAN:  IRC='ircd' MSN=1863 ICQ=5190 NFS='sunrpc' PORTAGE='rsync' OpenPGP_HTTP_Keyserver=11371 TCPSERV="domain ssh http https ftp ftp-data mail pop3 pop3s imap3 imaps imap2 time $PORTAGE $IRC $MSN $ICQ  $OpenPGP_HTTP_Keyserver" UDPSERV="domain time" echo -n "FW: Allowing inside systems to use service:" for i in $TCPSERV; do echo -n "$i " $IPT -A OUTPUT -o $EXTIF -p tcp -s $EXTIP --dport $i --syn -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF1 -p tcp -s $INTNET1 --dport $i --syn -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF2 -p tcp -s $INTNET2 --dport $i --syn -m state --state NEW -j ACCEPT done echo ""
 * 1) We have to sync!!
 * 1) All services ports are read from /etc/services

echo -n "FW: Allowing inside systems to use service:" for i in $UDPSERV; do echo -n "$i " $IPT -A OUTPUT -o $EXTIF -p udp -s $EXTIP --dport $i -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF1 -p udp -s $INTNET1 --dport $i -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF2 -p udp -s $INTNET2 done echo ""  Now we're done all that's left is allowing us to ping the outside world by opening up pinging out of the firewall:  $IPT -A OUTPUT -o $EXTIF -p icmp -s $EXTIP --icmp-type 8 -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF1 -p icmp -s $INTNET1 --icmp-type 8 -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF2 -p icmp -s $INTNET2 --icmp-type 8 -m state --state NEW -j ACCEPT
 * 1) Allow to ping out

$IPT -A OUTPUT -o $INTIF1 -p icmp -s $INTNET1 --icmp-type 8 -m state --state NEW -j ACCEPT $IPT -A OUTPUT -o $INTIF2 -p icmp -s $INTNET2 --icmp-type 8 -m state --state NEW -j ACCEPT  Now we are going to default to DROP and Log anything that's left in case we overlooked something. The ACCEPT entry's we made at the very beginning would come right before this in the final script:  $IPT -A INPUT            -j DROPl $IPT -A OUTPUT           -j REJECTl $IPT -A FORWARD          -j DROPl </tt> And you're done. I had a friend nmap and nessus my connection with this rule set and as far as both of them were concerned the only thing it was even slightly sure about was that the ip existed...other than that nothing. I can IRC, MSN, ICQ, ane emerge sync to my hearts content. PART III will cover setting up some essential SOHO services like NFS and CUPS in a security conscious manner.
 * 1) Allow firewall to ping internal systems
 * 1) Log & block whatever is left

Scripts
A couple scripts.

The full script
Now here's the full script from the above information in all its glory (I also put the ssh forwarding in a more appropriate place):

Another iptables startup script
Another iptables startup script designed for a local linux router / linux server.

Another script
This script can be found on HOWTO Iptables and stateful firewalls as well.

Reset iptables
This will make iptables accept everything:  iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F iptables -X </tt>

Interface configuration
The author created this document is using a PPPoE connection to the internet and the 2.6.x kernel.

However, the only adaptation that would need to be made is to replace 'ppp0' with 'eth0' (or whatever NIC you have facing the internet -- this will become clear later).

In my set up, I have three NIC's, one is connected to the WAN through PPPoE, the other two to my internal network. In order for them all to play nicely with iptables and masquerading (NAT'ing), they must be set to different subnets. For example, the two NIC's connected to my internal computers, e.g., the �internal NIC's�, are assigned: 192.168.1.1 and 192.168.2.1 respectively. It should be noted here that it is perfectly acceptable to connect these internal NIC's to any network capable device, such as a switch or hub. For pppoe connections we make sure the NIC connected to the outside world, e.g. the external NIC is not assigned any ip....its entries in should be left blank. This is due to pppoe acting as a virtual device which handles bringing up the NIC. We must also assign proper netmasks and broadcast values to these interfaces. Your should look like these:

Server:

Client One:

Client Two:

The gateways for the clients are set to the internal ip's of the NIC on the server as should be expected. Now add all the interfaces to the default run level and restart connections:

Start network cards
Now verify that you are connected to the internet on the server machine (the clients will not be.....yet) and that all the interfaces can ping each other.

Test connection
Server

Next ensure that your clients have appropriate DNS's set in your.

Troubleshooting
Failure on COMMIT lines iptables failures will nearly always occur on the COMMIT lines. This is because while the rules are read earlier, iptables does not check the match (-m) option until this point. The actual error will be somewhere between the COMMIT line where the error occurred and the previous COMMIT line.

The first things to check for when you get errors are typos and that you have the correct modules loaded (if you're unsure, the easiest way is to build all the features into the kernel - you might want to skip features marked experimental for this as they may cause issues and you're unlikely to be using them).

One way to locate the line causing the problem is to disable all your rules (this can be done by commenting them out by placing a # at the beginning of the line) and then re-enabling them one-by-one until the error occurs.

Another way of finding the erroneous line is to run your firewall script with the bash parameter -x. This will cause the bash shell to print every line of the script before executing it and makes debugging quite easy:

This will output something like this: + IPT=/sbin/iptables + /sbin/iptables -F + /sbin/iptables -P INPUT DOP iptables: Bad policy name + /sbin/iptables -P OUTPUT DROP [...] Retrieved from "http://www.gentoo-wiki.info/HOWTO_Iptables_for_newbies"