iptables is the default firewall and routing program instituted in GNU/Linux since mid-1999.

Contents

  1. Single-line Examples
    1. Closing ports
      1. Closing all ports
      2. Closing a specific port
    2. Opening ports
      1. Opening all ports
      2. Opening a specific port
    3. NAT, etc
      1. Redirecting one port to another
      2. Redirecting to another IP
      3. Redirecting outbound traffic
      4. Listing NAT rules
  2. Tutorial
    1. Clearing and listing your ruleset
    2. Dropping all traffic
    3. Re-opening service ports
    4. Testing the configuration
    5. Saving and restoring your configuration
  3. Theory
    1. REJECT vs DROP
    2. -I (Insert) vs -A (Append)
  4. Example Configurations
    1. An extremely simple webserver
    2. A webserver that also routes/NATs
  5. References and Other Documentation

Single-line Examples

Closing Ports

Closing all Ports

iptables -I INPUT -j DROP

This command will DROP all incoming traffic. Replacing INPUT with OUTPUT will drop all outgoing traffic as well, but for the most part firewalls are more concerned with what the internet is sending at them than what their users are sending at the internet.

Closing a specific port

iptables -I INPUT -j DROP -p tcp --dport 80

This command will DROP all incoming tcp traffic to port 80.

Opening Ports

Opening all Ports

iptables -I INPUT -j ACCEPT

This command will ACCEPT all incoming traffic. By default, iptables accepts all traffic, so you could also accomplish this by flushing the rules:

iptables -F

Opening a specific port

iptables -I INPUT -j ACCEPT -p tcp --dport 80

This command will ACCEPT incoming tcp traffic to port 80.

NAT, etc

Redirecting one port to another

iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

This command will REDIRECT all tcp traffic from port 80 to port 8080.

Redirecting a port to another IP:port

iptables -t nat -I PREROUTING -p tcp --dport 88 -j DNAT --to-destination 192.168.0.4:80

This command will DNAT all tcp traffic from port 88 to 192.168.0.4 port 80

Redirecting outbound traffic

iptables -t nat -I POSTROUTING -s 192.168.0.0/16 -o eth0 -j SNAT --to-source 72.29.66.200

This command will SNAT all traffic from 192.168.* outbound to eth0 and out as 72.29.66.200.
This would be useful for using iptables to route between two NICs where your internal network is 192.168.0.0/16 and eth0 is your external-interface with the IP of 72.29.66.200.

Listing NAT rules

iptables -nL -t nat

You can also add the --line-numbers argument here if you wish to modify/delete NAT lines using -D.


Back to contents

Tutorial

Clearing and listing your current set

This example, done from a HN with two separate VE's, shows how they will communicate - as well as how DROP vs REJECT will work. First, from inside the first VE we do iptables -nL which shows our rules as numerical (-n) (rather than with hostnames) and lists them (-L)"

VE1:/# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

There are no rules. If we did have rules, we could clear them by flushing them:

iptables -F

Dropping all traffic

Now we DROP all traffic coming into our machine, then list our rules:

VE1:/# iptables -I INPUT -j DROP
VE1:/# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP 0 -- 0.0.0.0/0 0.0.0.0/0

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

It is out of the realm of this example, but consider in the world of IP addresses that 0.0.0.0/0 equals all, or * if you prefer. Likewise, although this does not show ports, that would be redundant, as it is DROPping for all ports. [1]

Re-opening service ports

Now we wish to open a port so that anyone can access it:

VE1:/# iptables -I INPUT -j ACCEPT -p tcp --dport 80

This line can be loosely interpreted as "Insert (-I) to the table INPUT a rule that will jump (-j) all transmission control protocol (tcp) (-p=protocol) that has a destination port (--dport) of 80 to be ACCEPTed"

Now we list our rules again:

VE1:/# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination 
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
DROP 0 -- 0.0.0.0/0 0.0.0.0/0

Chain FORWARD (policy ACCEPT)
target prot opt source destination 

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Here we see that the ACCEPT rule is above our original DROP rule. This is important as the rules are followed in the order that they are "hit" (top to bottom) - just as with junk-mail filters and much else. So incoming traffic would come, and if it had a destination port of 80, it would be ACCEPTed - if it did not match that rule, it would continue to the next rule, which says to DROP everything. Had we use -A (append) instead of -I (insert) the new rule would appear at the bottom of this list.

Testing the configuration

Now note that we are on VE2 to attempt to port 80 on VE1:

