diff options
-rw-r--r-- | interface-definitions/include/firewall/common-rule.xml.i | 20 | ||||
-rw-r--r-- | interface-definitions/include/firewall/name-default-log.xml.i | 43 | ||||
-rw-r--r-- | interface-definitions/include/firewall/rule-log-level.xml.i | 45 | ||||
-rw-r--r-- | python/vyos/firewall.py | 5 | ||||
-rw-r--r-- | python/vyos/template.py | 3 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 17 | ||||
-rwxr-xr-x | src/migration-scripts/firewall/6-to-7 | 27 |
7 files changed, 130 insertions, 30 deletions
diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i index 2a5137dbf..0b8838872 100644 --- a/interface-definitions/include/firewall/common-rule.xml.i +++ b/interface-definitions/include/firewall/common-rule.xml.i @@ -76,25 +76,7 @@ </leafNode> </children> </node> -<leafNode name="log"> - <properties> - <help>Option to log packets matching rule</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable log</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable log</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> - </properties> -</leafNode> +#include <include/firewall/rule-log-level.xml.i> <node name="connection-status"> <properties> <help>Connection status</help> diff --git a/interface-definitions/include/firewall/name-default-log.xml.i b/interface-definitions/include/firewall/name-default-log.xml.i index 979395146..c3f6f0171 100644 --- a/interface-definitions/include/firewall/name-default-log.xml.i +++ b/interface-definitions/include/firewall/name-default-log.xml.i @@ -1,8 +1,45 @@ <!-- include start from firewall/name-default-log.xml.i --> <leafNode name="enable-default-log"> <properties> - <help>Option to log packets hitting default-action</help> - <valueless/> + <help>Option to log packets matching default-action</help> + <completionHelp> + <list>emerg alert crit err warn notice info debug</list> + </completionHelp> + <valueHelp> + <format>emerg</format> + <description>Emerg log level</description> + </valueHelp> + <valueHelp> + <format>alert</format> + <description>Alert log level</description> + </valueHelp> + <valueHelp> + <format>crit</format> + <description>Critical log level</description> + </valueHelp> + <valueHelp> + <format>err</format> + <description>Error log level</description> + </valueHelp> + <valueHelp> + <format>warn</format> + <description>Warning log level</description> + </valueHelp> + <valueHelp> + <format>notice</format> + <description>Notice log level</description> + </valueHelp> + <valueHelp> + <format>info</format> + <description>Info log level</description> + </valueHelp> + <valueHelp> + <format>debug</format> + <description>Debug log level</description> + </valueHelp> + <constraint> + <regex>(emerg|alert|crit|err|warn|notice|info|debug)</regex> + </constraint> </properties> </leafNode> -<!-- include end --> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/rule-log-level.xml.i b/interface-definitions/include/firewall/rule-log-level.xml.i new file mode 100644 index 000000000..4842b73ca --- /dev/null +++ b/interface-definitions/include/firewall/rule-log-level.xml.i @@ -0,0 +1,45 @@ +<!-- include start from firewall/common-rule.xml.i --> +<leafNode name="log"> + <properties> + <help>Option to log packets matching rule</help> + <completionHelp> + <list>emerg alert crit err warn notice info debug</list> + </completionHelp> + <valueHelp> + <format>emerg</format> + <description>Emerg log level</description> + </valueHelp> + <valueHelp> + <format>alert</format> + <description>Alert log level</description> + </valueHelp> + <valueHelp> + <format>crit</format> + <description>Critical log level</description> + </valueHelp> + <valueHelp> + <format>err</format> + <description>Error log level</description> + </valueHelp> + <valueHelp> + <format>warn</format> + <description>Warning log level</description> + </valueHelp> + <valueHelp> + <format>notice</format> + <description>Notice log level</description> + </valueHelp> + <valueHelp> + <format>info</format> + <description>Info log level</description> + </valueHelp> + <valueHelp> + <format>debug</format> + <description>Debug log level</description> + </valueHelp> + <constraint> + <regex>(emerg|alert|crit|err|warn|notice|info|debug)</regex> + </constraint> + </properties> +</leafNode> +<!-- include end -->
\ No newline at end of file diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 04fd44173..0c6811d72 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -146,9 +146,10 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name): output.append(f'{proto} {prefix}port {operator} $P_{group_name}') - if 'log' in rule_conf and rule_conf['log'] == 'enable': + if 'log' in rule_conf: action = rule_conf['action'] if 'action' in rule_conf else 'accept' - output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}] "') + log_level = rule_conf['log'] + output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]" level {log_level}') if 'hop_limit' in rule_conf: operators = {'eq': '==', 'gt': '>', 'lt': '<'} diff --git a/python/vyos/template.py b/python/vyos/template.py index 132f5ddde..b41525421 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -554,7 +554,8 @@ def nft_default_rule(fw_conf, fw_name): if 'enable_default_log' in fw_conf: action_suffix = default_action[:1].upper() - output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}] "') + log_level = fw_conf['enable_default_log'] + output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}]" level {log_level}') output.append(nft_action(default_action)) output.append(f'comment "{fw_name} default-action {default_action}"') diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index b8f944575..7b9691d9d 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -91,12 +91,15 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_basic_rules(self): self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop']) + self.cli_set(['firewall', 'name', 'smoketest', 'enable-default-log', 'info']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10']) + self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'log', 'debug']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'protocol', 'tcp']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'destination', 'port', '8888']) + self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'log', 'err']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'tcp', 'flags', 'syn']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'tcp', 'flags', 'not', 'ack']) self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept']) @@ -110,10 +113,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ ['iifname "eth0"', 'jump NAME_smoketest'], - ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'return'], - ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'reject'], + ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" level debug','return'], + ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'log prefix "[smoketest-2-R]" level err', 'reject'], ['tcp dport { 22 }', 'limit rate 5/minute', 'return'], - ['smoketest default-action', 'drop'] + ['log prefix "[smoketest-default-D]" level info','smoketest default-action', 'drop'] ] nftables_output = cmd('sudo nft list table ip filter') @@ -128,9 +131,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_basic_rules_ipv6(self): self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'default-action', 'drop']) + self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'enable-default-log', 'emerg']) + self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'action', 'accept']) self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'source', 'address', '2002::1']) self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'destination', 'address', '2002::1:1']) + self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'log', 'crit']) + self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'protocol', 'tcp_udp']) self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'destination', 'port', '8888']) @@ -141,9 +148,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ ['iifname "eth0"', 'jump NAME6_v6-smoketest'], - ['saddr 2002::1', 'daddr 2002::1:1', 'return'], + ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" level crit', 'return'], ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'], - ['smoketest default-action', 'drop'] + ['smoketest default-action', 'log prefix "[v6-smoketest-default-D]" level emerg', 'drop'] ] nftables_output = cmd('sudo nft list table ip6 filter') diff --git a/src/migration-scripts/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7 index 5f4cff90d..1e698da0b 100755 --- a/src/migration-scripts/firewall/6-to-7 +++ b/src/migration-scripts/firewall/6-to-7 @@ -19,6 +19,11 @@ # utc: nftables userspace uses localtime and calculates the UTC offset automatically # icmp/v6: migrate previously available `type-name` to valid type/code # T4178: Update tcp flags to use multi value node +# T3907: Add log levels +# `enable-default-log` --> `enable-default-log warn` +# `rule X log enable` --> `rule X log warn` +# `rule X log disable` --> No log config + import re @@ -100,6 +105,9 @@ icmpv6_translations = { if config.exists(base + ['name']): for name in config.list_nodes(base + ['name']): + if config.exists(base + ['name', name, 'enable-default-log']): + config.set(base + ['name', name, 'enable-default-log'], value='warn') + if not config.exists(base + ['name', name, 'rule']): continue @@ -108,6 +116,7 @@ if config.exists(base + ['name']): rule_time = base + ['name', name, 'rule', rule, 'time'] rule_tcp_flags = base + ['name', name, 'rule', rule, 'tcp', 'flags'] rule_icmp = base + ['name', name, 'rule', rule, 'icmp'] + rule_log = base + ['name', name, 'rule', rule, 'log'] if config.exists(rule_time + ['monthdays']): config.delete(rule_time + ['monthdays']) @@ -146,6 +155,13 @@ if config.exists(base + ['name']): config.set(rule_icmp + ['type'], value=translate[0]) config.set(rule_icmp + ['code'], value=translate[1]) + if config.exists(rule_log): + tmp = config.return_value(rule_log) + if tmp == 'disable': + config.delete(rule_log) + else: + config.set(rule_log, value='warn') + for src_dst in ['destination', 'source']: pg_base = base + ['name', name, 'rule', rule, src_dst, 'group', 'port-group'] proto_base = base + ['name', name, 'rule', rule, 'protocol'] @@ -153,6 +169,9 @@ if config.exists(base + ['name']): config.set(proto_base, value='tcp_udp') if config.exists(base + ['ipv6-name']): + if config.exists(base + ['ipv6-name', name, 'enable-default-log']): + config.set(base + ['ipv6-name', name, 'enable-default-log'], value='warn') + for name in config.list_nodes(base + ['ipv6-name']): if not config.exists(base + ['ipv6-name', name, 'rule']): continue @@ -162,6 +181,7 @@ if config.exists(base + ['ipv6-name']): rule_time = base + ['ipv6-name', name, 'rule', rule, 'time'] rule_tcp_flags = base + ['ipv6-name', name, 'rule', rule, 'tcp', 'flags'] rule_icmp = base + ['ipv6-name', name, 'rule', rule, 'icmpv6'] + rule_log = base + ['ipv6-name', name, 'rule', rule, 'log'] if config.exists(rule_time + ['monthdays']): config.delete(rule_time + ['monthdays']) @@ -212,6 +232,13 @@ if config.exists(base + ['ipv6-name']): else: config.rename(rule_icmp + ['type'], 'type-name') + if config.exists(rule_log): + tmp = config.return_value(rule_log) + if tmp == 'disable': + config.delete(rule_log) + else: + config.set(rule_log, value='warn') + for src_dst in ['destination', 'source']: pg_base = base + ['ipv6-name', name, 'rule', rule, src_dst, 'group', 'port-group'] proto_base = base + ['ipv6-name', name, 'rule', rule, 'protocol'] |