Skip to content

Commit fdda826

Browse files
authored
Merge pull request #4672 from apschultz/zone_default_firewall_ruleset
firewall: T7739: Default ruleset for firewall zones
2 parents 1aec97d + cc5895f commit fdda826

File tree

4 files changed

+106
-2
lines changed

4 files changed

+106
-2
lines changed

data/templates/firewall/nftables-zone.j2

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
iifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter return
5656
{% endif %}
5757
{% endfor %}
58+
{% endif %}
59+
{% if zone_conf.default_firewall is vyos_defined and zone_conf.default_firewall[fw_name] is vyos_defined %}
60+
counter jump NAME{{ suffix }}_{{ zone_conf.default_firewall[fw_name] }}
61+
counter return
5862
{% endif %}
5963
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
6064
}
@@ -71,6 +75,20 @@
7175
oifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter return
7276
{% endif %}
7377
{% endfor %}
78+
{% endif %}
79+
{% if zone_conf.default_local is vyos_defined %}
80+
{% for from_zone, from_conf in zone_conf.default_local.items() if from_conf[fw_name] is vyos_defined %}
81+
{% if 'interface' in zone[from_zone].member %}
82+
oifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf[fw_name] }}
83+
oifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter return
84+
{% endif %}
85+
{% if 'vrf' in zone[from_zone].member %}
86+
{% for vrf_name in zone[from_zone].member.vrf %}
87+
oifname { "{{ zone[from_zone]['vrf_interfaces'][vrf_name] }}" } counter jump NAME{{ suffix }}_{{ from_conf[fw_name] }}
88+
oifname { "{{ zone[from_zone]['vrf_interfaces'][vrf_name] }}" } counter return
89+
{% endfor %}
90+
{% endif %}
91+
{% endfor %}
7492
{% endif %}
7593
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
7694
}
@@ -103,6 +121,10 @@
103121
{% endif %}
104122
{% endif %}
105123
{% endfor %}
124+
{% endif %}
125+
{% if zone_conf.default_firewall is vyos_defined and zone_conf.default_firewall[fw_name] is vyos_defined %}
126+
counter jump NAME{{ suffix }}_{{ zone_conf.default_firewall[fw_name] }}
127+
counter return
106128
{% endif %}
107129
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
108130
}

interface-definitions/firewall.xml.in

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,29 @@
428428
</properties>
429429
<defaultValue>drop</defaultValue>
430430
</leafNode>
431+
<node name="default-firewall">
432+
<properties>
433+
<help>Default firewall rules for traffic coming into this zone</help>
434+
</properties>
435+
<children>
436+
<leafNode name="ipv6-name">
437+
<properties>
438+
<help>IPv6 firewall ruleset</help>
439+
<completionHelp>
440+
<path>firewall ipv6 name</path>
441+
</completionHelp>
442+
</properties>
443+
</leafNode>
444+
<leafNode name="name">
445+
<properties>
446+
<help>IPv4 firewall ruleset</help>
447+
<completionHelp>
448+
<path>firewall ipv4 name</path>
449+
</completionHelp>
450+
</properties>
451+
</leafNode>
452+
</children>
453+
</node>
431454
<tagNode name="from">
432455
<properties>
433456
<help>Zone from which to filter traffic</help>

smoketest/scripts/cli/test_firewall.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,50 @@ def test_zone_basic(self):
994994
self.verify_nftables(nftables_search, 'ip vyos_filter')
995995
self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')
996996

