Page MenuHomeVyOS Platform

No OneTemporary

Size
17 KB
Referenced Files
None
Subscribers
None
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
@@ -1,187 +1,256 @@
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.
# T5160: Firewall re-writing
# cli changes from:
# set firewall name <name> ...
# set firewall ipv6-name <name> ...
# To
# set firewall ipv4 name <name>
# set firewall ipv6 name <name>
## Also from 'firewall interface' removed.
## in and out:
# set firewall interface <iface> [in|out] [name | ipv6-name] <name>
# To
# set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> [inbound-interface | outboubd-interface] interface-name <iface>
# set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> action jump
# set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> jump-target <name>
## local:
# set firewall interface <iface> local [name | ipv6-name] <name>
# To
# set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> inbound-interface interface-name <iface>
# 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
return
### Migration of state policies
if config.exists(base + ['state-policy']):
for state in config.list_nodes(base + ['state-policy']):
action = config.return_value(base + ['state-policy', state, 'action'])
config.set(base + ['global-options', 'state-policy', state, 'action'], value=action)
if config.exists(base + ['state-policy', state, 'log']):
config.set(base + ['global-options', 'state-policy', state, 'log'], value='enable')
config.delete(base + ['state-policy'])
## migration of global options:
for option in ['all-ping', 'broadcast-ping', 'config-trap', 'ip-src-route', 'ipv6-receive-redirects', 'ipv6-src-route', 'log-martians',
'receive-redirects', 'resolver-cache', 'resolver-internal', 'send-redirects', 'source-validation', 'syn-cookies', 'twa-hazards-protection']:
if config.exists(base + [option]):
if option != 'config-trap':
val = config.return_value(base + [option])
config.set(base + ['global-options', option], value=val)
config.delete(base + [option])
### Migration of firewall name and ipv6-name
### Also migrate legacy 'accept' behaviour
if config.exists(base + ['name']):
config.set(['firewall', 'ipv4', 'name'])
config.set_tag(['firewall', 'ipv4', 'name'])
for ipv4name in config.list_nodes(base + ['name']):
config.copy(base + ['name', ipv4name], base + ['ipv4', 'name', ipv4name])
if config.exists(base + ['ipv4', 'name', ipv4name, 'default-action']):
action = config.return_value(base + ['ipv4', 'name', ipv4name, 'default-action'])
if action == 'accept':
config.set(base + ['ipv4', 'name', ipv4name, 'default-action'], value='return')
if config.exists(base + ['ipv4', 'name', ipv4name, 'rule']):
for rule_id in config.list_nodes(base + ['ipv4', 'name', ipv4name, 'rule']):
action = config.return_value(base + ['ipv4', 'name', ipv4name, 'rule', rule_id, 'action'])
if action == 'accept':
config.set(base + ['ipv4', 'name', ipv4name, 'rule', rule_id, 'action'], value='return')
config.delete(base + ['name'])
if config.exists(base + ['ipv6-name']):
config.set(['firewall', 'ipv6', 'name'])
config.set_tag(['firewall', 'ipv6', 'name'])
for ipv6name in config.list_nodes(base + ['ipv6-name']):
config.copy(base + ['ipv6-name', ipv6name], base + ['ipv6', 'name', ipv6name])
if config.exists(base + ['ipv6', 'name', ipv6name, 'default-action']):
action = config.return_value(base + ['ipv6', 'name', ipv6name, 'default-action'])
if action == 'accept':
config.set(base + ['ipv6', 'name', ipv6name, 'default-action'], value='return')
if config.exists(base + ['ipv6', 'name', ipv6name, 'rule']):
for rule_id in config.list_nodes(base + ['ipv6', 'name', ipv6name, 'rule']):
action = config.return_value(base + ['ipv6', 'name', ipv6name, 'rule', rule_id, 'action'])
if action == 'accept':
config.set(base + ['ipv6', 'name', ipv6name, 'rule', rule_id, 'action'], value='return')
config.delete(base + ['ipv6-name'])
### Migration of firewall interface
if config.exists(base + ['interface']):
fwd_ipv4_rule = 5
inp_ipv4_rule = 5
fwd_ipv6_rule = 5
inp_ipv6_rule = 5
for direction in ['in', 'out', 'local']:
for iface in config.list_nodes(base + ['interface']):
if config.exists(base + ['interface', iface, direction]):
if config.exists(base + ['interface', iface, direction, 'name']):
target = config.return_value(base + ['interface', iface, direction, 'name'])
if direction == 'in':
# Add default-action== accept for compatibility reasons:
config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
new_base = base + ['ipv4', 'forward', 'filter', 'rule']
config.set(new_base)
config.set_tag(new_base)
config.set(new_base + [fwd_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
fwd_ipv4_rule = fwd_ipv4_rule + 5
elif direction == 'out':
# Add default-action== accept for compatibility reasons:
config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
new_base = base + ['ipv4', 'forward', 'filter', 'rule']
config.set(new_base)
config.set_tag(new_base)
config.set(new_base + [fwd_ipv4_rule, 'outbound-interface', 'interface-name'], value=iface)
config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
fwd_ipv4_rule = fwd_ipv4_rule + 5
else:
# Add default-action== accept for compatibility reasons:
config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept')
new_base = base + ['ipv4', 'input', 'filter', 'rule']
config.set(new_base)
config.set_tag(new_base)
config.set(new_base + [inp_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
config.set(new_base + [inp_ipv4_rule, 'action'], value='jump')
config.set(new_base + [inp_ipv4_rule, 'jump-target'], value=target)
inp_ipv4_rule = inp_ipv4_rule + 5
if config.exists(base + ['interface', iface, direction, 'ipv6-name']):
target = config.return_value(base + ['interface', iface, direction, 'ipv6-name'])
if direction == 'in':
# Add default-action== accept for compatibility reasons:
config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
new_base = base + ['ipv6', 'forward', 'filter', 'rule']
config.set(new_base)
config.set_tag(new_base)
config.set(new_base + [fwd_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
fwd_ipv6_rule = fwd_ipv6_rule + 5
elif direction == 'out':
# Add default-action== accept for compatibility reasons:
config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
new_base = base + ['ipv6', 'forward', 'filter', 'rule']
config.set(new_base)
config.set_tag(new_base)
config.set(new_base + [fwd_ipv6_rule, 'outbound-interface', 'interface-name'], value=iface)
config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
fwd_ipv6_rule = fwd_ipv6_rule + 5
else:
new_base = base + ['ipv6', 'input', 'filter', 'rule']
# Add default-action== accept for compatibility reasons:
config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
config.set(new_base)
config.set_tag(new_base)
config.set(new_base + [inp_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
config.set(new_base + [inp_ipv6_rule, 'action'], value='jump')
config.set(new_base + [inp_ipv6_rule, 'jump-target'], value=target)
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])

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 6, 6:58 AM (14 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4652936
Default Alt Text
(17 KB)

Event Timeline