Now, supposedly nftables is replacing iptables, so look out for that.
A Few Notes
First, if you are wanting to completely overhaul your iptables consider deleting all iptables that are there (see Delete Rules section).
Second, know that more than likely you will lock yourself out of a server if you try editing iptables remotely. So, be prepared in case you do this by making sure you have access to a direct console to the computer. If you are using a hosting service like Rackspace, Linode, or Amazon you will be able to do it through their website.
Third, working with iptables is easiest when you are logged in as root. Or, run "sudo su -".
Fourth, if there are pre-existing firewall rules and you are modifying them be VERY VERY VERY careful. Like, write out on paper what you're going to do then type a line and read it three or four or fifteen times before hitting enter. Treat it with the same care you would "rm -rf".
Last, if you're looking for a quick and dirty "I don't care what my iptables are I want to wipe them clean and do something simple and secure" then see the Simple iptables Setup Example.
How to Read iptables
They are tested from top to bottom. If a rule matches and accepts or declines, then the process is stopped.
So you need to code exceptions first, then more general rules.
Source vs Destination
It's important to know the difference between the source port and destination port, because you have to specify which for every rule you make. However, to understand source vs destination you have to know how a TCP packet is constructed. Every TCP packet has a source IP, source port, destination IP, and destination port. So, you on your computer at 192.168.0.150 requesting a web page from your web server at 192.168.0.10 would send a packet with source IP of 192.168.0.150, source port of 80, destination IP of 192.168.0.10, and destination port of 80. Now, on the trip back the server would send a packet with source IP 192.168.0.10, source port 80, destination IP 192.168.0.150, and destination port 80.
Now, imagine you are ssh'd into a server and your local port is 55980 and the remote port is 22. The packet sent from your computer would have source IP 192.168.0.150, source port 55980, destination IP 192.168.0.10, and destination port 22. The packet coming back from the server would have source IP 192.168.0.10, source port 22, destination IP 192.168.0.150, and destination port 55980.
So, when you are writing rules you generally write INPUT rules with source IP and destination port and OUTPUT rules with destination IP and source port. Now, if you think it about it this makes sense. An INPUT tcp packet's source IP will be the IP of the remote machine and the destination port will be the port on your local machine. Most of the INPUT rules you write are targeting a remote IP or your local port. An OUTPUT tcp packet's destination IP will be the IP of the remote machine and the source port will be the port of your local machine. Most of the OUTPUT rules you write are targeting a remote IP or your local port. Thus, the general format of most of your rules will look like:
$ iptables -A INPUT -p tcp -s 192.168.0.150 --dport 22 -j ACCEPT
$ iptables -A OUTPUT -p tcp -d 192.168.0.150 --sport 22 -j ACCEPT
Again, this is a general rule not a requirement. Here are some further reading links on source vs destination:
Simple iptables Setup Example
If you want a simple example here is what I use for very simple stupid security:
# completely clear iptables (removes anything that pre-existed) iptables -F iptables -X # accept everything no matter port on localhost iptables -A INPUT -i lo -j ACCEPT # allow input on the following ports iptables -A INPUT -p tcp --dport 22 -j ACCEPT # allow output on the following ports iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT iptables -A OUTPUT -p tcp --sport 443 -j ACCEPT # set default policy to drop iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP
This will deny everything incoming except port 22 and allow all outgoing on port 22, 80, and 443. You will probably want outgoing 80 and 443 for running system updates and such. The result looks like:
Chain INPUT (policy DROP) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 Chain FORWARD (policy DROP) target prot opt source destination Chain OUTPUT (policy DROP) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:22 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:80 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:443
Now, if you want something that's more usable I have written a setup script gist iptables-setup.sh.
This section I will talk about how to test for open ports. The easiest way is:
$ nc -l 80
Run the above on your server then make a request to it from outside your server to see if you get a response, for example:
$ echo "this port is open" | nc 192.168.0.150 80
Then, on your server 192.168.0.150 you should see "this port is open" in your console. If it's not open you won't see anything in your console on the server and netcat (nc) on your client will hang / timeout.
To list all rules, run:
$ iptables --list 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
My favorite way to list rules is the following:
$ iptables --list --numeric --line-numbers --verbose
This will list them as all IP's (--numeric) and show line numbers for each rule (--line-numbers) and show what network interface is used (--verbose). The reason I like this is because I'm always working with IP's, not hostnames, and if you know the line number it makes it easier to delete.
Setting Default Policies
Show Your Default Policy
First and foremost, know your default policy. Your default policy will show up just to the right of your chain, for example:
$ iptables --list Chain INPUT (policy ACCEPT) ...
That shows a default policy of ACCEPT. So, any rule not matching the rules listed will be accepted.
$ iptables --list Chain INPUT (policy DROP) ...
That shows a default policy of DROP. So, any rule not matching the rules listed will be dropped.
I find the easiest way to manage security is to make your default policy DROP. This ensures that only the traffic you specify will get through.
Set Your Default Policy
To set your default policy, run:
$ iptables -P INPUT ACCEPT
where "INPUT" can be whatever your chain is (e.g. "FORWARD" or "OUTPUT") and ACCEPT can be "DROP" or "RETURN".
Adding and Removing Rules
Before you begin blocking or opening ports you need to know how to add and remove rules. A rule will state whether a port is open or closed.
Add A Rule
The format for adding a rule is:
$ iptables -A INPUT -i eth0 -j ACCEPT
Where "INPUT" can be changed to "FORWARD" or "OUTPUT", "eth0" can be changed to whatever your ethernet device is, and "ACCEPT" can be changed to "DROP" or "RETURN".
Add A Rule At a Line
Say you are OCD and you want to insert a rule at line two given the following rules:
Chain OUTPUT (policy DROP 0 packets, 0 bytes) num target prot opt source destination 1 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:80 2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:6667
To do this, you would run:
$ iptables -I OUTPUT 2 -p tcp --sport 443 -j ACCEPT
Now, you have inserted a rule at line two and your ports are listed in ascending order :)
Chain OUTPUT (policy DROP 0 packets, 0 bytes) num target prot opt source destination 1 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:80 2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:443 3 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:6667
Drop A Rule
The format for dropping a rule is:
$ iptables -D INPUT 1
Where "INPUT" is whatever chain you are editing, usually "INPUT", "FORWARD", or "OUTPUT" unless you create your own. And, "1" is the line number given from iptables --list --line-numbers.
Delete All Rules
To delete all rules, run:
# Flush the selected chain (all the chains in the table if none is given). This is equivalent to deleting all the rules one by one. $ iptables -F
To delete the entire chain:
# Delete the optional user-defined chain specified. There must be no references to the chain. If there are, you must delete or replace the referring rules before the chain can be deleted. The chain must be empty, i.e. not contain any rules. If no argument is given, it will attempt to delete every non-builtin chain in the table. $ iptables -X
Running the above two commands will completely clear your iptables for you if you are wanting to start from scratch. A clean rule set will look like:
$ iptables -L 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
Before you open any ports you will most likely want to close the ones that you don't want through. So, here is how you block a port:
$ iptables -A INPUT -p tcp --dport 22 -j DROP
This will drop all incoming traffic to port 22.
The following will allow all incoming traffic to port 22:
$ iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Backing Up, Saving, Resetting, and Restoring iptables
Finally, on to backing up, saving, and restoring.
Backing Up iptables
To back up iptables, run:
$ iptables-save > /etc/iptables.bup
Where "/etc/iptables.bup" is where you want to save them. One time I saved them in /etc/sysconfig/iptables.bup, fwiw.
By default, iptables do not get saved to disk. This means that on restart your firewall rules will be destroyed. To save iptables, run:
$ /etc/init.d/iptables save
To check if they saved you can run /etc/init.d/iptables restart. However, if you are on Ubuntu you have to install iptables-persistent and then save them to:
$ iptables-save > /etc/iptables/rules.v4
Say you changed your iptables but don't like what you did. To reset your iptables to the last saved state, run:
$ /etc/init.d/iptables restart
You can also run this to check if your iptables were saved properly.
To restore iptables from a file, run:
$ iptables-restore < /etc/iptables.bup
A Note on UFW
Before you go digging around, check to see if your system is using ufw. If it is, commands are as simple as:
$ sudo ufw allow ftp
$ sudo ufw allow 222
An Odd Note
If you don't have an iptables, you may need to set them up by adding the following file, /etc/network/if-pre-up.d/iptablesload, with the following contents:
#!/bin/sh iptables-restore < /etc/iptables.rules exit 0
This tutorial explains in more depth:
Topics Not Covered
- http://serverfault.com/a/533130 (an interesting conversation about iptables)
- http://www.thegeekstuff.com/scripts/iptables-rules (example script)
- http://edoceo.com/howto/iptables (more examples)