From c7cf7b941445c9280ac1ed7bbbd68159654560a6 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Sat, 31 Jul 2021 19:23:44 +0200 Subject: zone-policy: T2199: Migrate zone-policy to XML/Python --- data/templates/zone_policy/nftables.tmpl | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 data/templates/zone_policy/nftables.tmpl (limited to 'data/templates/zone_policy/nftables.tmpl') diff --git a/data/templates/zone_policy/nftables.tmpl b/data/templates/zone_policy/nftables.tmpl new file mode 100644 index 000000000..4575a721c --- /dev/null +++ b/data/templates/zone_policy/nftables.tmpl @@ -0,0 +1,97 @@ +#!/usr/sbin/nft -f + +{% if cleanup_commands is defined %} +{% for command in cleanup_commands %} +{{ command }} +{% endfor %} +{% endif %} + +{% if zone is defined %} +table ip filter { +{% for zone_name, zone_conf in zone.items() if zone_conf.ipv4 %} +{% if zone_conf.local_zone is defined %} + chain VZONE_{{ zone_name }}_IN { + iifname lo counter return +{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter return +{% endfor %} + counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} + } + chain VZONE_{{ zone_name }}_OUT { + oifname lo counter return +{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.name is defined %} + oifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} + oifname { {{ zone[from_zone].interface | join(",") }} } counter return +{% endfor %} + counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} + } +{% else %} + chain VZONE_{{ zone_name }} { + iifname { {{ zone_conf.interface | join(",") }} } counter return +{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %} +{% if zone[from_zone].local_zone is not defined %} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter return +{% endif %} +{% endfor %} + counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} + } +{% endif %} +{% endfor %} +} + +table ip6 filter { +{% for zone_name, zone_conf in zone.items() if zone_conf.ipv6 %} +{% if zone_conf.local_zone is defined %} + chain VZONE6_{{ zone_name }}_IN { + iifname lo counter return +{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter return +{% endfor %} + counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} + } + chain VZONE6_{{ zone_name }}_OUT { + oifname lo counter return +{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.ipv6_name is defined %} + oifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} + oifname { {{ zone[from_zone].interface | join(",") }} } counter return +{% endfor %} + counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} + } +{% else %} + chain VZONE6_{{ zone_name }} { + iifname { {{ zone_conf.interface | join(",") }} } counter return +{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %} +{% if zone[from_zone].local_zone is not defined %} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter return +{% endif %} +{% endfor %} + counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} + } +{% endif %} +{% endfor %} +} + +{% for zone_name, zone_conf in zone.items() %} +{% if zone_conf.ipv4 %} +{% if 'local_zone' in zone_conf %} +insert rule ip filter VYOS_FW_LOCAL counter jump VZONE_{{ zone_name }}_IN +insert rule ip filter VYOS_FW_OUTPUT counter jump VZONE_{{ zone_name }}_OUT +{% else %} +insert rule ip filter VYOS_FW_OUT oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }} +{% endif %} +{% endif %} +{% if zone_conf.ipv6 %} +{% if 'local_zone' in zone_conf %} +insert rule ip6 filter VYOS_FW6_LOCAL counter jump VZONE6_{{ zone_name }}_IN +insert rule ip6 filter VYOS_FW6_OUTPUT counter jump VZONE6_{{ zone_name }}_OUT +{% else %} +insert rule ip6 filter VYOS_FW6_OUT oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE6_{{ zone_name }} +{% endif %} +{% endif %} +{% endfor %} + +{% endif %} -- cgit v1.2.3 From 28b285b4791aece18fe1bbd76f3d555370545006 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:24:40 +0100 Subject: zone_policy: T3873: Implement intra-zone-filtering --- data/templates/zone_policy/nftables.tmpl | 4 +-- interface-definitions/zone-policy.xml.in | 49 ++++++++++++++++++++++++++++++++ python/vyos/template.py | 15 ++++++++++ src/conf_mode/zone_policy.py | 20 +++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) (limited to 'data/templates/zone_policy/nftables.tmpl') diff --git a/data/templates/zone_policy/nftables.tmpl b/data/templates/zone_policy/nftables.tmpl index 4575a721c..21230c688 100644 --- a/data/templates/zone_policy/nftables.tmpl +++ b/data/templates/zone_policy/nftables.tmpl @@ -28,7 +28,7 @@ table ip filter { } {% else %} chain VZONE_{{ zone_name }} { - iifname { {{ zone_conf.interface | join(",") }} } counter return + iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=False) }} {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %} {% if zone[from_zone].local_zone is not defined %} iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} @@ -62,7 +62,7 @@ table ip6 filter { } {% else %} chain VZONE6_{{ zone_name }} { - iifname { {{ zone_conf.interface | join(",") }} } counter return + iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=True) }} {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %} {% if zone[from_zone].local_zone is not defined %} iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} diff --git a/interface-definitions/zone-policy.xml.in b/interface-definitions/zone-policy.xml.in index 52fd73f15..dd64c7c16 100644 --- a/interface-definitions/zone-policy.xml.in +++ b/interface-definitions/zone-policy.xml.in @@ -81,6 +81,55 @@ + + + Intra-zone filtering + + + + + Action for intra-zone traffic + + accept drop + + + accept + Accept traffic (default) + + + drop + Drop silently + + + ^(accept|drop)$ + + + + + + Use the specified firewall chain + + + + + IPv6 firewall ruleset + + firewall ipv6-name + + + + + + IPv4 firewall ruleset + + firewall name + + + + + + + Zone to be local-zone diff --git a/python/vyos/template.py b/python/vyos/template.py index 55bd04136..e20890e25 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -505,3 +505,18 @@ def nft_state_policy(conf, state): out.append(conf['action']) return " ".join(out) + +@register_filter('nft_intra_zone_action') +def nft_intra_zone_action(zone_conf, ipv6=False): + if 'intra_zone_filtering' in zone_conf: + intra_zone = zone_conf['intra_zone_filtering'] + fw_name = 'ipv6_name' if ipv6 else 'name' + + if 'action' in intra_zone: + if intra_zone['action'] == 'accept': + return 'return' + return intra_zone['action'] + elif dict_search_args(intra_zone, 'firewall', fw_name): + name = dict_search_args(intra_zone, 'firewall', fw_name) + return f'jump {name}' + return 'return' diff --git a/src/conf_mode/zone_policy.py b/src/conf_mode/zone_policy.py index 92f5624c2..2535ea33b 100755 --- a/src/conf_mode/zone_policy.py +++ b/src/conf_mode/zone_policy.py @@ -63,6 +63,8 @@ def verify(zone_policy): raise ConfigError('There cannot be multiple local zones') if 'interface' in zone_conf: raise ConfigError('Local zone cannot have interfaces assigned') + if 'intra_zone_filtering' in zone_conf: + raise ConfigError('Local zone cannot use intra-zone-filtering') local_zone = True if 'interface' in zone_conf: @@ -73,6 +75,24 @@ def verify(zone_policy): interfaces += zone_conf['interface'] + if 'intra_zone_filtering' in zone_conf: + intra_zone = zone_conf['intra_zone_filtering'] + + if len(intra_zone) > 1: + raise ConfigError('Only one intra-zone-filtering action must be specified') + + if 'firewall' in intra_zone: + v4_name = dict_search_args(intra_zone, 'firewall', 'name') + if v4_name and not dict_search_args(zone_policy, 'firewall', 'name', v4_name): + raise ConfigError(f'Firewall name "{v4_name}" does not exist') + + v6_name = dict_search_args(intra_zone, 'firewall', 'ipv6-name') + if v6_name and not dict_search_args(zone_policy, '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 intra-zone-filtering') + if 'from' in zone_conf: for from_zone, from_conf in zone_conf['from'].items(): v4_name = dict_search_args(from_conf, 'firewall', 'name') -- cgit v1.2.3