How we manage IPtables – now and then

Iptables, created more than 20 years ago 😀 , is still the most frequent and common tool to manage the traffic to/from/through your Linux machine. Below you will find two ways to manage the iptables rules. The typical manual way used for decades and the one with Ansible playbooks.

Here is a simple iptables firewall on chain INPUT:

root@sdnix:~# iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
2    ACCEPT     tcp  --  10.0.10.0/24        anywhere             tcp dpt:ssh
3    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
4    DROP       all  --  anywhere             anywhere

I really enjoy using:

 iptables -L --line-numbers 

It shows the rules with their consecutive number. You can add new iptables rules either above or below a certain rule, for example:

 root@sdnix:~# iptables -I INPUT 3 -s 10.0.10.0/24 -p tcp -m tcp --dport 53 -j ACCEPT 

The above command will put the rule above the number you have specified, which makes it the new rule number 3:

root@sdnix:~# iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
2    ACCEPT     tcp  --  10.0.10.0/24        anywhere             tcp dpt:ssh
3    ACCEPT     tcp  --  10.0.10.0/24         anywhere             tcp dpt:domain
4    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
5    DROP       all  --  anywhere             anywhere

The tricky question here is, what if we have to manage the firewall on 100 or 1000 servers and to keep track and consistency. Here, Ansible comes in useful.

There is a module for Ansible, which you can use to set up and maintain, iptables rules. Important notice is that it doesn’t save them permanently, but only update the one in the memory, so we will handle the saving in another way. Again with Ansible.

It has most of the functionalities, you may use on daily basis, such as: chain, source/destination IP address, source/destination ports, logging, jump and many more. It works for both IPv4 and IPv6.

So here is an example, which will create firewall rules with Ansible playbook, such as the one above:

  - name: Allow related and established connections IPV4
      iptables:
        chain: INPUT
        ctstate: ESTABLISHED,RELATED
        jump: ACCEPT
        action: insert
        state: present
      notify: saveiptables

    - name: Allow SSH traffic IPV4
      iptables:
        chain: INPUT
        destination_port: 22
        protocol: tcp
        jump: ACCEPT
        action: append
        state: present
      notify: saveiptables

    - name: Allow HTTPS traffic IPV4
      iptables:
        chain: INPUT
        destination_port: 443
        protocol: tcp
        jump: ACCEPT
        action: append
        state: present
      notify: saveiptables

    - name: Drop traffic IPV4
      iptables:
        chain: INPUT
        jump: DROP
        action: append
        state: present
      notify: saveiptables

Here is an example with IPv6, pay attention that you have to specify – ip_version: ipv6 :

    - name: Allow related and established connections IPV6
      iptables:
        chain: INPUT
        ctstate: ESTABLISHED,RELATED
        jump: ACCEPT
        action: insert
        state: present
        ip_version: ipv6
      notify: saveip6tables

    - name: Allow SSH traffic IPV6
      iptables:
        chain: INPUT
        destination_port: 22
        protocol: tcp
        jump: ACCEPT
        action: append
        state: present
        ip_version: ipv6
      notify: saveip6tables

    - name: Allow HTTPS traffic IPV6
      iptables:
        chain: INPUT
        destination_port: 443
        protocol: tcp
        jump: ACCEPT
        action: append
        state: present
        ip_version: ipv6
      notify: saveip6tables

    - name: Drop traffic IPV6
      iptables:
        chain: INPUT
        jump: DROP
        action: append
        state: present
        ip_version: ipv6
      notify: saveip6tables

It is pretty straight forward, as the parameters match the iptables commands and options. The Ansible specifics come in state: present and notify: saveiptables :

    state: (absent/present) – This indicate whether the rule must exists or no.
    notify: saveiptables – These actions are used by Handlers (pretty similar to tasks, but will be executed only if the “notify” action is triggered). Handlers are most often placed and executed in the end of the playbook. You can have multiple tasks which may trigger one “notify” action and the related handler will be executed once. For example, the idea is to restart a service but only once, not every time a change is made.
  handlers:
    - name: saveiptables
      shell: iptables-save > /etc/iptables/rules.v4
      become: yes
      become_user: root

    - name: saveip6tables
      shell: ip6tables-save > /etc/iptables/rules.v6
      become: yes
      become_user: root

The handlers above will save the changes of the iptables rules permanently, so they won’t be lost on restart. If there are no changes on the target machine and the “notify” action is not triggered, the handlers won’t be executed.

*Update 31.03.2020* Make sure you have installed the package: iptables-persistent (Check here how to do this with Ansible: “Install iptables-persistent with Ansible“)

You can find the full documentation of Ansible here.

Have you started using Ansible to manage your firewall? Do you find it easier? 😀

All of the examples are configured on Cloud Virtual Machine, provided by: CloudBalkan

Leave a Reply

Your email address will not be published. Required fields are marked *