Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions data/templates/firewall/nftables-zone.j2
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
iifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter return
{% endif %}
{% endfor %}
{% endif %}
{% if zone_conf.default_firewall is vyos_defined and zone_conf.default_firewall[fw_name] is vyos_defined %}
counter jump NAME{{ suffix }}_{{ zone_conf.default_firewall[fw_name] }}
counter return
{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
}
Expand All @@ -71,6 +75,20 @@
oifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter return
{% endif %}
{% endfor %}
{% endif %}
{% if zone_conf.default_local is vyos_defined %}
{% for from_zone, from_conf in zone_conf.default_local.items() if from_conf[fw_name] is vyos_defined %}
{% if 'interface' in zone[from_zone].member %}
oifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf[fw_name] }}
oifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter return
{% endif %}
{% if 'vrf' in zone[from_zone].member %}
{% for vrf_name in zone[from_zone].member.vrf %}
oifname { "{{ zone[from_zone]['vrf_interfaces'][vrf_name] }}" } counter jump NAME{{ suffix }}_{{ from_conf[fw_name] }}
oifname { "{{ zone[from_zone]['vrf_interfaces'][vrf_name] }}" } counter return
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
}
Expand Down Expand Up @@ -103,6 +121,10 @@
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% if zone_conf.default_firewall is vyos_defined and zone_conf.default_firewall[fw_name] is vyos_defined %}
counter jump NAME{{ suffix }}_{{ zone_conf.default_firewall[fw_name] }}
counter return
{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
}
Expand Down
23 changes: 23 additions & 0 deletions interface-definitions/firewall.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,29 @@
</properties>
<defaultValue>drop</defaultValue>
</leafNode>
<node name="default-firewall">
<properties>
<help>Default firewall rules for traffic coming into this zone</help>
</properties>
<children>
<leafNode name="ipv6-name">
<properties>
<help>IPv6 firewall ruleset</help>
<completionHelp>
<path>firewall ipv6 name</path>
</completionHelp>
</properties>
</leafNode>
<leafNode name="name">
<properties>
<help>IPv4 firewall ruleset</help>
<completionHelp>
<path>firewall ipv4 name</path>
</completionHelp>
</properties>
</leafNode>
</children>
</node>
<tagNode name="from">
<properties>
<help>Zone from which to filter traffic</help>
Expand Down
44 changes: 44 additions & 0 deletions smoketest/scripts/cli/test_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,50 @@ def test_zone_basic(self):
self.verify_nftables(nftables_search, 'ip vyos_filter')
self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')

def test_zone_with_default_firewall(self):
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'default-action', 'drop'])
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest-default', 'default-action', 'drop'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'member', 'interface', 'eth0'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-eth1', 'firewall', 'name', 'smoketest'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'default-firewall', 'name', 'smoketest-default'])
self.cli_set(['firewall', 'zone', 'smoketest-eth1', 'member', 'interface', 'eth1'])
self.cli_set(['firewall', 'zone', 'smoketest-eth1', 'default-firewall', 'name', 'smoketest-default'])
self.cli_set(['firewall', 'zone', 'smoketest-eth2', 'member', 'interface', 'eth2'])
self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
self.cli_set(['firewall', 'zone', 'smoketest-local', 'default-firewall', 'name', 'smoketest-default'])
self.cli_commit()

smoketest_eth0_search = [
['iifname "eth1"', 'jump NAME_smoketest'],
['jump NAME_smoketest-default']
]
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-eth0')
self.verify_nftables_chain(smoketest_eth0_search, 'ip vyos_filter', 'VZONE_smoketest-eth0')

smoketest_eth1_search = [
['jump NAME_smoketest-default']
]
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-eth1')
self.verify_nftables_chain(smoketest_eth1_search, 'ip vyos_filter', 'VZONE_smoketest-eth1')

self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-eth2')

smoketest_local_in_search = [
['iifname "eth0"', 'jump NAME_smoketest'],
['jump NAME_smoketest-default'],
]
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-local_IN')
self.verify_nftables_chain(smoketest_local_in_search, 'ip vyos_filter', 'VZONE_smoketest-local_IN')

smoketest_local_out_search = [
['oifname "eth0"', 'jump NAME_smoketest'],
['oifname "eth1"', 'jump NAME_smoketest-default']
]
self.verify_nftables_chain_exists('ip vyos_filter', 'VZONE_smoketest-local_OUT')
self.verify_nftables_chain(smoketest_local_out_search, 'ip vyos_filter', 'VZONE_smoketest-local_OUT')

def test_zone_with_vrf(self):
self.cli_set(['firewall', 'ipv4', 'name', 'ZONE1-to-LOCAL', 'default-action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'name', 'ZONE2_to_ZONE1', 'default-action', 'continue'])
Expand Down
19 changes: 17 additions & 2 deletions src/conf_mode/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,15 @@ def get_config(config=None):
continue

local_zone_conf['from_local'] = {}
local_zone_conf['default_local'] = {}

for zone, zone_conf in firewall['zone'].items():
if zone == local_zone or 'from' not in zone_conf:
if zone == local_zone:
continue
if local_zone in zone_conf['from']:
if 'from' in zone_conf and local_zone in zone_conf['from']:
local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone]
elif 'default_firewall' in zone_conf:
local_zone_conf['default_local'][zone] = zone_conf['default_firewall']

set_dependents('conntrack', conf)

Expand Down Expand Up @@ -626,6 +629,18 @@ def verify(firewall):
if v6_name and not dict_search_args(firewall, 'ipv6', 'name', v6_name):
raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')

if 'default_firewall' in zone_conf:
v4_name = dict_search_args(zone_conf, 'default_firewall', 'name')
if v4_name and not dict_search_args(firewall, 'ipv4', 'name', v4_name):
raise ConfigError(f'Firewall name "{v4_name}" does not exist')

v6_name = dict_search_args(zone_conf, 'default_firewall', 'ipv6_name')
if v6_name and not dict_search_args(firewall, 'ipv6', 'name', v6_name):
raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')

if not v4_name and not v6_name:
raise ConfigError('No firewall names specified for default-firewall')

return None

def generate(firewall):
Expand Down
Loading