diff options
| author | Daniil Baturin <daniil@vyos.io> | 2026-04-07 13:50:06 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-07 13:50:06 +0100 |
| commit | b7d191cd3053a736fa14ebfefe7a95093b0fcacd (patch) | |
| tree | f9279c0bda74b578e58140f3e90b184613139790 | |
| parent | 229cd51b7dda3fed93dd7da2aa1b131221e9aa75 (diff) | |
| parent | 227699e62146c71c3a0d3923650edd247b20ef16 (diff) | |
| download | vyos-1x-b7d191cd3053a736fa14ebfefe7a95093b0fcacd.tar.gz vyos-1x-b7d191cd3053a736fa14ebfefe7a95093b0fcacd.zip | |
Merge pull request #5103 from alexandr-san4ez/T8281-current
firewall: T8281: Normalize network prefixes in rule configuration
| -rw-r--r-- | smoketest/configs/assert/firewall-rules-address | 26 | ||||
| -rw-r--r-- | smoketest/configs/firewall-rules-address | 88 | ||||
| -rw-r--r-- | src/migration-scripts/firewall/10-to-11 | 69 |
3 files changed, 183 insertions, 0 deletions
diff --git a/smoketest/configs/assert/firewall-rules-address b/smoketest/configs/assert/firewall-rules-address new file mode 100644 index 000000000..59627040c --- /dev/null +++ b/smoketest/configs/assert/firewall-rules-address @@ -0,0 +1,26 @@ +set firewall ipv4 name TEST1 default-action 'drop' +set firewall ipv4 name TEST1 rule 10 action 'return' +set firewall ipv4 name TEST1 rule 10 destination address '10.10.11.2' +set firewall ipv4 name TEST1 rule 10 source address '10.10.10.0/30' +set firewall ipv4 name TEST1 rule 20 action 'return' +set firewall ipv4 name TEST1 rule 20 destination address '10.10.13.1-10.10.13.16' +set firewall ipv4 name TEST1 rule 20 source address '10.10.12.0/30' +set firewall ipv4 name TEST1 rule 30 action 'return' +set firewall ipv4 name TEST1 rule 30 destination address '!10.10.15.0/30' +set firewall ipv4 name TEST1 rule 30 source address '10.10.14.1' +set firewall ipv6 name TEST2 default-action 'drop' +set firewall ipv6 name TEST2 rule 10 action 'return' +set firewall ipv6 name TEST2 rule 10 destination address '2001:db8:10:11::2' +set firewall ipv6 name TEST2 rule 10 source address '2001:db8:10:10::/126' +set firewall ipv6 name TEST2 rule 20 action 'return' +set firewall ipv6 name TEST2 rule 20 destination address '2001:db8:10:13::1-2001:db8:10:13::16' +set firewall ipv6 name TEST2 rule 20 source address '2001:db8:10:12::/126' +set firewall ipv6 name TEST2 rule 30 action 'return' +set firewall ipv6 name TEST2 rule 30 destination address '!2001:db8:10:15::/126' +set firewall ipv6 name TEST2 rule 30 source address '2001:db8:10:14::1' +set interfaces ethernet eth0 +set interfaces loopback lo +set system console device ttyS0 speed '115200' +set system host-name 'vyos' +set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0' +set system login user vyos authentication plaintext-password ''
\ No newline at end of file diff --git a/smoketest/configs/firewall-rules-address b/smoketest/configs/firewall-rules-address new file mode 100644 index 000000000..44dd81342 --- /dev/null +++ b/smoketest/configs/firewall-rules-address @@ -0,0 +1,88 @@ +firewall { + name TEST1 { + default-action drop + rule 10 { + action accept + destination { + address 10.10.11.2 + } + source { + address 10.10.10.1/30 + } + } + rule 20 { + action accept + destination { + address 10.10.13.1-10.10.13.16 + } + source { + address 10.10.12.0/30 + } + } + rule 30 { + action accept + destination { + address !10.10.15.1/30 + } + source { + address 10.10.14.1 + } + } + } + ipv6-name TEST2 { + default-action drop + rule 10 { + action accept + destination { + address 2001:db8:10:11::2 + } + source { + address 2001:db8:10:10::1/126 + } + } + rule 20 { + action accept + destination { + address 2001:db8:10:13::1-2001:db8:10:13::16 + } + source { + address 2001:db8:10:12::/126 + } + } + rule 30 { + action accept + destination { + address !2001:db8:10:15::1/126 + } + source { + address 2001:db8:10:14::1 + } + } + } +} +interfaces { + ethernet eth0 { + } + loopback lo { + } +} +system { + console { + device ttyS0 { + speed 115200 + } + } + host-name "vyos" + login { + user vyos { + authentication { + encrypted-password "$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0" + plaintext-password "" + } + } + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:container@1:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@23:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3.8
\ No newline at end of file diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11 index 50d3eb89b..f608c200d 100644 --- a/src/migration-scripts/firewall/10-to-11 +++ b/src/migration-scripts/firewall/10-to-11 @@ -36,10 +36,57 @@ # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> action jump # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> jump-target <name> +# T8281: Normalize firewall rule network prefixes for source and destination address +# +# Fixes invalid/non-canonical prefixes like: +# 10.10.10.1/30 -> 10.10.10.0/30 +# 2001:db8::1/64 -> 2001:db8::/64 +# +# Only "source address" and "destination address" are rewritten. + +from ipaddress import ip_network from vyos.configtree import ConfigTree base = ['firewall'] + +def normalize_address(config: ConfigTree, path: list[str]): + if not config.exists(path): + return + + def normalize(value: str) -> str | None: + if '/' not in value: + return None + + is_except = value.startswith('!') + if is_except: + value = value.lstrip('!') + + try: + new_value = str(ip_network(value, strict=False)) + except ValueError: + return None + + return f'!{new_value}' if is_except else new_value + + value = config.return_value(path) + if value: + normalized = normalize(value) + if normalized and normalized != value: + config.set(path, value=normalized) + + +def migrate_ruleset(config: ConfigTree, ruleset_base: list[str]): + if not config.exists(ruleset_base + ['rule']): + return + + for rule_id in config.list_nodes(ruleset_base + ['rule']): + rule_base = ruleset_base + ['rule', rule_id] + + for direction in ['source', 'destination']: + normalize_address(config, rule_base + [direction, 'address']) + + def migrate(config: ConfigTree) -> None: if not config.exists(base): # Nothing to do @@ -185,3 +232,25 @@ def migrate(config: ConfigTree) -> None: inp_ipv6_rule = inp_ipv6_rule + 5 config.delete(base + ['interface']) + + ### Normalize/fix firewall rule network prefixes for source and destination address (T8281) + for family in ['ipv4', 'ipv6', 'bridge']: + # 1) Named rulesets: + # set firewall ipv4|ipv6|bridge name <ruleset> rule <id> ... + ruleset_base = base + [family, 'name'] + if config.exists(ruleset_base): + for ruleset in config.list_nodes(ruleset_base): + migrate_ruleset(config, ruleset_base + [ruleset]) + + # 2) Hook-based rulesets: + # set firewall ipv4|ipv6|bridge input|output|forward filter|raw rule <id> ... + # set firewall ipv4|ipv6 prerouting raw rule <id> ... + hook_rulesets = { + 'input': ['filter', 'raw'], + 'output': ['filter', 'raw'], + 'forward': ['filter', 'raw'], + 'prerouting': ['raw'], + } + for hook, rulesets in hook_rulesets.items(): + for ruleset in rulesets: + migrate_ruleset(config, base + [family, hook, ruleset]) |
