From 2dc2df575bc4de60759a272f5e6880326501a7ef Mon Sep 17 00:00:00 2001 From: Nicolas Fort Date: Thu, 16 Nov 2023 15:37:56 +0000 Subject: T4072: firewall: backport bridge firewall to sagitta --- data/templates/firewall/nftables-bridge.j2 | 35 +++++++++ data/templates/firewall/nftables-defines.j2 | 14 ++-- data/templates/firewall/nftables-nat.j2 | 2 +- data/templates/firewall/nftables-policy.j2 | 4 +- data/templates/firewall/nftables.j2 | 15 +++- interface-definitions/firewall.xml.in | 9 +++ .../include/firewall/action-l2.xml.i | 37 ++++++++++ .../include/firewall/action.xml.i | 8 ++- .../include/firewall/bridge-custom-name.xml.i | 39 ++++++++++ .../include/firewall/bridge-hook-forward.xml.i | 34 +++++++++ .../include/firewall/common-rule-bridge.xml.i | 34 +++++++++ .../include/firewall/common-rule-inet.xml.i | 7 +- .../include/firewall/default-action-bridge.xml.i | 34 +++++++++ .../include/firewall/default-action.xml.i | 10 ++- .../include/firewall/match-vlan.xml.i | 41 +++++++++++ op-mode-definitions/firewall.xml.in | 84 +++++++++++++++++----- python/vyos/firewall.py | 2 +- smoketest/scripts/cli/test_firewall.py | 35 +++++++++ 18 files changed, 404 insertions(+), 40 deletions(-) create mode 100644 data/templates/firewall/nftables-bridge.j2 create mode 100644 interface-definitions/include/firewall/action-l2.xml.i create mode 100644 interface-definitions/include/firewall/bridge-custom-name.xml.i create mode 100644 interface-definitions/include/firewall/bridge-hook-forward.xml.i create mode 100644 interface-definitions/include/firewall/common-rule-bridge.xml.i create mode 100644 interface-definitions/include/firewall/default-action-bridge.xml.i create mode 100644 interface-definitions/include/firewall/match-vlan.xml.i diff --git a/data/templates/firewall/nftables-bridge.j2 b/data/templates/firewall/nftables-bridge.j2 new file mode 100644 index 000000000..1a4ad2ed9 --- /dev/null +++ b/data/templates/firewall/nftables-bridge.j2 @@ -0,0 +1,35 @@ +{% macro bridge(bridge) %} +{% set ns = namespace(sets=[]) %} +{% if bridge.forward is vyos_defined %} +{% for prior, conf in bridge.forward.items() %} +{% set def_action = conf.default_action %} + chain VYOS_FORWARD_{{ prior }} { + type filter hook forward priority {{ prior }}; policy {{ def_action }}; +{% if conf.rule is vyos_defined %} +{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} + {{ rule_conf | nft_rule('FWD', prior, rule_id, 'bri') }} +{% if rule_conf.recent is vyos_defined %} +{% set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %} +{% endif %} +{% endfor %} +{% endif %} + } +{% endfor %} +{% endif %} + +{% if bridge.name is vyos_defined %} +{% for name_text, conf in bridge.name.items() %} + chain NAME_{{ name_text }} { +{% if conf.rule is vyos_defined %} +{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} + {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'bri') }} +{% if rule_conf.recent is vyos_defined %} +{% set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %} +{% endif %} +{% endfor %} +{% endif %} + {{ conf | nft_default_rule(name_text) }} + } +{% endfor %} +{% endif %} +{% endmacro %} \ No newline at end of file diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2 index 0a7e79edd..a20c399ae 100644 --- a/data/templates/firewall/nftables-defines.j2 +++ b/data/templates/firewall/nftables-defines.j2 @@ -1,7 +1,7 @@ -{% macro groups(group, is_ipv6) %} +{% macro groups(group, is_ipv6, is_l3) %} {% if group is vyos_defined %} {% set ip_type = 'ipv6_addr' if is_ipv6 else 'ipv4_addr' %} -{% if group.address_group is vyos_defined and not is_ipv6 %} +{% if group.address_group is vyos_defined and not is_ipv6 and is_l3 %} {% for group_name, group_conf in group.address_group.items() %} {% set includes = group_conf.include if group_conf.include is vyos_defined else [] %} set A_{{ group_name }} { @@ -14,7 +14,7 @@ } {% endfor %} {% endif %} -{% if group.ipv6_address_group is vyos_defined and is_ipv6 %} +{% if group.ipv6_address_group is vyos_defined and is_ipv6 and is_l3 %} {% for group_name, group_conf in group.ipv6_address_group.items() %} {% set includes = group_conf.include if group_conf.include is vyos_defined else [] %} set A6_{{ group_name }} { @@ -27,7 +27,7 @@ } {% endfor %} {% endif %} -{% if group.domain_group is vyos_defined %} +{% if group.domain_group is vyos_defined and is_l3 %} {% for name, name_config in group.domain_group.items() %} set D_{{ name }} { type {{ ip_type }} @@ -46,7 +46,7 @@ } {% endfor %} {% endif %} -{% if group.network_group is vyos_defined and not is_ipv6 %} +{% if group.network_group is vyos_defined and not is_ipv6 and is_l3 %} {% for group_name, group_conf in group.network_group.items() %} {% set includes = group_conf.include if group_conf.include is vyos_defined else [] %} set N_{{ group_name }} { @@ -59,7 +59,7 @@ } {% endfor %} {% endif %} -{% if group.ipv6_network_group is vyos_defined and is_ipv6 %} +{% if group.ipv6_network_group is vyos_defined and is_ipv6 and is_l3 %} {% for group_name, group_conf in group.ipv6_network_group.items() %} {% set includes = group_conf.include if group_conf.include is vyos_defined else [] %} set N6_{{ group_name }} { @@ -72,7 +72,7 @@ } {% endfor %} {% endif %} -{% if group.port_group is vyos_defined %} +{% if group.port_group is vyos_defined and is_l3 %} {% for group_name, group_conf in group.port_group.items() %} {% set includes = group_conf.include if group_conf.include is vyos_defined else [] %} set P_{{ group_name }} { diff --git a/data/templates/firewall/nftables-nat.j2 b/data/templates/firewall/nftables-nat.j2 index f0be3cf5d..dcf28da88 100644 --- a/data/templates/firewall/nftables-nat.j2 +++ b/data/templates/firewall/nftables-nat.j2 @@ -62,6 +62,6 @@ table ip vyos_nat { return } -{{ group_tmpl.groups(firewall_group, False) }} +{{ group_tmpl.groups(firewall_group, False, True) }} } {% endif %} diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2 index 699349e2b..d77e3f6e9 100644 --- a/data/templates/firewall/nftables-policy.j2 +++ b/data/templates/firewall/nftables-policy.j2 @@ -32,7 +32,7 @@ table ip vyos_mangle { {% endfor %} {% endif %} -{{ group_tmpl.groups(firewall_group, False) }} +{{ group_tmpl.groups(firewall_group, False, True) }} } table ip6 vyos_mangle { @@ -61,5 +61,5 @@ table ip6 vyos_mangle { {% endfor %} {% endif %} -{{ group_tmpl.groups(firewall_group, True) }} +{{ group_tmpl.groups(firewall_group, True, True) }} } diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index 6257b576a..9fcacf677 100644 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -1,6 +1,7 @@ #!/usr/sbin/nft -f {% import 'firewall/nftables-defines.j2' as group_tmpl %} +{% import 'firewall/nftables-bridge.j2' as bridge_tmpl %} {% import 'firewall/nftables-zone.j2' as zone_tmpl %} flush chain raw FW_CONNTRACK @@ -147,7 +148,7 @@ table ip vyos_filter { {% endfor %} {% endif %} {% endif %} -{{ group_tmpl.groups(group, False) }} +{{ group_tmpl.groups(group, False, True) }} {% if zone is vyos_defined %} {{ zone_tmpl.zone_chains(zone, False) }} @@ -254,10 +255,20 @@ table ip6 vyos_filter { {% endfor %} {% endif %} {% endif %} -{{ group_tmpl.groups(group, True) }} +{{ group_tmpl.groups(group, True, True) }} {% if zone is vyos_defined %} {{ zone_tmpl.zone_chains(zone, True) }} {% endif %} +} + +## Bridge Firewall +{% if first_install is not vyos_defined %} +delete table bridge vyos_filter +{% endif %} +table bridge vyos_filter { +{{ bridge_tmpl.bridge(bridge) }} +{{ group_tmpl.groups(group, False, False) }} + } \ No newline at end of file diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index b0e6358d8..4704b200e 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -284,6 +284,15 @@ + + + Bridge firewall + + + #include + #include + + IPv4 firewall diff --git a/interface-definitions/include/firewall/action-l2.xml.i b/interface-definitions/include/firewall/action-l2.xml.i new file mode 100644 index 000000000..43fd211b4 --- /dev/null +++ b/interface-definitions/include/firewall/action-l2.xml.i @@ -0,0 +1,37 @@ + + + + Rule action + + accept continue jump return drop queue + + + accept + Accept matching entries + + + continue + Continue parsing next rule + + + jump + Jump to another chain + + + return + Return from the current chain and continue at the next rule of the last chain + + + drop + Drop matching entries + + + queue + Enqueue packet to userspace + + + (accept|continue|jump|return|drop|queue) + + + + \ No newline at end of file diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i index 7c6e33839..9391a7bee 100644 --- a/interface-definitions/include/firewall/action.xml.i +++ b/interface-definitions/include/firewall/action.xml.i @@ -3,12 +3,16 @@ Rule action - accept jump reject return drop queue + accept continue jump reject return drop queue accept Accept matching entries + + continue + Continue parsing next rule + jump Jump to another chain @@ -30,7 +34,7 @@ Enqueue packet to userspace - (accept|jump|reject|return|drop|queue) + (accept|continue|jump|reject|return|drop|queue) diff --git a/interface-definitions/include/firewall/bridge-custom-name.xml.i b/interface-definitions/include/firewall/bridge-custom-name.xml.i new file mode 100644 index 000000000..a85fd5a19 --- /dev/null +++ b/interface-definitions/include/firewall/bridge-custom-name.xml.i @@ -0,0 +1,39 @@ + + + + Bridge custom firewall + + [a-zA-Z0-9][\w\-\.]* + + + + #include + #include + #include + + + Set jump target. Action jump must be defined in default-action to use this setting + + firewall bridge name + + + + + + Bridge Firewall forward filter rule number + + u32:1-999999 + Number for this firewall rule + + + + + Firewall rule number must be between 1 and 999999 + + + #include + + + + + \ No newline at end of file diff --git a/interface-definitions/include/firewall/bridge-hook-forward.xml.i b/interface-definitions/include/firewall/bridge-hook-forward.xml.i new file mode 100644 index 000000000..23d757070 --- /dev/null +++ b/interface-definitions/include/firewall/bridge-hook-forward.xml.i @@ -0,0 +1,34 @@ + + + + Bridge forward firewall + + + + + Bridge firewall forward filter + + + #include + #include + + + Bridge Firewall forward filter rule number + + u32:1-999999 + Number for this firewall rule + + + + + Firewall rule number must be between 1 and 999999 + + + #include + + + + + + + \ No newline at end of file diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i new file mode 100644 index 000000000..ebf95a111 --- /dev/null +++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i @@ -0,0 +1,34 @@ + +#include +#include + + + Destination parameters + + + #include + + +#include + + + Set jump target. Action jump must be defined to use this setting + + firewall bridge name + + + +#include +#include + + + Source parameters + + + #include + + +#include +#include +#include + \ No newline at end of file diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i index 52721ecc4..030adfe7c 100644 --- a/interface-definitions/include/firewall/common-rule-inet.xml.i +++ b/interface-definitions/include/firewall/common-rule-inet.xml.i @@ -7,12 +7,7 @@ #include #include #include - - - Option to disable firewall rule - - - +#include IP fragment match diff --git a/interface-definitions/include/firewall/default-action-bridge.xml.i b/interface-definitions/include/firewall/default-action-bridge.xml.i new file mode 100644 index 000000000..577165976 --- /dev/null +++ b/interface-definitions/include/firewall/default-action-bridge.xml.i @@ -0,0 +1,34 @@ + + + + Default action for rule-set + + drop jump return accept continue + + + drop + Drop if no prior rules are hit + + + jump + Jump to another chain if no prior rules are hit + + + return + Return from the current chain and continue at the next rule of the last chain + + + accept + Accept if no prior rules are hit + + + continue + Continue parsing next rule + + + (drop|jump|return|accept|continue) + + + drop + + \ No newline at end of file diff --git a/interface-definitions/include/firewall/default-action.xml.i b/interface-definitions/include/firewall/default-action.xml.i index 80efaf335..6a49d800e 100644 --- a/interface-definitions/include/firewall/default-action.xml.i +++ b/interface-definitions/include/firewall/default-action.xml.i @@ -1,9 +1,9 @@ - Default-action for rule-set + Default action for rule-set - drop jump reject return accept + drop jump reject return accept continue drop @@ -25,8 +25,12 @@ accept Accept if no prior rules are hit + + continue + Continue parsing next rule + - (drop|jump|reject|return|accept) + (drop|jump|reject|return|accept|continue) drop diff --git a/interface-definitions/include/firewall/match-vlan.xml.i b/interface-definitions/include/firewall/match-vlan.xml.i new file mode 100644 index 000000000..d0820f7d8 --- /dev/null +++ b/interface-definitions/include/firewall/match-vlan.xml.i @@ -0,0 +1,41 @@ + + + + VLAN parameters + + + + + VLAN id + + u32:0-4096 + VLAN id + + + <start-end> + VLAN id range to match + + + + + + + + + VLAN priority(pcp) + + u32:0-7 + VLAN priority + + + <start-end> + VLAN priority range to match + + + + + + + + + \ No newline at end of file diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in index 0f296c272..4a7ffbb66 100644 --- a/op-mode-definitions/firewall.xml.in +++ b/op-mode-definitions/firewall.xml.in @@ -132,6 +132,58 @@ sudo ${vyos_op_scripts_dir}/firewall.py --action show_group + + + Show bridge firewall + + + + + Show bridge forward firewall ruleset + + + + + Show bridge forward filter firewall ruleset + + + + + Show summary of bridge forward filter firewall rules + + firewall bridge forward filter rule + + + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 + + + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 + + + + + + Show bridge custom firewall chains + + firewall bridge name + + + + + + Show summary of bridge custom firewall ruleset + + firewall bridge name ${COMP_WORDS[6]} rule + + + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 + + + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 + + + sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3 + Show IPv6 firewall @@ -154,10 +206,10 @@ firewall ipv6 forward filter rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 @@ -178,10 +230,10 @@ firewall ipv6 input filter rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 @@ -202,10 +254,10 @@ firewall ipv6 output filter rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 @@ -224,10 +276,10 @@ firewall ipv6 ipv6-name ${COMP_WORDS[6]} rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3 @@ -254,10 +306,10 @@ firewall ipv4 forward filter rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 @@ -278,10 +330,10 @@ firewall ipv4 input filter rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 @@ -302,10 +354,10 @@ firewall ipv4 output filter rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 @@ -324,10 +376,10 @@ firewall ipv4 name ${COMP_WORDS[6]} rule - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 - sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 + sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3 diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 06b58d4ed..8ae269fed 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -438,7 +438,7 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): else: output.append('return') - output.append(f'comment "{hook}-{fw_name}-{rule_id}"') + output.append(f'comment "{family}-{hook}-{fw_name}-{rule_id}"') return " ".join(output) def parse_tcp_flags(flags): diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index c51592ec2..e6c928ad7 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -543,6 +543,41 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.verify_nftables_chain([['accept']], 'raw', 'FW_CONNTRACK') self.verify_nftables_chain([['return']], 'ip6 raw', 'FW_CONNTRACK') + def test_bridge_basic_rules(self): + name = 'smoketest' + interface_in = 'eth0' + mac_address = '00:53:00:00:00:01' + vlan_id = '12' + vlan_prior = '3' + + self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept']) + self.cli_set(['firewall', 'bridge', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'action', 'accept']) + self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'source', 'mac-address', mac_address]) + self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'inbound-interface', 'name', interface_in]) + self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log']) + self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log-options', 'level', 'crit']) + + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'default-action', 'drop']) + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'action', 'accept']) + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'vlan', 'id', vlan_id]) + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'action', 'jump']) + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'jump-target', name]) + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'vlan', 'priority', vlan_prior]) + + self.cli_commit() + + nftables_search = [ + ['chain VYOS_FORWARD_filter'], + ['type filter hook forward priority filter; policy drop;'], + [f'vlan id {vlan_id}', 'accept'], + [f'vlan pcp {vlan_prior}', f'jump NAME_{name}'], + [f'chain NAME_{name}'], + [f'ether saddr {mac_address}', f'iifname "{interface_in}"', f'log prefix "[bri-NAM-{name}-1-A]" log level crit', 'accept'] + ] + + self.verify_nftables(nftables_search, 'bridge vyos_filter') + def test_source_validation(self): # Strict self.cli_set(['firewall', 'global-options', 'source-validation', 'strict']) -- cgit v1.2.3