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