diff options
| author | Nicolas Fort <nicolasfort1988@gmail.com> | 2022-09-16 16:35:11 +0000 | 
|---|---|---|
| committer | Nicolas Fort <nicolasfort1988@gmail.com> | 2022-09-16 16:35:11 +0000 | 
| commit | 99155150df9ceed0be4df46351844451b0683b3b (patch) | |
| tree | f9f080672486ffa2cf04e60d098b941a05f1789c | |
| parent | d41909874a6a7e2264e0a89928885b491efd9851 (diff) | |
| download | vyos-1x-99155150df9ceed0be4df46351844451b0683b3b.tar.gz vyos-1x-99155150df9ceed0be4df46351844451b0683b3b.zip | |
T4699: Firewall: Add jump action in firewall rulest
| -rw-r--r-- | data/templates/firewall/nftables.j2 | 2 | ||||
| -rw-r--r-- | interface-definitions/firewall.xml.in | 32 | ||||
| -rw-r--r-- | interface-definitions/include/firewall/action.xml.i | 8 | ||||
| -rw-r--r-- | interface-definitions/include/firewall/default-action.xml.i | 8 | ||||
| -rw-r--r-- | python/vyos/firewall.py | 4 | ||||
| -rw-r--r-- | python/vyos/template.py | 7 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 24 | ||||
| -rwxr-xr-x | src/conf_mode/firewall.py | 26 | 
8 files changed, 103 insertions, 8 deletions
| diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index c0780dad5..9d609f73f 100644 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -175,7 +175,7 @@ table ip6 vyos_filter {  {%                 endif %}  {%             endfor %}  {%         endif %} -        {{ conf | nft_default_rule(name_text) }} +        {{ conf | nft_default_rule(name_text, ipv6=True) }}      }  {%     endfor %}  {%     for set_name in ns.sets %} diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index d39dddc77..d6fa76892 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -379,6 +379,14 @@            #include <include/firewall/default-action.xml.i>            #include <include/firewall/enable-default-log.xml.i>            #include <include/generic-description.xml.i> +          <leafNode name="default-jump-target"> +            <properties> +              <help>Set jump target. Action jump must be defined in default-action to use this setting</help> +              <completionHelp> +                <path>firewall ipv6-name</path> +              </completionHelp> +            </properties> +          </leafNode>            <tagNode name="rule">              <properties>                <help>Firewall rule number (IPv6)</help> @@ -452,6 +460,14 @@                    #include <include/firewall/icmpv6-type-name.xml.i>                  </children>                </node> +              <leafNode name="jump-target"> +                <properties> +                  <help>Set jump target. Action jump must be defined to use this setting</help> +                  <completionHelp> +                    <path>firewall ipv6-name</path> +                  </completionHelp> +                </properties> +              </leafNode>              </children>            </tagNode>          </children> @@ -527,6 +543,14 @@            #include <include/firewall/default-action.xml.i>            #include <include/firewall/enable-default-log.xml.i>            #include <include/generic-description.xml.i> +          <leafNode name="default-jump-target"> +            <properties> +              <help>Set jump target. Action jump must be defined in default-action to use this setting</help> +              <completionHelp> +                <path>firewall name</path> +              </completionHelp> +            </properties> +          </leafNode>            <tagNode name="rule">              <properties>                <help>Firewall rule number (IPv4)</help> @@ -599,6 +623,14 @@                    #include <include/firewall/icmp-type-name.xml.i>                  </children>                </node> +              <leafNode name="jump-target"> +                <properties> +                  <help>Set jump target. Action jump must be defined to use this setting</help> +                  <completionHelp> +                    <path>firewall name</path> +                  </completionHelp> +                </properties> +              </leafNode>                #include <include/firewall/ttl.xml.i>              </children>            </tagNode> diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i index 512cc23bd..0738fa503 100644 --- a/interface-definitions/include/firewall/action.xml.i +++ b/interface-definitions/include/firewall/action.xml.i @@ -3,13 +3,17 @@    <properties>      <help>Rule action</help>      <completionHelp> -      <list>accept reject drop</list> +      <list>accept jump reject drop</list>      </completionHelp>      <valueHelp>        <format>accept</format>        <description>Accept matching entries</description>      </valueHelp>      <valueHelp> +      <format>jump</format> +      <description>Jump to another chain</description> +    </valueHelp> +    <valueHelp>        <format>reject</format>        <description>Reject matching entries</description>      </valueHelp> @@ -18,7 +22,7 @@        <description>Drop matching entries</description>      </valueHelp>      <constraint> -      <regex>(accept|reject|drop)</regex> +      <regex>(accept|jump|reject|drop)</regex>      </constraint>    </properties>  </leafNode> diff --git a/interface-definitions/include/firewall/default-action.xml.i b/interface-definitions/include/firewall/default-action.xml.i index 92a2fcaaf..5107768d3 100644 --- a/interface-definitions/include/firewall/default-action.xml.i +++ b/interface-definitions/include/firewall/default-action.xml.i @@ -3,13 +3,17 @@    <properties>      <help>Default-action for rule-set</help>      <completionHelp> -      <list>drop reject accept</list> +      <list>drop jump reject accept</list>      </completionHelp>      <valueHelp>        <format>drop</format>        <description>Drop if no prior rules are hit</description>      </valueHelp>      <valueHelp> +      <format>jump</format> +      <description>Jump to another chain if no prior rules are hit</description> +    </valueHelp> +    <valueHelp>        <format>reject</format>        <description>Drop and notify source if no prior rules are hit</description>      </valueHelp> @@ -18,7 +22,7 @@        <description>Accept if no prior rules are hit</description>      </valueHelp>      <constraint> -      <regex>(drop|reject|accept)</regex> +      <regex>(drop|jump|reject|accept)</regex>      </constraint>    </properties>    <defaultValue>drop</defaultValue> diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index b56caef71..f9b7222fd 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -326,6 +326,10 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):      if 'action' in rule_conf:          output.append(nft_action(rule_conf['action'])) +        if 'jump' in rule_conf['action']: +            target = rule_conf['jump_target'] +            output.append(f'NAME{def_suffix}_{target}') +      else:          output.append('return') diff --git a/python/vyos/template.py b/python/vyos/template.py index 9804308c1..2bad6df06 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -548,7 +548,7 @@ def nft_rule(rule_conf, fw_name, rule_id, ip_name='ip'):      return parse_rule(rule_conf, fw_name, rule_id, ip_name)  @register_filter('nft_default_rule') -def nft_default_rule(fw_conf, fw_name): +def nft_default_rule(fw_conf, fw_name, ipv6=False):      output = ['counter']      default_action = fw_conf['default_action'] @@ -557,6 +557,11 @@ def nft_default_rule(fw_conf, fw_name):          output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}]"')      output.append(nft_action(default_action)) +    if 'default_jump_target' in fw_conf: +        target = fw_conf['default_jump_target'] +        def_suffix = '6' if ipv6 else '' +        output.append(f'NAME{def_suffix}_{target}') +      output.append(f'comment "{fw_name} default-action {default_action}"')      return " ".join(output) diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 8e4aac788..c54cba027 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -228,6 +228,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):      def test_ipv4_advanced(self):          name = 'smoketest-adv' +        name2 = 'smoketest-adv2'          interface = 'eth0'          self.cli_set(['firewall', 'name', name, 'default-action', 'drop']) @@ -246,6 +247,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):          self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp', '3-11'])          self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp-exclude', '21-25']) +        self.cli_set(['firewall', 'name', name2, 'default-action', 'jump']) +        self.cli_set(['firewall', 'name', name2, 'default-jump-target', name]) +        self.cli_set(['firewall', 'name', name2, 'enable-default-log']) +        self.cli_set(['firewall', 'name', name2, 'rule', '1', 'source', 'address', '198.51.100.1']) +        self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump']) +        self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name]) +          self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])          self.cli_commit() @@ -254,7 +262,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):              [f'iifname "{interface}"', f'jump NAME_{name}'],              ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', 'return'],              ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'], -            [f'log prefix "[{name}-default-D]"', 'drop'] +            [f'log prefix "[{name}-default-D]"', 'drop'], +            ['ip saddr 198.51.100.1', f'jump NAME_{name}'], +            [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}']          ]          self.verify_nftables(nftables_search, 'ip vyos_filter') @@ -291,6 +301,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):      def test_ipv6_advanced(self):          name = 'v6-smoketest-adv' +        name2 = 'v6-smoketest-adv2'          interface = 'eth0'          self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop']) @@ -309,6 +320,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):          self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp', '4-14'])          self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp-exclude', '31-35']) +        self.cli_set(['firewall', 'ipv6-name', name2, 'default-action', 'jump']) +        self.cli_set(['firewall', 'ipv6-name', name2, 'default-jump-target', name]) +        self.cli_set(['firewall', 'ipv6-name', name2, 'enable-default-log']) +        self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'source', 'address', '2001:db8::/64']) +        self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'action', 'jump']) +        self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'jump-target', name]) +          self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])          self.cli_commit() @@ -317,7 +335,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):              [f'iifname "{interface}"', f'jump NAME6_{name}'],              ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'return'],              ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'return'], -            [f'log prefix "[{name}-default-D]"', 'drop'] +            [f'log prefix "[{name}-default-D]"', 'drop'], +            ['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'], +            [f'log prefix "[{name2}-default-J]"', f'jump NAME6_{name}']          ]          self.verify_nftables(nftables_search, 'ip6 vyos_filter') diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index eeb57bd30..cbd9cbe90 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -179,6 +179,20 @@ def verify_rule(firewall, rule_conf, ipv6):      if 'action' not in rule_conf:          raise ConfigError('Rule action must be defined') +    if 'jump' in rule_conf['action'] and 'jump_target' not in rule_conf: +        raise ConfigError('Action set to jump, but no jump-target specified') + +    if 'jump_target' in rule_conf: +        if 'jump' not in rule_conf['action']: +            raise ConfigError('jump-target defined, but action jump needed and it is not defined') +        target = rule_conf['jump_target'] +        if not ipv6: +            if target not in dict_search_args(firewall, 'name'): +                raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system') +        else: +            if target not in dict_search_args(firewall, 'ipv6_name'): +                raise ConfigError(f'Invalid jump-target. Firewall ipv6-name {target} does not exist on the system') +      if 'fragment' in rule_conf:          if {'match_frag', 'match_non_frag'} <= set(rule_conf['fragment']):              raise ConfigError('Cannot specify both "match-frag" and "match-non-frag"') @@ -287,6 +301,18 @@ def verify(firewall):      for name in ['name', 'ipv6_name']:          if name in firewall:              for name_id, name_conf in firewall[name].items(): +                if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: +                    raise ConfigError('default-action set to jump, but no default-jump-target specified') +                if 'default_jump_target' in name_conf: +                    target = name_conf['default_jump_target'] +                    if 'jump' not in name_conf['default_action']: +                        raise ConfigError('default-jump-target defined,but default-action jump needed and it is not defined') +                    if name_conf['default_jump_target'] == name_id: +                        raise ConfigError(f'Loop detected on default-jump-target.') +                    ## Now need to check that default-jump-target exists (other firewall chain/name) +                    if target not in dict_search_args(firewall, name): +                        raise ConfigError(f'Invalid jump-target. Firewall {name} {target} does not exist on the system') +                  if 'rule' in name_conf:                      for rule_id, rule_conf in name_conf['rule'].items():                          verify_rule(firewall, rule_conf, name == 'ipv6_name') | 
