ansible [core 2.19.2]
community.routeros 3.10.0
I want to ensure firewall rules match those in my vars/firewall.yml. I’m not sure how to specify this in the playbook.
What I have done is create a generic firewall.yml playbook task, and then I feed it with a loop.
It mostly works, without any attempt to pin the firewall rules in order. One rule always says it has changed, and results in a duplicate firewall entry added to the end of the firewall rules. The others are recognised as already being there, and are unchanged.
I tried using the following, but they don’t work with an Ansible loop. Only the last rule remains.
handle_absent_entries: remove
handle_entries_content: remove_as_much_as_possible
ensure_order: true
tasks/firewall.yml
---
#/ip firewall filter
# .id looks to be being ignored.
# Router is injecting a rule 0, so .id is always 1 off
# Rule with hw=yes always returned changed, and a duplicate rule is added.
- name: Firewall settings
community.routeros.api_modify:
path: ip firewall filter
data:
- action: "{{ item.action }}"
chain: "{{ item.chain }}"
comment: "{{ item.comment | default(omit) }}"
address-list: "{{ item.address_list | default(omit) }}"
address-list-timeout: "{{ item.address_list_timeout | default(omit) }}"
connection-bytes: "{{ item.connection_bytes | default(omit) }}"
connection-limit: "{{ item.connection_limit | default(omit) }}"
connection-mark: "{{ item.connection_mark | default(omit) }}"
connection-nat-state: "{{ item.connection_nat_state | default(omit) }}"
connection-rate: "{{ item.connection_rate | default(omit) }}"
connection-state: "{{ item.connection_state | default(omit) }}"
connection-type: "{{ item.connection_type | default(omit) }}"
content: "{{ item.content | default(omit) }}"
disabled: "{{ item.disabled | default(omit) }}"
dscp: "{{ item.dscp | default(omit) }}"
dst-address: "{{ item.dst_address | default(omit) }}"
dst-address-list: "{{ item.dst_address_list | default(omit) }}"
dst-address-type: "{{ item.dst_address_type | default(omit) }}"
dst-limit: "{{ item.dst_limit | default(omit) }}"
dst-port: "{{ item.dst_port | default(omit) }}"
fragment: "{{ item.fragment | default(omit) }}"
hotspot: "{{ item.hotspot | default(omit) }}"
hw-offload: "{{ item.hw_offload | default(omit) }}"
icmp-options: "{{ item.icmp_options | default(omit) }}"
in-bridge-port: "{{ item.in_bridge_port | default(omit) }}"
in-bridge-port-list: "{{ item.in_bridge_port_list | default(omit) }}"
in-interface: "{{ item.in_interface | default(omit) }}"
in-interface-list: "{{ item.in_interface_list | default(omit) }}"
ingress-priority: "{{ item.ingress_priority | default(omit) }}"
ipsec-policy: "{{ item.ipsec_policy | default(omit) }}"
ipv4-options: "{{ item.ipv4_options | default(omit) }}"
jump-target: "{{ item.jump_target | default(omit) }}"
layer7-protocol: "{{ item.layer7_protocol | default(omit) }}"
limit: "{{ item.limit | default(omit) }}"
log: "{{ item.log | default(omit) }}"
log-prefix: "{{ item.log_prefix | default(omit) }}"
nth: "{{ item.nth | default(omit) }}"
out-bridge-port: "{{ item.out_bridge_port | default(omit) }}"
out-bridge-port-list: "{{ item.out_bridge_port_list | default(omit) }}"
out-interface: "{{ item.out_interface | default(omit) }}"
out-interface-list: "{{ item.out_interface_list | default(omit) }}"
p2p: "{{ item.p2p | default(omit) }}"
packet-mark: "{{ item.packet_mark | default(omit) }}"
packet-size: "{{ item.packet_size | default(omit) }}"
per-connection-classifier: "{{ item.per_connection_classifier | default(omit) }}"
port: "{{ item.port | default(omit) }}"
priority: "{{ item.priority | default(omit) }}"
protocol: "{{ item.protocol | default(omit) }}"
psd: "{{ item.psd | default(omit) }}"
random: "{{ item.random | default(omit) }}"
realm: "{{ item.realm | default(omit) }}"
reject-with: "{{ item.reject_with | default(omit) }}"
routing-mark: "{{ item.routing_mark | default(omit) }}"
routing-table: "{{ item.routing_table | default(omit) }}"
src-address: "{{ item.src_address | default(omit) }}"
src-address-list: "{{ item.src_address_list | default(omit) }}"
src-address-type: "{{ item.src_address_type | default(omit) }}"
src-mac-address: "{{ item.src_mac_address | default(omit) }}"
src-port: "{{ item.src_port | default(omit) }}"
tcp-flags: "{{ item.tcp_flags | default(omit) }}"
tcp-mss: "{{ item.tcp_mss | default(omit) }}"
time: "{{ item.time | default(omit) }}"
tls-host: "{{ item.tls_host | default(omit) }}"
ttl: "{{ item.ttl | default(omit) }}"
# .id: "*{{ rindex + 1 }}"
loop: "{{ firewall_rules }}"
loop_control:
index_var: rindex
vars/firewall.yml
# Note for variables with '-'s we have substituted '_' or we get an Ansible error with loops
firewall_rules:
# input chain
- { action: accept, chain: input, comment: "accept established,related,untracked", connection_state: "established,related,untracked" }
- { action: drop, chain: input, comment: "drop invalid", connection_state: "invalid" }
- { action: accept, chain: input, comment: "accept ICMP", protocol: icmp }
- { action: accept, chain: input, comment: "accept to local loopback (for CAPsMAN)", dst_address: "127.0.0.1" }
- { action: accept, chain: input, comment: "Allow Management Port", in_interface_list: "MGMT" }
- { action: drop, chain: input, comment: "drop all not coming from LAN", in_interface_list: "!LAN" }
# Forward chain
- { action: accept, chain: forward, comment: "accept in ipsec policy", ipsec_policy: "in,ipsec" }
- { action: accept, chain: forward, comment: "accept out ipsec policy", ipsec_policy: "out,ipsec" }
# the following rule get duplicated every run
- { action: "fasttrack-connection", chain: forward, comment: "fasttrack", connection_state: "established,related", hw_offload: "yes"}
- { action: accept, chain: forward, comment: "accept established,related,untracked", connection_state: "established,related,untracked" }
- { action: drop, chain: forward, comment: "drop invalid", connection_state: "invalid" }
- { action: drop, chain: forward, comment: "drop all from WAN not DSTNATed", connection_nat_state: "!dstnat", connection_state: "new", in_interface_list: "WAN" }
nat_rules:
- { action: masquerade, chain: srcnat, comment: "masquerade", ipsec_policy: "out,none", out_interface_list: "WAN" }
Gives:
TASK [networking : Firewall settings] *************************************************************************************************************************************************************************
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'input', 'comment': 'accept established,related,untracked', 'connection_state': 'established,related,untracked'})
ok: [fibre-02] => (item={'action': 'drop', 'chain': 'input', 'comment': 'drop invalid', 'connection_state': 'invalid'})
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'input', 'comment': 'accept ICMP', 'protocol': 'icmp'})
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'input', 'comment': 'accept to local loopback (for CAPsMAN)', 'dst_address': '127.0.0.1'})
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'input', 'comment': 'Allow Management Port', 'in_interface_list': 'MGMT'})
ok: [fibre-02] => (item={'action': 'drop', 'chain': 'input', 'comment': 'drop all not coming from LAN', 'in_interface_list': '!LAN'})
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'forward', 'comment': 'accept in ipsec policy', 'ipsec_policy': 'in,ipsec'})
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'forward', 'comment': 'accept out ipsec policy', 'ipsec_policy': 'out,ipsec'})
changed: [fibre-02] => (item={'action': 'fasttrack-connection', 'chain': 'forward', 'comment': 'fasttrack', 'connection_state': 'established,related', 'hw_offload': 'yes'})
ok: [fibre-02] => (item={'action': 'accept', 'chain': 'forward', 'comment': 'accept established,related,untracked', 'connection_state': 'established,related,untracked'})
ok: [fibre-02] => (item={'action': 'drop', 'chain': 'forward', 'comment': 'drop invalid', 'connection_state': 'invalid'})
ok: [fibre-02] => (item={'action': 'drop', 'chain': 'forward', 'comment': 'drop all from WAN not DSTNATed', 'connection_nat_state': '!dstnat', 'connection_state': 'new', 'in_interface_list': 'WAN'})
After 3 runs, the changed
rule appears 3x
```
/ip firewall filter
add action=accept chain=input comment=“accept established,related,untracked” connection-state=established,related,untracked
add action=drop chain=input comment=“drop invalid” connection-state=invalid
add action=accept chain=input comment=“accept ICMP” protocol=icmp
add action=accept chain=input comment=“accept to local loopback (for CAPsMAN)” dst-address=127.0.0.1
add action=accept chain=input comment=“Allow Management Port” in-interface-list=MGMT
add action=drop chain=input comment=“drop all not coming from LAN” in-interface-list=!LAN
add action=accept chain=forward comment=“accept in ipsec policy” ipsec-policy=in,ipsec
add action=accept chain=forward comment=“accept out ipsec policy” ipsec-policy=out,ipsec
add action=fasttrack-connection chain=forward comment=fasttrack connection-state=established,related hw-offload=yes
add action=accept chain=forward comment=“accept established,related,untracked” connection-state=established,related,untracked
add action=drop chain=forward comment=“drop invalid” connection-state=invalid
add action=drop chain=forward comment=“drop all from WAN not DSTNATed” connection-nat-state=!dstnat connection-state=new in-interface-list=WAN
add action=fasttrack-connection chain=forward comment=fasttrack connection-state=established,related hw-offload=yes
add action=fasttrack-connection chain=forward comment=fasttrack connection-state=established,related hw-offload=yes
add action=fasttrack-connection chain=forward comment=fasttrack connection-state=established,related hw-offload=yes
```
The complete playbook is at routeros_isp_modem. This works on a newly reset RB960 running routeros 7.19.4, with only the firewall rules needing to be corrected after a second run. A number of other tasks do report changed
but these are not altering the configuration.
#firewall #get-help routeros #api_modify