VE2:/# telnet VE1 80
Trying 192.168.0.66...
Connected to VE1.
Escape character is '^]'.

We area able to connect to port 80.

Now from VE1 we will specifically DROP traffic to port 80:

VE1:/# iptables -I INPUT -j DROP -p tcp --dport 80

...or since we have all traffic DROPped already, we could just delete the ACCEPT line we put in earlier. This is accomplished by listing the line-number of the rule we wish to delete. You can list your ruleset with line-numbers by specifying iptables -nL --line-numbers. Once you have the line-number you use -D to delete it:

VE1:/# iptables -D INPUT 1

Saving and restoring your configuration

Rather than running a shell script with the entire iptables -I INPUT -j ... ruleset, or typing them manually, you can use two commands to save and retrieve rulesets.

# iptables-save > iptables.rules

iptables-save will save the ruleset if an easily-edited and modified text-file. This file is conveniently in the format required by iptables-restore. You can test this by saving your ruleset, flushing it, and then restoring it. As a note for paranoia, many people run the restore line in an rc.* startfile, or in their crontab. Neither of those two options are discussed in this file, as they are broader subjects for other articles to cover.

# iptables-restore < iptables.rules

Theory

REJECT vs DROP

REJECT sends a message back to the originator letting them know they cannot access what they are attempting to. As far as the internet is concerned, this is the proper way to do things, and most definitely the polite way; however, people often no longer think of the internet as a polite place for research, and do not wish for others to know that their machine is giving any answer at all, even if it is to REJECT it. DROP simply throws it away. Below shows the difference between a DROP and REJECT from the client-standpoint.

DROP

VE2:/# telnet VE1 80
Trying VE1...

This will stay here until a timeout occurs as their machine does not receive a REJECT to know that they cannot connect. As far as their machine is concerned it is just taking a while.

REJECT

VE2:/# telnet 192.168.0.66 80
Trying 192.168.0.66...
telnet: Unable to connect to remote host: Connection refused

This then disconnects as the machine has been told that it cannot connect.

-I (Insert) vs -A (Append)

INSERT puts the rule at the top of the ruleset while APPEND adds it to the bottom. This is very important as rules in iptables are followed in a top-down order. Thus, if you -I an ALLOW that matches a REJECT/DROP that was already on the table, the ALLOW will win; however, if you -A an ALLOW that matches a REJECT/DROP that was already on the table, the REJECT/DROP will win.


Back to contents

Example Configurations

In these configurations I'm adding the rules as if you were entering them one-per-line at the console. Thus, the first -I will inevitably end up at the bottom of the list. So make sure your ruleset reads in the right order before implementing it.

An extremely simple webserver

iptables -I INPUT -j DROP
iptables -I INPUT -j ACCEPT -p tcp --dport 21 #FTP
iptables -I INPUT -j ACCEPT -p tcp --dport 22 #SSH
iptables -I INPUT -j ACCEPT -p tcp --dport 80 #HTTPD
iptables -I INPUT -j ACCEPT -p tcp --dport 110 #POP3
iptables -I INPUT -j ACCEPT -p tcp --dport 143 #IMAP
iptables -I INPUT -j REJECT -p tcp --dport 25 #Let SMTP connections know they're rejected

The setup above is pretty simple, yet should work for most simple web-serving applications with only one machine.

A webserver that also routes/NATs

Using the above configuration as a base, say we now have a second NIC and would like to pass traffic from our webserving machine at our house to our home wireless router (or another machine). We will still use the beginning lines from An extremely simple webserver setup, but will add more to handle the NAT. Our router assigns DHCP to the wireless units connected to it, using a 192.168.0.0/24[1] subnet. Our external IP is the 72.29.66.200. We also have a DMZ machine on the localnet that has a static IP of 192.168.1.5 which will have HTTPD accessible to the outside world on port 8080.

Thus, our setup is: INTERNET->(eth0)->WEBSERVER/ROUTER->(eth1)->WIRELESS ROUTER->[WIRELESS MACHINES & DMZ]

iptables -t nat -I POSTROUTING -s 192.168.0.0/16 -o eth0 -j SNAT --to-source 72.29.66.200
iptables -t nat -I PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.5:80

Yep, those two rules just turned us into a router. We now pass all traffic from our 192.168/16[1] outbound through our external interface, as well as allowing traffic starting on our external interface on port 8080 to go to our destination machine on port 80.


Back to contents

References and Other Documentation

  1. TCP/IP Subnet Chart