# using assemble to build iptables rules

**URL:** <https://forum.ansible.com/t/using-assemble-to-build-iptables-rules/14000>\
**Category:** Ansible Project\
**Tags:** rhel, ubuntu\
**Created:** [November 25, 2013, 10:04pm UTC](https://forum.ansible.com/t/using-assemble-to-build-iptables-rules/14000 "2013-11-25T22:04:37Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![James\_Martin2](https://avatars.discourse-cdn.com/v4/letter/j/b9e5f3/32.png) [@James\_Martin2](https://forum.ansible.com/u/James_Martin2)\
**Post date:** [November 25, 2013, 10:04pm UTC](https://forum.ansible.com/t/using-assemble-to-build-iptables-rules/14000/1 "2013-11-25T22:04:37Z")

</div>

So I've been thinking through a clean way to build iptables rules sets  
with some re-use between debian/ubuntu/redhat/centos world, and this  
is the strategy I've come up with in pseudo code:

in a common bootstrap role:

create /etc/syconfig/iptables.d

in a debian/ubuntu bootstrap role:  
install iptables-persist, disable ufw

In any of your application roles, have a template or file with contents like:

-A INPUT -p tcp -m state --state NEW -m tcp --dport 4369 -j ACCEPT  
-A INPUT -p tcp -m state --state NEW -m tcp --dport 6000:7999 -j ACCEPT

that gets dropped into /etc/sysconfig/iptables.d

In a "chinstrap" role (runs after all other roles), use the assemble  
module to sum everything in /etc/sysconfig/iptables.d/ together and  
place it in /etc/sysconfig/iptables (RedHat) or /etc/iptables/v4.rules  
(Ubuntu), and notify a handler to restart iptables if the assembled  
file has changed.

The tricky part was the ordering, but that seems ok -- the chinstrap  
role creates a segment named 0000-begin that contains all the  
necessary beginning entries for the iptables config, and zzzz-end that  
contains the ending bits. Everything else gets merged in between  
begin & end.

Does this sound kosher? It works in the technical sense. If only  
firewalld existed in RHEL \< 7.

- James

---

<div class="post-metadata">

**Author:** ![Nick\_Groenen](https://avatars.discourse-cdn.com/v4/letter/n/c77e96/32.png) [@Nick\_Groenen](https://forum.ansible.com/u/Nick_Groenen)\
**Post date:** [November 26, 2013, 10:02am UTC](https://forum.ansible.com/t/using-assemble-to-build-iptables-rules/14000/2 "2013-11-26T10:02:51Z")

</div>

Yes it does, this is almost the same approach I've taken. There's a  
few gotcha's to be aware of, however.

- iptables has multiple tables (filter, nat, mangle, raw and security,  
I believe), "filter" is just the default table. If you're  
concatenating your fragments together like this, you're locked into  
always using filter for your rules. I solved this by:  
&nbsp;&nbsp;\* Having per-table begin and end snippets, with the table name  
prefixed to the snippet name  
&nbsp;&nbsp;\* Having per-table snippets with actual rules, again with the table  
name prefixed to the snippet name

- iptables and ip6tables have their own iptables-restore and  
ip6tables-restore respectively, so you need two sets of snippets, one  
for ipv4 and one for ipv6. Although 90% of your rules will probably  
look identical, there are subtle nuances so I would stress not to try  
and use the same rules-file for iptables and ip6tables.

- fail2ban [1] and other similar tools that add their own chains, have  
these chains wiped out when iptables-restore is run. My "firewall  
activate" role fires a handler which restarts fail2ban for this  
reason.

I wrote a small module [2] to help with making snippets in other roles  
though, as using template or copy for it felt like exposing too much  
implementation detail. I still supply the rule in a "raw" form, but  
allow the module to abstract away a few things. This way I can just  
set present/absent on it, specify the iptables version (4 or 6) as  
well as the table (with a default of "filter"). In playbooks, it looks  
like this:

- name: Open up firewall ports 80 and 443  
&nbsp;&nbsp;iptables:  
&nbsp;&nbsp;&nbsp;&nbsp;state: present  
&nbsp;&nbsp;&nbsp;&nbsp;ipversion: "{{ item.version }}"  
&nbsp;&nbsp;&nbsp;&nbsp;name: "{{ item.name }}"  
&nbsp;&nbsp;&nbsp;&nbsp;rules: "{{ item.rules }}"  
&nbsp;&nbsp;with\_items:  
&nbsp;&nbsp;&nbsp;- name: 50\_nginx  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version: 4  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rules: |  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT  
&nbsp;&nbsp;&nbsp;- name: 50\_nginx  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version: 6  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rules: |  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT  
&nbsp;&nbsp;when: nginx\_firewall\_integration|default(False)

Since in this case (usually it will be this way, but not always) the  
rules for 4 and 6 are identical, it can also be condensed to:

- name: Open up firewall ports 80 and 443  
&nbsp;&nbsp;iptables:  
&nbsp;&nbsp;&nbsp;&nbsp;state: present  
&nbsp;&nbsp;&nbsp;&nbsp;ipversion: "{{ item }}"  
&nbsp;&nbsp;&nbsp;&nbsp;name: 50\_nginx  
&nbsp;&nbsp;&nbsp;&nbsp;rules: |  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT  
&nbsp;&nbsp;with\_items: [4,6]  
&nbsp;&nbsp;when: nginx\_firewall\_integration|default(False)

[1]: [http://www.fail2ban.org](http://www.fail2ban.org)  
[2]: [https://gist.github.com/zoni/7655561](https://gist.github.com/zoni/7655561)

---

<div class="post-metadata">

**Author:** ![James\_Martin2](https://avatars.discourse-cdn.com/v4/letter/j/b9e5f3/32.png) [@James\_Martin2](https://forum.ansible.com/u/James_Martin2)\
**Post date:** [November 26, 2013, 3:20pm UTC](https://forum.ansible.com/t/using-assemble-to-build-iptables-rules/14000/3 "2013-11-26T15:20:43Z")

</div>

This is great stuff, thanks!

---

<div class="post-metadata">

**Author:** ![Joost\_Cassee](https://avatars.discourse-cdn.com/v4/letter/j/898d66/32.png) [@Joost\_Cassee](https://forum.ansible.com/u/Joost_Cassee)\
**Post date:** [November 26, 2013, 9:15pm UTC](https://forum.ansible.com/t/using-assemble-to-build-iptables-rules/14000/4 "2013-11-26T21:15:02Z")

</div>

Hi James,

If you do not mind using a tool, ferm [1] configuration is very, very close to close to the iptables syntax and includes an @include statement that makes the assemble step unnecessary. Oh, and it has syntax for managing the ipv4/ipv6 duplication.

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

Regards,  
Joost
