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