A common issue when setting up iptables on a new cloud server is that users may append the record to the existing chain, without looking at the ruleset first.
Iptables is read top to bottom, with a default installation of CentOS 5.5, the command iptables-L –line-number yields the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[root@TestServer01 ~]# iptables -L --line-number Chain INPUT (policy ACCEPT) num target prot opt source destination 1 RH-Firewall-1-INPUT all -- anywhere anywhere Chain FORWARD (policy ACCEPT) num target prot opt source destination 1 RH-Firewall-1-INPUT all -- anywhere anywhere Chain OUTPUT (policy ACCEPT) num target prot opt source destination Chain RH-Firewall-1-INPUT (2 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere 2 ACCEPT icmp -- anywhere anywhere icmp any 3 ACCEPT esp -- anywhere anywhere 4 ACCEPT ah -- anywhere anywhere 5 ACCEPT udp -- anywhere 224.0.0.251 udp dpt:mdns 6 ACCEPT udp -- anywhere anywhere udp dpt:ipp 7 ACCEPT tcp -- anywhere anywhere tcp dpt:ipp 8 ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED 9 ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh 10 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited |
Looking at this, you can see that the INPUT chain has a single rule: to go read the RH-Firewall-1-INPUT chain. That chain then has 10 rules, with the last one being to reject all traffic. This means that if it isn’t explicitly allowed in rules 1-9, it ain’t gonna happen.
The problem comes in when you try to add a new rule using the -A flag which appends the rule, meaning that the new rule goes to the bottom. Here is an example of that, and is what you do NOT want to do:
1 |
iptables -A INPUT -p tcp --dport 80 -j ACCEPT |
Let’s assume that we did run this command. The new output of iptables -L –line-number would be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@TestServer01 ~]# iptables -L --line-number Chain INPUT (policy ACCEPT) num target prot opt source destination 1 RH-Firewall-1-INPUT all -- anywhere anywhere 2 ACCEPT tcp -- anywhere anywhere tcp dpt:http Chain FORWARD (policy ACCEPT) num target prot opt source destination 1 RH-Firewall-1-INPUT all -- anywhere anywhere Chain OUTPUT (policy ACCEPT) num target prot opt source destination Chain RH-Firewall-1-INPUT (2 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere 2 ACCEPT icmp -- anywhere anywhere icmp any 3 ACCEPT esp -- anywhere anywhere 4 ACCEPT ah -- anywhere anywhere 5 ACCEPT udp -- anywhere 224.0.0.251 udp dpt:mdns 6 ACCEPT udp -- anywhere anywhere udp dpt:ipp 7 ACCEPT tcp -- anywhere anywhere tcp dpt:ipp 8 ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED 9 ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh 10 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited |
See anything wrong here? Let’s look at the INPUT chain. The first rule is to read the RH-Firewall-1-INPUT chain, which has 10 rules. After it reads through that chain, the next rule from the INPUT chain would be read, the rule that we just added for opening port 80.
Problem is, RH-Firewall-1-INPUT said in line 10 to reject anything that didn’t match. That means that your rule for opening port 80 will never even be looked at, requests will just be rejected.
Ok, so we need to remove the bad rule and do it right. First, let’s get rid of the bad rule by removing it based off of the line number
1 |
iptables -D INPUT 2 |
To break this command down for you:
iptables: should be pretty obvious…
-D: This option is for DELETE
INPUT: Specify the chain we want to delete from
2: Specify the line number of the rule to remove.
After running that, my bad rule from above will be gone. Now I need to do it the RIGHT way:
1 |
iptables -I INPUT -p tcp --dport 80 -j ACCEPT |
This rule looks an awful lot like the one above that I told you not to use, but look closely and you will see that instead of -A for append, this rule uses -I for insert, which will put the rule at the TOP of the list. Running iptables -L –line-number now yields the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@TestServer01 ~]# iptables -L --line-number Chain INPUT (policy ACCEPT) num target prot opt source destination 1 ACCEPT tcp -- anywhere anywhere tcp dpt:http 2 RH-Firewall-1-INPUT all -- anywhere anywhere Chain FORWARD (policy ACCEPT) num target prot opt source destination 1 RH-Firewall-1-INPUT all -- anywhere anywhere Chain OUTPUT (policy ACCEPT) num target prot opt source destination Chain RH-Firewall-1-INPUT (2 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere 2 ACCEPT icmp -- anywhere anywhere icmp any 3 ACCEPT esp -- anywhere anywhere 4 ACCEPT ah -- anywhere anywhere 5 ACCEPT udp -- anywhere 224.0.0.251 udp dpt:mdns 6 ACCEPT udp -- anywhere anywhere udp dpt:ipp 7 ACCEPT tcp -- anywhere anywhere tcp dpt:ipp 8 ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED 9 ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh 10 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited |
Nice – Now the rule about allowing port 80 will be read FIRST, and then it will read the RH-Firewall-1-INPUT chain.
Always remember to save! If you do not save your ruleset, when the box reboots all of your rules will be lost!
For Redhat, CentOS, and Fedora:
1 |
/etc/init.d/iptables save |
For Ubuntu:
1 |
iptables-save > /etc/iptables.rules |
For all other distros:
1 |
iptables-save > /etc/sysconfig/iptables |