Adding iptables Rules With Ansible

Note: The Ansible built in role ansible.builtin.iptables makes this post largely irrelevant.

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.

- 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.

- 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'.

- 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.

- 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.

- 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.

---
- 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

Comments

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.
- name: Apache | get iptables rules
  shell: iptables -L
  register: iptablesrules
  always_run: yes
  changed_when: false  # Never report as changed
  sudo: true
Permalink
iptables-save doesn't save the iptables...
Permalink
Another way to do it... -name: Save iptables rules command: /sbin/service iptables save sudo: True
Permalink

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
5 + 12 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.