997+
def test_zone_with_default_firewall(self):
998+
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'default-action', 'drop'])
999+
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest-default', 'default-action', 'drop'])
1000+
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'member', 'interface', 'eth0'])
1001+
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-eth1', 'firewall', 'name', 'smoketest'])
1002+
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
1003+
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'default-firewall', 'name', 'smoketest-default'])
1004+
self.cli_set(['firewall', 'zone', 'smoketest-eth1', 'member', 'interface', 'eth1'])
1005+
self.cli_set(['firewall', 'zone', 'smoketest-eth1', 'default-firewall', 'name', 'smoketest-default'])
1006+
self.cli_set(['firewall', 'zone', 'smoketest-eth2', 'member', 'interface', 'eth2'])
1007+
self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
1008+
self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
1009+
self.cli_set(['firewall', 'zone', 'smoketest-local', 'default-firewall', 'name', 'smoketest-default'])
1010+
self.cli_commit()
1011+
1012+
smoketest_eth0_search = [
1013+
['iifname "eth1"', 'jump NAME_smoketest'],
1014+
['jump NAME_smoketest-default']
1015+
]
1016+
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-eth0')
1017+
self.verify_nftables_chain(smoketest_eth0_search, 'ip vyos_filter', 'VZONE_smoketest-eth0')
1018+
1019+
smoketest_eth1_search = [
1020+
['jump NAME_smoketest-default']
1021+
]
1022+
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-eth1')
1023+
self.verify_nftables_chain(smoketest_eth1_search, 'ip vyos_filter', 'VZONE_smoketest-eth1')
1024+
1025+
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-eth2')
1026+
1027+
smoketest_local_in_search = [
1028+
['iifname "eth0"', 'jump NAME_smoketest'],
1029+
['jump NAME_smoketest-default'],
1030+
]
1031+
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-local_IN')
1032+
self.verify_nftables_chain(smoketest_local_in_search, 'ip vyos_filter', 'VZONE_smoketest-local_IN')
1033+
1034+
smoketest_local_out_search = [
1035+
['oifname "eth0"', 'jump NAME_smoketest'],
1036+
['oifname "eth1"', 'jump NAME_smoketest-default']
1037+
]
1038+
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-local_OUT')
1039+
self.verify_nftables_chain(smoketest_local_out_search, 'ip vyos_filter', 'VZONE_smoketest-local_OUT')
1040+
9971041
def test_zone_with_vrf(self):
9981042
self.cli_set(['firewall', 'ipv4', 'name', 'ZONE1-to-LOCAL', 'default-action', 'accept'])
9991043
self.cli_set(['firewall', 'ipv4', 'name', 'ZONE2_to_ZONE1', 'default-action', 'continue'])

src/conf_mode/firewall.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,15 @@ def get_config(config=None):
154154
continue
155155

156156
local_zone_conf['from_local'] = {}
157+
local_zone_conf['default_local'] = {}
157158

158159
for zone, zone_conf in firewall['zone'].items():
159-
if zone == local_zone or 'from' not in zone_conf:
160+
if zone == local_zone:
160161
continue
161-
if local_zone in zone_conf['from']:
162+
if 'from' in zone_conf and local_zone in zone_conf['from']:
162163
local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone]
164+
elif 'default_firewall' in zone_conf:
165+
local_zone_conf['default_local'][zone] = zone_conf['default_firewall']
163166

164167
set_dependents('conntrack', conf)
165168

@@ -627,6 +630,18 @@ def verify(firewall):
627630
if v6_name and not dict_search_args(firewall, 'ipv6', 'name', v6_name):
628631
raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
629632

633+
if 'default_firewall' in zone_conf:
634+
v4_name = dict_search_args(zone_conf, 'default_firewall', 'name')
635+
if v4_name and not dict_search_args(firewall, 'ipv4', 'name', v4_name):
636+
raise ConfigError(f'Firewall name "{v4_name}" does not exist')
637+
638+
v6_name = dict_search_args(zone_conf, 'default_firewall', 'ipv6_name')
639+
if v6_name and not dict_search_args(firewall, 'ipv6', 'name', v6_name):
640+
raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
641+
642+
if not v4_name and not v6_name:
643+
raise ConfigError('No firewall names specified for default-firewall')
644+
630645
return None
631646

632647
def generate(firewall):

0 commit comments

Comments
 (0)