Adding iptables Rules With Ansible

Monday, February 17, 2014 - 00:44

Many systems and applications require certain access to certain ports and protocols. When installing these systems using Ansible it is necessary to also open up the needed ports so that the systems can function correctly. As there is no iptables module in Ansible the shell command is needed to add the iptables rules.

As an example, here is a task that adds a iptables rule to allow Apache to communicate on port 80.

1
2
3
- name: Apache | add apache iptable rule
  command: /sbin/iptables -I INPUT 1 -p tcp --dport http -j ACCEPT -m comment --comment "Apache"
  sudo: true

Once this is in place you might need to save and/or restart iptables in order to get the rule to be permanently saved. The following two rules will save the iptables rule and restart the iptables service. Note that these commands are specific to Ubuntu and so might not work on your system setup.

1
2
3
4
5
6
7
- name: save iptables
  command: iptables-save
  sudo: true
 
- name: restart iptables
  service: name=ufw state=restarted
  sudo: true

This allows Apache to communicate, but the trouble is that when the Ansible task is run a second time a second iptables rule will be added. This can cause problems, especially when Ansible is run over and over again to maintain the configuration of the server. For this reason another step must be added to detect for the existence of the rule before it is added. This is done by using the shell module to print the available iptables rules out and store them into a variable. The Ansible keyword 'register' allows any returned value from the current task to be saved as a variable, in this case the variable registered is called 'iptablesrules'.

1
2
3
4
- name: Apache | get iptables rules
  shell: iptables -L
  register: iptablesrules
  sudo: true

With this in place we can now run the iptables insertion command only when we cannot find a rule that matches the one that we want to add. The find function is run on the iptablesrules.stdout parameter to find the word "Apache" within the contents. This matches the comment that was used in the iptables rule creation. and Here is the modified task.

1
2
3
4
- name: Apache | add apache iptable rule
  command: /sbin/iptables -I INPUT 1 -p tcp --dport http -j ACCEPT -m comment --comment "Apache"
  sudo: true
  when: iptablesrules.stdout.find("Apache") == -1

The only problem now is that when checking that an Ansible playbook will execute as expected (by using the --check flag) the added conditional will fail. This is because the iptablesrules variable that is generated is not done in this case and so Ansible will error and complain about a missing variable. The way around this it to force the iptables rules variable registry task to always run, even if we are in check mode. To do this we add the always_run flag to the task.

1
2
3
4
5
- name: Apache | get iptables rules
  shell: iptables -L
  register: iptablesrules
  always_run: yes
  sudo: true

Here is the entire playbook in full. Note that it is better practice to use create the iptables save and restart tasks as handlers and notify them from the needed tasks. This allows them to be reused when generating iptables rules for other services.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- name: Apache | get iptables rules
  shell: iptables -L
  register: iptablesrules
  always_run: yes
  sudo: true
 
- name: Apache | add apache iptable rule
  command: /sbin/iptables -I INPUT 1 -p tcp --dport http -j ACCEPT -m comment --comment "Apache"
  sudo: true
  when: iptablesrules.stdout.find("Apache") == -1
 
- name: save iptables
  command: iptables-save
  sudo: true
 
- name: restart iptables
  service: name= ufw state=restarted
  sudo: true
Category: 
philipnorton42's picture

Philip Norton

Phil is the founder and administrator of #! code and is an IT professional working in the North West of the UK.
Google+ | Twitter

Comments

Great! now there is a module to handle that http://docs.ansible.com/ufw_module.html

It's also worth taking a look at Ferm[1] for managing iptables configuration. It remains a less than perfect solution, but It is possible to add firewall rules with each role. See http://wiki.gema-soft.de/doku.php?id=it-administration:tools:ansible:ferm for an example Ansible Ferm setup.

[1] http://ferm.foo-projects.org/

Great playbook, thanks for sharing. I just added changed_when: false to the "get iptables rules" task so that it does not report as changed when running the playbook.
1
2
3
4
5
6
- name: Apache | get iptables rules
  shell: iptables -L
  register: iptablesrules
  always_run: yes
  changed_when: false  # Never report as changed
  sudo: true

I created a role to ease the process of using iptables with Ansible, check it out: https://github.com/mikegleasonjr/ansible-role-firewall

Thank you

iptables-save doesn't save the iptables...

Another way to do it...

1
2
3
 -name: Save iptables rules           
  command: /sbin/service iptables save
  sudo: True                          

Add new comment