iptables is the default firewall and routing program instituted in GNU/Linux since mid-1999.
Contents
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