mirror of
https://github.com/ansible-collections/hetzner.hcloud.git
synced 2026-02-04 16:11:49 +00:00
fix: firewall idempotency with ipv6 addresses (#722)
##### SUMMARY Always use the canonical address representation when checking if rules changed. Fixes #708
This commit is contained in:
parent
7ac361a9cc
commit
907a7fd73c
4 changed files with 73 additions and 17 deletions
2
changelogs/fragments/firewall-rules-ipv6-idempotency.yml
Normal file
2
changelogs/fragments/firewall-rules-ipv6-idempotency.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- firewall - Ensure idempotency when using non canonical ipv6 representation in Firewall rules.
|
||||
7
plugins/module_utils/ipaddress.py
Normal file
7
plugins/module_utils/ipaddress.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from ipaddress import ip_interface
|
||||
|
||||
|
||||
def normalize_ip(value: str) -> str:
|
||||
return str(ip_interface(value))
|
||||
|
|
@ -221,6 +221,7 @@ import time
|
|||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ..module_utils.hcloud import AnsibleHCloud
|
||||
from ..module_utils.ipaddress import normalize_ip
|
||||
from ..module_utils.vendor.hcloud import APIException, HCloudException
|
||||
from ..module_utils.vendor.hcloud.firewalls import (
|
||||
BoundFirewall,
|
||||
|
|
@ -229,6 +230,14 @@ from ..module_utils.vendor.hcloud.firewalls import (
|
|||
)
|
||||
|
||||
|
||||
def normalize_rules(rules: list[dict]) -> list[dict]:
|
||||
for rule in rules:
|
||||
# Ensure ip addresses canonical representation
|
||||
rule["source_ips"] = [normalize_ip(o) for o in rule["source_ips"]]
|
||||
rule["destination_ips"] = [normalize_ip(o) for o in rule["destination_ips"]]
|
||||
return rules
|
||||
|
||||
|
||||
class AnsibleHCloudFirewall(AnsibleHCloud):
|
||||
represent = "hcloud_firewall"
|
||||
|
||||
|
|
@ -287,6 +296,7 @@ class AnsibleHCloudFirewall(AnsibleHCloud):
|
|||
}
|
||||
rules = self.module.params.get("rules")
|
||||
if rules is not None:
|
||||
rules = normalize_rules(rules)
|
||||
params["rules"] = [
|
||||
FirewallRule(
|
||||
direction=rule["direction"],
|
||||
|
|
@ -323,21 +333,24 @@ class AnsibleHCloudFirewall(AnsibleHCloud):
|
|||
self._mark_as_changed()
|
||||
|
||||
rules = self.module.params.get("rules")
|
||||
if rules is not None and rules != [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules]:
|
||||
if not self.module.check_mode:
|
||||
new_rules = [
|
||||
FirewallRule(
|
||||
direction=rule["direction"],
|
||||
protocol=rule["protocol"],
|
||||
source_ips=rule["source_ips"] if rule["source_ips"] is not None else [],
|
||||
destination_ips=rule["destination_ips"] if rule["destination_ips"] is not None else [],
|
||||
port=rule["port"],
|
||||
description=rule["description"],
|
||||
)
|
||||
for rule in rules
|
||||
]
|
||||
self.hcloud_firewall.set_rules(new_rules)
|
||||
self._mark_as_changed()
|
||||
if rules is not None:
|
||||
rules = normalize_rules(rules)
|
||||
|
||||
if rules != [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules]:
|
||||
if not self.module.check_mode:
|
||||
new_rules = [
|
||||
FirewallRule(
|
||||
direction=rule["direction"],
|
||||
protocol=rule["protocol"],
|
||||
source_ips=rule["source_ips"] if rule["source_ips"] is not None else [],
|
||||
destination_ips=rule["destination_ips"] if rule["destination_ips"] is not None else [],
|
||||
port=rule["port"],
|
||||
description=rule["description"],
|
||||
)
|
||||
for rule in rules
|
||||
]
|
||||
self.hcloud_firewall.set_rules(new_rules)
|
||||
self._mark_as_changed()
|
||||
|
||||
self._get_firewall()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@
|
|||
- result is failed
|
||||
- 'result.msg == "one of the following is required: id, name"'
|
||||
|
||||
- name: Save allowed ssh sources
|
||||
ansible.builtin.set_fact:
|
||||
allowed_ssh_source_ips:
|
||||
- "201.10.10.0/24" # ipv4 network
|
||||
- "201.11.11.2/32" # ipv4 host
|
||||
- "2a02:1234:10:0100::/64" # ipv6 network (non canonical representation)
|
||||
- "2a02:1234:11:10::1/128" # ipv6 host
|
||||
|
||||
- name: Test create with check mode
|
||||
hetzner.hcloud.firewall:
|
||||
name: "{{ hcloud_firewall_name }}"
|
||||
|
|
@ -20,6 +28,11 @@
|
|||
direction: in
|
||||
protocol: icmp
|
||||
source_ips: ["0.0.0.0/0", "::/0"]
|
||||
- description: allow ssh in
|
||||
direction: in
|
||||
protocol: tcp
|
||||
port: 22
|
||||
source_ips: "{{ allowed_ssh_source_ips }}"
|
||||
labels:
|
||||
key: value
|
||||
check_mode: true
|
||||
|
|
@ -37,6 +50,11 @@
|
|||
direction: in
|
||||
protocol: icmp
|
||||
source_ips: ["0.0.0.0/0", "::/0"]
|
||||
- description: allow ssh in
|
||||
direction: in
|
||||
protocol: tcp
|
||||
port: 22
|
||||
source_ips: "{{ allowed_ssh_source_ips }}"
|
||||
labels:
|
||||
key: value
|
||||
register: result
|
||||
|
|
@ -45,11 +63,22 @@
|
|||
that:
|
||||
- result is changed
|
||||
- result.hcloud_firewall.name == hcloud_firewall_name
|
||||
- result.hcloud_firewall.rules | list | count == 1
|
||||
- result.hcloud_firewall.rules | list | count == 2
|
||||
- result.hcloud_firewall.rules[0].description == "allow icmp in"
|
||||
- result.hcloud_firewall.rules[0].direction == "in"
|
||||
- result.hcloud_firewall.rules[0].protocol == "icmp"
|
||||
- result.hcloud_firewall.rules[0].source_ips == ["0.0.0.0/0", "::/0"]
|
||||
- result.hcloud_firewall.rules[0].source_ips | count == 2
|
||||
- result.hcloud_firewall.rules[0].source_ips[0] == "0.0.0.0/0"
|
||||
- result.hcloud_firewall.rules[0].source_ips[1] == "::/0"
|
||||
- result.hcloud_firewall.rules[1].description == "allow ssh in"
|
||||
- result.hcloud_firewall.rules[1].direction == "in"
|
||||
- result.hcloud_firewall.rules[1].protocol == "tcp"
|
||||
- result.hcloud_firewall.rules[1].port == "22"
|
||||
- result.hcloud_firewall.rules[1].source_ips | count == 4
|
||||
- result.hcloud_firewall.rules[1].source_ips[0] == "201.10.10.0/24"
|
||||
- result.hcloud_firewall.rules[1].source_ips[1] == "201.11.11.2/32"
|
||||
- result.hcloud_firewall.rules[1].source_ips[2] == "2a02:1234:10:100::/64" # canonical representation
|
||||
- result.hcloud_firewall.rules[1].source_ips[3] == "2a02:1234:11:10::1/128"
|
||||
- result.hcloud_firewall.labels.key == "value"
|
||||
- result.hcloud_firewall.applied_to | list | count == 0
|
||||
|
||||
|
|
@ -61,6 +90,11 @@
|
|||
direction: in
|
||||
protocol: icmp
|
||||
source_ips: ["0.0.0.0/0", "::/0"]
|
||||
- description: allow ssh in
|
||||
direction: in
|
||||
protocol: tcp
|
||||
port: 22
|
||||
source_ips: "{{ allowed_ssh_source_ips }}"
|
||||
labels:
|
||||
key: value
|
||||
register: result
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue