diff options
author | Christian Poessinger <christian@poessinger.com> | 2022-09-17 21:16:40 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-17 21:16:40 +0200 |
commit | dcf755594d3ce63239af407f71ceae295a12ed75 (patch) | |
tree | ba46fd714927010c7f7f4ccc1fae2e8fa11c1a75 | |
parent | a4feb96af9ac45aff41ded1744cf302b5c5a9e7e (diff) | |
parent | 99155150df9ceed0be4df46351844451b0683b3b (diff) | |
download | vyos-1x-dcf755594d3ce63239af407f71ceae295a12ed75.tar.gz vyos-1x-dcf755594d3ce63239af407f71ceae295a12ed75.zip |
Merge pull request #1546 from nicolas-fort/fwall-jump
T4699: Firewall: Add jump action in firewall ruleset
-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 4281fb34f..0e79994f5 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') |