fix issue where you couldn't update ports
Build and Push Container / build-and-push (push) Successful in 22s
Build and Push Container / build-and-push (push) Successful in 22s
This commit is contained in:
@@ -19,10 +19,9 @@ Exported env vars:
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import logging
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
import requests
|
||||
@@ -31,9 +30,13 @@ from dotenv import load_dotenv
|
||||
|
||||
from ip_lookup import get_ipv4, get_ipv6
|
||||
from unifi_firewall import (
|
||||
FirewallPolicy,
|
||||
FirewallZone,
|
||||
build_policy_payload,
|
||||
create_policy,
|
||||
get_action_from_policy,
|
||||
get_dest_ports_from_policy,
|
||||
get_protocol_from_policy,
|
||||
get_session,
|
||||
get_source_ip_from_policy,
|
||||
list_policies,
|
||||
@@ -101,6 +104,7 @@ class RuleConfig(TypedDict, total=False):
|
||||
dest_port_ranges: list[PortRange] | None
|
||||
logging_enabled: bool
|
||||
enabled: bool
|
||||
description: str | None
|
||||
|
||||
|
||||
class RulesConfig(TypedDict):
|
||||
@@ -168,6 +172,84 @@ def build_zone_map(zones: list[FirewallZone]) -> dict[str, str]:
|
||||
return zone_map
|
||||
|
||||
|
||||
def policy_matches_config(
|
||||
existing_policy: FirewallPolicy,
|
||||
rule: RuleConfig,
|
||||
zone_map: dict[str, str],
|
||||
public_ip: str,
|
||||
) -> bool:
|
||||
"""Check if an existing policy matches the desired config exactly."""
|
||||
# 1. Source IP
|
||||
existing_ip = get_source_ip_from_policy(existing_policy)
|
||||
if existing_ip != public_ip:
|
||||
return False
|
||||
|
||||
# 2. Dest ports (compare as sorted lists)
|
||||
existing_ports_result = get_dest_ports_from_policy(existing_policy)
|
||||
rule_dest_ports = rule.get("dest_ports") or []
|
||||
if existing_ports_result is None:
|
||||
if rule_dest_ports:
|
||||
return False
|
||||
else:
|
||||
existing_ports, existing_ranges = existing_ports_result
|
||||
if sorted(existing_ports) != sorted(rule_dest_ports):
|
||||
return False
|
||||
|
||||
# 3. Dest port ranges
|
||||
rule_dest_ranges = rule.get("dest_port_ranges") or []
|
||||
if len(existing_ranges) != len(rule_dest_ranges):
|
||||
return False
|
||||
for er, rr in zip(sorted(existing_ranges, key=lambda x: x["start"]), sorted(rule_dest_ranges, key=lambda x: x["start"])):
|
||||
if er["start"] != rr["start"] or er["stop"] != rr["stop"]:
|
||||
return False
|
||||
|
||||
# 4. Protocol (case-insensitive)
|
||||
existing_protocol = get_protocol_from_policy(existing_policy)
|
||||
rule_protocol = rule.get("protocol")
|
||||
if existing_protocol is not None and rule_protocol is not None:
|
||||
if existing_protocol.upper() != rule_protocol.upper():
|
||||
return False
|
||||
elif existing_protocol is not None or rule_protocol is not None:
|
||||
return False
|
||||
|
||||
# 5. Action type
|
||||
existing_action_result = get_action_from_policy(existing_policy)
|
||||
if existing_action_result is None:
|
||||
return False
|
||||
existing_action_type, existing_allow_return = existing_action_result
|
||||
if existing_action_type != rule.get("action", "ALLOW"):
|
||||
return False
|
||||
|
||||
# 6. allowReturnTraffic
|
||||
if existing_action_type == "ALLOW" and existing_allow_return != rule.get("allow_return_traffic", True):
|
||||
return False
|
||||
|
||||
# 7. Source zone ID
|
||||
rule_source_zone = rule.get("source_zone", "WAN")
|
||||
if existing_policy["source"]["zoneId"] != zone_map.get(rule_source_zone):
|
||||
return False
|
||||
|
||||
# 8. Dest zone ID
|
||||
rule_dest_zone = rule.get("dest_zone", "LAN")
|
||||
if existing_policy["destination"]["zoneId"] != zone_map.get(rule_dest_zone):
|
||||
return False
|
||||
|
||||
# 9. IP version
|
||||
if existing_policy["ipProtocolScope"]["ipVersion"] != rule.get("ip_version", "IPV4"):
|
||||
return False
|
||||
|
||||
# 10. enabled
|
||||
if existing_policy["enabled"] != rule.get("enabled", True):
|
||||
return False
|
||||
|
||||
# 11. loggingEnabled
|
||||
if existing_policy["loggingEnabled"] != rule.get("logging_enabled", False):
|
||||
return False
|
||||
|
||||
# 12. description
|
||||
return existing_policy["description"] == rule.get("description")
|
||||
|
||||
|
||||
def process_rule(
|
||||
session: requests.Session,
|
||||
host: str,
|
||||
@@ -208,15 +290,16 @@ def process_rule(
|
||||
|
||||
existing_policy = existing_policies[0] if existing_policies else None
|
||||
|
||||
# Check if policy exists and IP matches
|
||||
# Check if policy exists and matches config
|
||||
if existing_policy:
|
||||
existing_ip = get_source_ip_from_policy(existing_policy)
|
||||
if existing_ip == public_ip:
|
||||
logger.info("Rule '%s' already has correct IP (%s), skipping", rule_name, public_ip)
|
||||
if policy_matches_config(existing_policy, rule, zone_map, public_ip):
|
||||
logger.info("Rule '%s' already matches config, skipping", rule_name)
|
||||
return {"rule_name": rule_name, "action": "skipped", "ip": public_ip, "error": None}
|
||||
|
||||
# Update existing policy
|
||||
logger.info("Rule '%s' exists with IP %s, updating to %s", rule_name, existing_ip, public_ip)
|
||||
# Update existing policy (IP or other attributes changed)
|
||||
existing_ip = get_source_ip_from_policy(existing_policy)
|
||||
logger.info("Rule '%s' exists but config changed (current IP: %s, desired IP: %s), updating",
|
||||
rule_name, existing_ip, public_ip)
|
||||
try:
|
||||
payload = build_policy_payload(
|
||||
name=rule_name,
|
||||
|
||||
Reference in New Issue
Block a user