Custom chains in iptables with Ansible
I was really upset when I realized that an Ansible iptables
module does not support creation of custom chains. Initially I’ve assumed they’re going to be creates automatically, as soon as I added a single rule into a new chain. Nope.
...
iptables: No chain/target/match by that name.", "rc": 1, "stderr": "iptables: No chain/target/match by that name.\n", "stderr_lines": ["iptables: No chain/target/match by that name."]
Moreover, there is no way to create it in explicit way (iptables module have open PR for that (at the moment of writing, end of April 2020).
It was like ‘ugh…’.
There is a custom iptables_raw
module, but it look like a huge overkill. Not because of the module, but because of the complexity of their policy for iptables, which I don’t like.
Therefore, we can either fall back to writing a proper module, or try to duck-tape few parts together and call it done.
Therefore, I’m falling back to a rather laborious “two-piston” pattern: get a state, run command with ‘when
’ depending on output of the first command.
Not the best way, but the easiest.
How to add a custom iptables chain into Ansible
- name: Get existing iptables rules
become: true
command: iptables -n -L
changed_when: false
register: iptables_rules- name: Create custom iptables chain
become: true
command: iptables -N '{{ custom_chain }}'
when: "'Chain {{ custom_chain }}' not in iptables_rules.stdout"
This is a classic example of “two-piston” emulation of the module in Ansible. We query iptables for existing (everything) and then creates custom chain only if it’s not in the output of the previous task. This combination gives us idempotency, moreover, it yields 0 changes on the second run.
Unfortunately, check mode breaks on this pattern. There is no way you can reliably report that changes are required in this scheme.