Introducing iptables part 2

This article provides an overview of how to configure the Linux kernel firewall for ipv4 using iptables and the Filter table. It is intended for beginners to intermediate linux users and provides an insight on syntax, adding and deleting rules.


In the Introducing iptables part 1 article we briefly discussed the Filter table and chains as well as backing up and restoring configurations. This article will cover adding and removing rules.

Adding and Removing Rules

We'll step through the process of recreating the example iptables rules file to get a better insight on how it works.

Some of the most common uses of iptables would be to Allow and Block traffic to the slice.

It's a quite common practice to block all incoming traffic and then open specific ports and protocols, so let's do that. But first let's flush the current rules and create a rule to allow incoming ssh traffic so we're not disconnected. Change port 30000 to the port your ssh service is running on.

sudo /sbin/iptables -F
sudo /sbin/iptables -A INPUT -i eth0 -p tcp -m tcp --dport 30000 -j ACCEPT

The above command uses the -A flag to append the rule to the end of the INPUT chain. The -i flag states what interface to apply the rule to. The -p flag indicates the protocol to use while the -m flag is used to match the rule to the tcp module. The --dport allows you to specify a destination port. Be sure to change 30000 to the port your ssh service is running on!! And the -j flag specifies the action to take when there is a rule match.

Let's also allow all ESTABLISHED and RELATED traffic using the state module with the -m flag.

sudo /sbin/iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Now let's get to blocking all incoming and forwarding traffic.

sudo /sbin/iptables -A INPUT -j REJECT
sudo /sbin/iptables -A FORWARD -j REJECT

Let's also allow all outgoing and loopback traffic

sudo /sbin/iptables -A OUTPUT -j ACCEPT
sudo /sbin/iptables -I INPUT -i lo -j ACCEPT

The -I flag was used in the loopback rule to add the rule to the beginning of the chain. This is because the rules go in order. With the REJECT all from anywhere rule in the INPUT chain before our ACCEPT loopback rule, localhost traffic was being blocked before it could be allowed.

And if we would want to insert a rule in the INPUT chain that drops all traffic to the loopback ip that doesn't originate from the loopback device, we would want it inserted at the beginning as well.

sudo /sbin/iptables -I INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

This rule is read as 'not on interface lo' with the '!' character representing not.

Now take a look at the current rules with the following command:

sudo /sbin/iptables -L --line-numbers

The --line-numbers flag includes the rule line numbers which can be useful when inserting rules into and dropping rules out of chains. We will get into this momentarily and your output should look something like:

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  anywhere             127.0.0.0/8         reject-with icmp-port-unreachable 
2    ACCEPT     all  --  anywhere             anywhere            
3    ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:30000
4    ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
5    REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  anywhere             anywhere        

Let's go ahead and enable ping responses. This can be useful when troubleshooting server and website-related issues as well as being a helpful monitoring tool. But, let's put the rule at line number 3.

sudo /sbin/iptables -I INPUT 3 -p icmp -m icmp --icmp-type 8 -j ACCEPT

The -I flag is used to insert the rule into the INPUT chain at the rule number 3 position in this example. If you wanted to delete or drop a rule you could do it with the -D flag and a similar syntax.

**EXAMPLE ONLY**
sudo /sbin/iptables -D INPUT 3

Now, as long as you didn't just delete your ping rule, you can verify that it was inserted at the proper line number with:

sudo /sbin/iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  anywhere             127.0.0.0/8         reject-with icmp-port-unreachable 
2    ACCEPT     all  --  anywhere             anywhere            
3    ACCEPT     icmp --  anywhere             anywhere            icmp echo-request 
4    ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:30000
5    ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
6    REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  anywhere             anywhere            

Let's go ahead and enable http and https traffic to come in as well. We'll add these at position 5 in our INPUT chain.

sudo /sbin/iptables -I INPUT 5 -p tcp --dport 80 -j ACCEPT 
sudo /sbin/iptables -I INPUT 5 -p tcp --dport 443 -j ACCEPT

And finally, we will enable logging with the following:

sudo /sbin/iptables -I INPUT 8 -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

Your end result should look something like:

sudo /sbin/iptables -L -n --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  0.0.0.0/0            127.0.0.0/8         reject-with icmp-port-unreachable 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 8 
4    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:30000 
5    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:443 
6    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:80 
7    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
8    LOG        all  --  0.0.0.0/0            0.0.0.0/0           limit: avg 5/min burst 5 LOG flags 0 level 7 prefix `iptables denied: ' 
9    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-port-unreachable 

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-port-unreachable 

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

We used the -n flag in our list to display port numbers as well as source and destination ip's.

Be sure to save your iptables configuration:

sudo sh -c '/sbin/iptables-save > /etc/iptables.save'

Summary

Appending, inserting and deleting rules from the Filter table using iptables is quite trivial once you know what you're looking at. Knowing the syntax and rule flow gives us better understanding of the firewall process. In the next article, we go over the iptables startup configuration as well as some useful examples.

  • -- Natorious

Article Comments:

Scott commented Mon Feb 28 14:46:40 UTC 2011:

If you get permission denied when running:

sudo /sbin/iptables-save > /etc/iptables.save

Try instead:

sudo -s

/sbin/iptables-save > /etc/iptables.save

exit

Scott commented Mon Feb 28 14:50:23 UTC 2011:

In my last comment, the line breaks did not show up right for some reason.

"sudo -s" and "exit" are two separate commands surrounding the iptables-save command.

Jered commented Mon Feb 28 17:57:30 UTC 2011:

Thanks Scott. Another way to do it all on one line would be to use sudo to run "sh -c" with it, as in:

sudo sh -c "/sbin/iptables-save > /etc/iptables.save"

Either approach will work. We'll update that part of the article too.

Sean commented Sun Mar 27 07:37:34 UTC 2011:

Thanks for this. These instructions have been most helpful.

Anyway, I noticed that the first two input rules are reversed when one imports your example rules file (@ 2007/9/4/rules.txt) rather than building them individually per the instructions on part-2.

1 REJECT all -- anywhere 127.0.0.0/8 reject-with icmp-port-unreachable 2 ACCEPT all -- anywhere anywhere

Here is what I see when the iptable rules are copied and pasted directly from the example txt file:

1 ACCEPT all -- anywhere anywhere
2 REJECT all -- anywhere 127.0.0.0/8 reject-with icmp-port-unreachable

Does this minor ordering variation have any functional impact? The idea of having accept all as the first rule kinda feels weird, but I'm probably missing something. My concern was that a packet would stop looking for a match in the list after having reached a non-LOG rule where it matches (such as this accept all anywhere).

Thanks again!

Sean commented Sun Mar 27 21:00:46 UTC 2011:

Here's a quick follow up to my concern about the accept all. Following a quick 'sudo iptables -L -v' command, it was revealed that the that accept all was limited to the loopback interface per the iptables.txt instruction: "ACCEPT all -- lo any anywhere anywhere"

Much less scary than I originally though. :)

Ulysses commented Thu Jul 25 17:11:57 UTC 2013:

sudo -s worked for me. Thank you. Great post.

Ulysses commented Thu Jul 25 17:13:04 UTC 2013:

sudo -s worked for me. Thank you. Great post.

Ulysses commented Thu Jul 25 17:13:47 UTC 2013:

sudo -s worked for me. Thank you. Great post.

Want to comment?


(not made public)

(optional)

(use plain text or Markdown syntax)