Sunday, May 4, 2014

Using Puppet to open port 80 through the iptables command

Puppet provides an add-on module called firewall to manage firewall configuration on your system. I tried it out, and ended up locking myself out of my Vagrant box. All I needed to do was open port 80 on my VM, and the steps mentioned in the Puppet Firewall module setup page seemed like an overkill for something so simple.

So, I decided to understand the iptables command better. This writeup on centos website is an excellent introduction to understanding iptables. Armed with this knowledge, I realized all I needed was:
  1. Make a single rule entry on my Centos VM for allowing incoming traffic on port 80.
  2. Save the state of the iptables rule, so that on restart of the iptables service, this new rule is not lost.
Note: I am working on puppet v3.2.3 and Cento v6.4 Minimal Version.

If you google, most places you will find this command for opening port 80 (didn't work!):

sudo iptables -A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT

What this command essentially says is append (-A) a new rule to INPUT traffic chain, where for NEW connections of type tcp, and destination port (-dport) 80, perform ACCEPT connections.

This command did not actually open the port for me because this command "APPENDS" the rule right at the end of the iptable chain. By default, Centos already came with a rule:

-A INPUT -j REJECT --reject-with icmp-host-prohibited

The line I added, got appended AFTER this rule. Meaning, all requests were getting blocked anyways, and hence my rule to allow incoming traffic to port 80 was never evaluated.

Therefore, what we need to do is to insert our rule before the REJECT rule. To do that, we use the -I (insert) switch, instead of the -A (Append) switch. The -I switch needs to know the line number of the rule to insert at. To see the line numbers of various, use the following command:

ipTables with Line Number (Notice Line 5 Has the Reject Rule)

Using the "sudo iptables -L -n -v --line-numbers" you can see that by default line number 5 has the REJECT rule for all traffic.

Hence the command that we need to allow incoming traffic to port 80 is (works!):

sudo iptables -I INPUT 5 -m state --state NEW -p tcp --dport 80 -j ACCEPT

Now, our rules gets inserted at line 5, and everything works. Based on this knowledge, our puppet script now looks like this (using two execs):

The first exec fires the command only if it does not detect a port 80 entry (using the unless attribute). The unless attribute tests that a grep on iptables-save command contains port 80 entry. The iptables-save command allows us to see the current iptables rule configuration in a parseable format.

The second exec fires the service iptables save  command to save the configuration to disk. This command needs to be fired only if the first exec actually makes an entry. Hence we specify notify attribute in exec and also mark this exec with refreshonly => true so that it is executed only as part of the notify process, and should not be executed otherwise.

That's it! You should be good to go now! You can do the same thing if you also want to make an entry for port 443 (SSL) or anything similar.