diff options
25 files changed, 452 insertions, 173 deletions
diff --git a/data/templates/dns-forwarding/override.conf.j2 b/data/templates/dns-forwarding/override.conf.j2 new file mode 100644 index 000000000..9d81a2977 --- /dev/null +++ b/data/templates/dns-forwarding/override.conf.j2 @@ -0,0 +1,8 @@ +[Unit] +ConditionPathExists={{ config_file }} +After=vyos-router.service + +[Service] +RuntimeDirectoryPreserve=yes +ExecStart= +ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir={{ config_dir }} diff --git a/data/templates/dns-forwarding/recursor.conf.j2 b/data/templates/dns-forwarding/recursor.conf.j2 index 55b37732b..e4e8e7044 100644 --- a/data/templates/dns-forwarding/recursor.conf.j2 +++ b/data/templates/dns-forwarding/recursor.conf.j2 @@ -12,7 +12,7 @@ allow-from={{ allow_from | join(',') }} log-common-errors=yes non-local-bind=yes query-local-address={{ source_address | join(',') }} -lua-config-file=recursor.conf.lua +lua-config-file={{ config_dir }}/recursor.conf.lua # cache-size max-cache-entries={{ cache_size }} @@ -56,4 +56,4 @@ serve-rfc1918={{ 'no' if no_serve_rfc1918 is vyos_defined else 'yes' }} # zones auth-zones={% for z in authoritative_zones %}{{ z.name }}={{ z.file }}{{- "," if not loop.last -}}{% endfor %} -forward-zones-file=recursor.forward-zones.conf +forward-zones-file={{ config_dir }}/recursor.forward-zones.conf diff --git a/data/templates/dns-forwarding/recursor.conf.lua.j2 b/data/templates/dns-forwarding/recursor.conf.lua.j2 index 816f69160..8026442c7 100644 --- a/data/templates/dns-forwarding/recursor.conf.lua.j2 +++ b/data/templates/dns-forwarding/recursor.conf.lua.j2 @@ -5,4 +5,4 @@ dofile("/usr/share/pdns-recursor/lua-config/rootkeys.lua") -- Load lua from vyos-hostsd -- -dofile("recursor.vyos-hostsd.conf.lua") +dofile("{{ config_dir }}/recursor.vyos-hostsd.conf.lua") diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2 index a20c399ae..8a75ab2d6 100644 --- a/data/templates/firewall/nftables-defines.j2 +++ b/data/templates/firewall/nftables-defines.j2 @@ -98,5 +98,26 @@ } {% endfor %} {% endif %} + +{% if group.dynamic_group is vyos_defined %} +{% if group.dynamic_group.address_group is vyos_defined and not is_ipv6 and is_l3 %} +{% for group_name, group_conf in group.dynamic_group.address_group.items() %} + set DA_{{ group_name }} { + type {{ ip_type }} + flags dynamic, timeout + } +{% endfor %} +{% endif %} + +{% if group.dynamic_group.ipv6_address_group is vyos_defined and is_ipv6 and is_l3 %} +{% for group_name, group_conf in group.dynamic_group.ipv6_address_group.items() %} + set DA6_{{ group_name }} { + type {{ ip_type }} + flags dynamic, timeout + } +{% endfor %} +{% endif %} +{% endif %} + {% endif %} {% endmacro %} diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index a4023058f..662ba24ab 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -115,6 +115,35 @@ #include <include/generic-description.xml.i> </children> </tagNode> + <node name="dynamic-group"> + <properties> + <help>Firewall dynamic group</help> + </properties> + <children> + <tagNode name="address-group"> + <properties> + <help>Firewall dynamic address group</help> + <constraint> + <regex>[a-zA-Z0-9][\w\-\.]*</regex> + </constraint> + </properties> + <children> + #include <include/generic-description.xml.i> + </children> + </tagNode> + <tagNode name="ipv6-address-group"> + <properties> + <help>Firewall dynamic IPv6 address group</help> + <constraint> + <regex>[a-zA-Z0-9][\w\-\.]*</regex> + </constraint> + </properties> + <children> + #include <include/generic-description.xml.i> + </children> + </tagNode> + </children> + </node> <tagNode name="interface-group"> <properties> <help>Firewall interface-group</help> diff --git a/interface-definitions/include/firewall/add-dynamic-address-groups.xml.i b/interface-definitions/include/firewall/add-dynamic-address-groups.xml.i new file mode 100644 index 000000000..769761cb6 --- /dev/null +++ b/interface-definitions/include/firewall/add-dynamic-address-groups.xml.i @@ -0,0 +1,34 @@ +<!-- include start from firewall/add-dynamic-address-groups.xml.i --> +<leafNode name="address-group"> + <properties> + <help>Dynamic address-group</help> + <completionHelp> + <path>firewall group dynamic-group address-group</path> + </completionHelp> + </properties> +</leafNode> +<leafNode name="timeout"> + <properties> + <help>Set timeout</help> + <valueHelp> + <format><number>s</format> + <description>Timeout value in seconds</description> + </valueHelp> + <valueHelp> + <format><number>m</format> + <description>Timeout value in minutes</description> + </valueHelp> + <valueHelp> + <format><number>h</format> + <description>Timeout value in hours</description> + </valueHelp> + <valueHelp> + <format><number>d</format> + <description>Timeout value in days</description> + </valueHelp> + <constraint> + <regex>\d+(s|m|h|d)</regex> + </constraint> + </properties> +</leafNode> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/add-dynamic-ipv6-address-groups.xml.i b/interface-definitions/include/firewall/add-dynamic-ipv6-address-groups.xml.i new file mode 100644 index 000000000..7bd91c58a --- /dev/null +++ b/interface-definitions/include/firewall/add-dynamic-ipv6-address-groups.xml.i @@ -0,0 +1,34 @@ +<!-- include start from firewall/add-dynamic-ipv6-address-groups.xml.i --> +<leafNode name="address-group"> + <properties> + <help>Dynamic ipv6-address-group</help> + <completionHelp> + <path>firewall group dynamic-group ipv6-address-group</path> + </completionHelp> + </properties> +</leafNode> +<leafNode name="timeout"> + <properties> + <help>Set timeout</help> + <valueHelp> + <format><number>s</format> + <description>Timeout value in seconds</description> + </valueHelp> + <valueHelp> + <format><number>m</format> + <description>Timeout value in minutes</description> + </valueHelp> + <valueHelp> + <format><number>h</format> + <description>Timeout value in hours</description> + </valueHelp> + <valueHelp> + <format><number>d</format> + <description>Timeout value in days</description> + </valueHelp> + <constraint> + <regex>\d+(s|m|h|d)</regex> + </constraint> + </properties> +</leafNode> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/common-rule-ipv4.xml.i b/interface-definitions/include/firewall/common-rule-ipv4.xml.i index 4ed179ae7..158c7a662 100644 --- a/interface-definitions/include/firewall/common-rule-ipv4.xml.i +++ b/interface-definitions/include/firewall/common-rule-ipv4.xml.i @@ -1,6 +1,29 @@ <!-- include start from firewall/common-rule-ipv4.xml.i --> #include <include/firewall/common-rule-inet.xml.i> #include <include/firewall/ttl.xml.i> +<node name="add-address-to-group"> + <properties> + <help>Add ip address to dynamic address-group</help> + </properties> + <children> + <node name="source-address"> + <properties> + <help>Add source ip addresses to dynamic address-group</help> + </properties> + <children> + #include <include/firewall/add-dynamic-address-groups.xml.i> + </children> + </node> + <node name="destination-address"> + <properties> + <help>Add destination ip addresses to dynamic address-group</help> + </properties> + <children> + #include <include/firewall/add-dynamic-address-groups.xml.i> + </children> + </node> + </children> +</node> <node name="destination"> <properties> <help>Destination parameters</help> @@ -13,6 +36,7 @@ #include <include/firewall/mac-address.xml.i> #include <include/firewall/port.xml.i> #include <include/firewall/source-destination-group.xml.i> + #include <include/firewall/source-destination-dynamic-group.xml.i> </children> </node> <node name="icmp"> @@ -67,6 +91,7 @@ #include <include/firewall/mac-address.xml.i> #include <include/firewall/port.xml.i> #include <include/firewall/source-destination-group.xml.i> + #include <include/firewall/source-destination-dynamic-group.xml.i> </children> </node> <!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/common-rule-ipv6.xml.i b/interface-definitions/include/firewall/common-rule-ipv6.xml.i index 6219557db..78eeb361e 100644 --- a/interface-definitions/include/firewall/common-rule-ipv6.xml.i +++ b/interface-definitions/include/firewall/common-rule-ipv6.xml.i @@ -1,6 +1,29 @@ <!-- include start from firewall/common-rule-ipv6.xml.i --> #include <include/firewall/common-rule-inet.xml.i> #include <include/firewall/hop-limit.xml.i> +<node name="add-address-to-group"> + <properties> + <help>Add ipv6 address to dynamic ipv6-address-group</help> + </properties> + <children> + <node name="source-address"> + <properties> + <help>Add source ipv6 addresses to dynamic ipv6-address-group</help> + </properties> + <children> + #include <include/firewall/add-dynamic-ipv6-address-groups.xml.i> + </children> + </node> + <node name="destination-address"> + <properties> + <help>Add destination ipv6 addresses to dynamic ipv6-address-group</help> + </properties> + <children> + #include <include/firewall/add-dynamic-ipv6-address-groups.xml.i> + </children> + </node> + </children> +</node> <node name="destination"> <properties> <help>Destination parameters</help> @@ -13,6 +36,7 @@ #include <include/firewall/mac-address.xml.i> #include <include/firewall/port.xml.i> #include <include/firewall/source-destination-group-ipv6.xml.i> + #include <include/firewall/source-destination-dynamic-group-ipv6.xml.i> </children> </node> <node name="icmpv6"> @@ -67,6 +91,7 @@ #include <include/firewall/mac-address.xml.i> #include <include/firewall/port.xml.i> #include <include/firewall/source-destination-group-ipv6.xml.i> + #include <include/firewall/source-destination-dynamic-group-ipv6.xml.i> </children> </node> <!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/source-destination-dynamic-group-ipv6.xml.i b/interface-definitions/include/firewall/source-destination-dynamic-group-ipv6.xml.i new file mode 100644 index 000000000..845f8fe7c --- /dev/null +++ b/interface-definitions/include/firewall/source-destination-dynamic-group-ipv6.xml.i @@ -0,0 +1,17 @@ +<!-- include start from firewall/source-destination-dynamic-group-ipv6.xml.i --> +<node name="group"> + <properties> + <help>Group</help> + </properties> + <children> + <leafNode name="dynamic-address-group"> + <properties> + <help>Group of dynamic ipv6 addresses</help> + <completionHelp> + <path>firewall group dynamic-group ipv6-address-group</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/firewall/source-destination-dynamic-group.xml.i b/interface-definitions/include/firewall/source-destination-dynamic-group.xml.i new file mode 100644 index 000000000..29ab98c68 --- /dev/null +++ b/interface-definitions/include/firewall/source-destination-dynamic-group.xml.i @@ -0,0 +1,17 @@ +<!-- include start from firewall/source-destination-dynamic-group.xml.i --> +<node name="group"> + <properties> + <help>Group</help> + </properties> + <children> + <leafNode name="dynamic-address-group"> + <properties> + <help>Group of dynamic addresses</help> + <completionHelp> + <path>firewall group dynamic-group address-group</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/op-mode-definitions/dns-forwarding.xml.in b/op-mode-definitions/dns-forwarding.xml.in index a4c650c38..ebedae6eb 100644 --- a/op-mode-definitions/dns-forwarding.xml.in +++ b/op-mode-definitions/dns-forwarding.xml.in @@ -11,7 +11,7 @@ <children> <node name="forwarding"> <properties> - <help>Monitor last lines of DNS forwarding</help> + <help>Monitor last lines of DNS Forwarding</help> </properties> <command>journalctl --no-hostname --follow --boot --unit pdns-recursor.service</command> </node> @@ -47,12 +47,12 @@ <children> <node name="forwarding"> <properties> - <help>Show DNS forwarding information</help> + <help>Show DNS Forwarding information</help> </properties> <children> <leafNode name="statistics"> <properties> - <help>Show DNS forwarding statistics</help> + <help>Show DNS Forwarding statistics</help> </properties> <command>sudo ${vyos_op_scripts_dir}/dns.py show_forwarding_statistics</command> </leafNode> @@ -71,9 +71,9 @@ <children> <leafNode name="forwarding"> <properties> - <help>Restart DNS forwarding service</help> + <help>Restart DNS Forwarding service</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/dns_forwarding_restart.sh</command> + <command>if cli-shell-api existsActive service dns forwarding; then sudo systemctl restart pdns-recursor.service; else echo "DNS forwarding not configured"; fi</command> </leafNode> </children> </node> @@ -88,19 +88,19 @@ <children> <node name="forwarding"> <properties> - <help>Reset DNS forwarding cache</help> + <help>Reset DNS Forwarding cache</help> </properties> <children> <tagNode name="domain"> - <command>sudo ${vyos_op_scripts_dir}/dns_forwarding_reset.py $5</command> + <command>sudo ${vyos_op_scripts_dir}/dns.py reset_forwarding --domain $5</command> <properties> - <help>Reset DNS forwarding cache for a domain</help> + <help>Reset DNS Forwarding cache for a domain</help> </properties> </tagNode> <leafNode name="all"> - <command>sudo ${vyos_op_scripts_dir}/dns_forwarding_reset.py --all</command> + <command>sudo ${vyos_op_scripts_dir}/dns.py reset_forwarding --all</command> <properties> - <help>Reset DNS forwarding cache</help> + <help>Reset DNS Forwarding cache for all domains</help> </properties> </leafNode> </children> diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 28ebf282c..eee11bd2d 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -226,6 +226,14 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): operator = '!=' if exclude else '==' operator = f'& {address_mask} {operator}' output.append(f'{ip_name} {prefix}addr {operator} @A{def_suffix}_{group_name}') + elif 'dynamic_address_group' in group: + group_name = group['dynamic_address_group'] + operator = '' + exclude = group_name[0] == "!" + if exclude: + operator = '!=' + group_name = group_name[1:] + output.append(f'{ip_name} {prefix}addr {operator} @DA{def_suffix}_{group_name}') # Generate firewall group domain-group elif 'domain_group' in group: group_name = group['domain_group'] @@ -419,6 +427,18 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): output.append('counter') + if 'add_address_to_group' in rule_conf: + for side in ['destination_address', 'source_address']: + if side in rule_conf['add_address_to_group']: + prefix = side[0] + side_conf = rule_conf['add_address_to_group'][side] + dyn_group = side_conf['address_group'] + if 'timeout' in side_conf: + timeout_value = side_conf['timeout'] + output.append(f'set update ip{def_suffix} {prefix}addr timeout {timeout_value} @DA{def_suffix}_{dyn_group}') + else: + output.append(f'set update ip{def_suffix} saddr @DA{def_suffix}_{dyn_group}') + if 'set' in rule_conf: output.append(parse_policy_set(rule_conf['set'], def_suffix)) diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 72fbdb37d..a7dd11145 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -403,6 +403,46 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.verify_nftables(nftables_search, 'ip vyos_filter') + def test_ipv4_dynamic_groups(self): + group01 = 'knock01' + group02 = 'allowed' + + self.cli_set(['firewall', 'group', 'dynamic-group', 'address-group', group01]) + self.cli_set(['firewall', 'group', 'dynamic-group', 'address-group', group02]) + + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'port', '5151']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'address-group', group01]) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'timeout', '30s']) + + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'action', 'drop']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'destination', 'port', '7272']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'source', 'group', 'dynamic-address-group', group01]) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'address-group', group02]) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'timeout', '5m']) + + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'action', 'accept']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'destination', 'port', '22']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'source', 'group', 'dynamic-address-group', group02]) + + self.cli_commit() + + nftables_search = [ + [f'DA_{group01}'], + [f'DA_{group02}'], + ['type ipv4_addr'], + ['flags dynamic,timeout'], + ['chain VYOS_INPUT_filter {'], + ['type filter hook input priority filter', 'policy accept'], + ['tcp dport 5151', f'update @DA_{group01}', '{ ip saddr timeout 30s }', 'drop'], + ['tcp dport 7272', f'ip saddr @DA_{group01}', f'update @DA_{group02}', '{ ip saddr timeout 5m }', 'drop'], + ['tcp dport 22', f'ip saddr @DA_{group02}', 'accept'] + ] + + self.verify_nftables(nftables_search, 'ip vyos_filter') def test_ipv6_basic_rules(self): name = 'v6-smoketest' @@ -540,6 +580,47 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.verify_nftables(nftables_search, 'ip6 vyos_filter') + def test_ipv6_dynamic_groups(self): + group01 = 'knock01' + group02 = 'allowed' + + self.cli_set(['firewall', 'group', 'dynamic-group', 'ipv6-address-group', group01]) + self.cli_set(['firewall', 'group', 'dynamic-group', 'ipv6-address-group', group02]) + + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'action', 'drop']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'destination', 'port', '5151']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'address-group', group01]) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'timeout', '30s']) + + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'action', 'drop']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'destination', 'port', '7272']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'source', 'group', 'dynamic-address-group', group01]) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'address-group', group02]) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'timeout', '5m']) + + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'action', 'accept']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'destination', 'port', '22']) + self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'source', 'group', 'dynamic-address-group', group02]) + + self.cli_commit() + + nftables_search = [ + [f'DA6_{group01}'], + [f'DA6_{group02}'], + ['type ipv6_addr'], + ['flags dynamic,timeout'], + ['chain VYOS_IPV6_INPUT_filter {'], + ['type filter hook input priority filter', 'policy accept'], + ['tcp dport 5151', f'update @DA6_{group01}', '{ ip6 saddr timeout 30s }', 'drop'], + ['tcp dport 7272', f'ip6 saddr @DA6_{group01}', f'update @DA6_{group02}', '{ ip6 saddr timeout 5m }', 'drop'], + ['tcp dport 22', f'ip6 saddr @DA6_{group02}', 'accept'] + ] + + self.verify_nftables(nftables_search, 'ip6 vyos_filter') + def test_ipv4_state_and_status_rules(self): name = 'smoketest-state' interface = 'eth0' diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index 85a5f1448..652c4fa7b 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -24,9 +24,10 @@ from vyos.template import bracketize_ipv6 from vyos.utils.file import read_file from vyos.utils.process import process_named_running -CONFIG_FILE = '/run/powerdns/recursor.conf' -FORWARD_FILE = '/run/powerdns/recursor.forward-zones.conf' -HOSTSD_FILE = '/run/powerdns/recursor.vyos-hostsd.conf.lua' +PDNS_REC_RUN_DIR = '/run/pdns-recursor' +CONFIG_FILE = f'{PDNS_REC_RUN_DIR}/recursor.conf' +FORWARD_FILE = f'{PDNS_REC_RUN_DIR}/recursor.forward-zones.conf' +HOSTSD_FILE = f'{PDNS_REC_RUN_DIR}/recursor.vyos-hostsd.conf.lua' PROCESS_NAME= 'pdns_recursor' base_path = ['service', 'dns', 'forwarding'] @@ -279,7 +280,7 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase): def test_listening_port(self): # We can listen on a different port compared to '53' but only one at a time - for port in ['1053', '5353']: + for port in ['10053', '10054']: self.cli_set(base_path + ['port', port]) for network in allow_from: self.cli_set(base_path + ['allow-from', network]) diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index bd9b5162c..26822b755 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -69,6 +69,10 @@ def get_config(config=None): nat['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + # Remove dynamic firewall groups if present: + if 'dynamic_group' in nat['firewall_group']: + del nat['firewall_group']['dynamic_group'] + return nat def verify_rule(config, err_msg, groups_dict): diff --git a/src/conf_mode/policy_route.py b/src/conf_mode/policy_route.py index adad012de..6d7a06714 100755 --- a/src/conf_mode/policy_route.py +++ b/src/conf_mode/policy_route.py @@ -53,6 +53,10 @@ def get_config(config=None): policy['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + # Remove dynamic firewall groups if present: + if 'dynamic_group' in policy['firewall_group']: + del policy['firewall_group']['dynamic_group'] + return policy def verify_rule(policy, name, rule_conf, ipv6, rule_id): diff --git a/src/conf_mode/service_dns_forwarding.py b/src/conf_mode/service_dns_forwarding.py index c186f47af..ecad765f4 100755 --- a/src/conf_mode/service_dns_forwarding.py +++ b/src/conf_mode/service_dns_forwarding.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -26,18 +26,18 @@ from vyos.template import render from vyos.template import bracketize_ipv6 from vyos.utils.process import call from vyos.utils.permission import chown -from vyos.utils.dict import dict_search from vyos import ConfigError from vyos import airbag airbag.enable() -pdns_rec_user = pdns_rec_group = 'pdns' -pdns_rec_run_dir = '/run/powerdns' +pdns_rec_user_group = 'pdns' +pdns_rec_run_dir = '/run/pdns-recursor' pdns_rec_lua_conf_file = f'{pdns_rec_run_dir}/recursor.conf.lua' pdns_rec_hostsd_lua_conf_file = f'{pdns_rec_run_dir}/recursor.vyos-hostsd.conf.lua' pdns_rec_hostsd_zones_file = f'{pdns_rec_run_dir}/recursor.forward-zones.conf' pdns_rec_config_file = f'{pdns_rec_run_dir}/recursor.conf' +pdns_rec_systemd_override = '/run/systemd/system/pdns-recursor.service.d/override.conf' hostsd_tag = 'static' @@ -55,6 +55,9 @@ def get_config(config=None): get_first_key=True, with_recursive_defaults=True) + dns['config_file'] = pdns_rec_config_file + dns['config_dir'] = os.path.dirname(pdns_rec_config_file) + # some additions to the default dictionary if 'system' in dns: base_nameservers = ['system', 'name-server'] @@ -251,11 +254,16 @@ def generate(dns): if not dns: return None - render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.j2', - dns, user=pdns_rec_user, group=pdns_rec_group) + render(pdns_rec_systemd_override, 'dns-forwarding/override.conf.j2', dns) + + render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.j2', dns, + user=pdns_rec_user_group, group=pdns_rec_user_group) - render(pdns_rec_lua_conf_file, 'dns-forwarding/recursor.conf.lua.j2', - dns, user=pdns_rec_user, group=pdns_rec_group) + render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.j2', dns, + user=pdns_rec_user_group, group=pdns_rec_user_group) + + render(pdns_rec_lua_conf_file, 'dns-forwarding/recursor.conf.lua.j2', dns, + user=pdns_rec_user_group, group=pdns_rec_user_group) for zone_filename in glob(f'{pdns_rec_run_dir}/zone.*.conf'): os.unlink(zone_filename) @@ -263,21 +271,25 @@ def generate(dns): if 'authoritative_zones' in dns: for zone in dns['authoritative_zones']: render(zone['file'], 'dns-forwarding/recursor.zone.conf.j2', - zone, user=pdns_rec_user, group=pdns_rec_group) + zone, user=pdns_rec_user_group, group=pdns_rec_user_group) # if vyos-hostsd didn't create its files yet, create them (empty) for file in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]: with open(file, 'a'): pass - chown(file, user=pdns_rec_user, group=pdns_rec_group) + chown(file, user=pdns_rec_user_group, group=pdns_rec_user_group) return None def apply(dns): + systemd_service = 'pdns-recursor.service' + # Reload systemd manager configuration + call('systemctl daemon-reload') + if not dns: # DNS forwarding is removed in the commit - call('systemctl stop pdns-recursor.service') + call(f'systemctl stop {systemd_service}') if os.path.isfile(pdns_rec_config_file): os.unlink(pdns_rec_config_file) @@ -345,7 +357,7 @@ def apply(dns): hc.apply() ### finally (re)start pdns-recursor - call('systemctl restart pdns-recursor.service') + call(f'systemctl reload-or-restart {systemd_service}') if __name__ == '__main__': try: diff --git a/src/etc/systemd/system/pdns-recursor.service.d/override.conf b/src/etc/systemd/system/pdns-recursor.service.d/override.conf deleted file mode 100644 index 158bac02b..000000000 --- a/src/etc/systemd/system/pdns-recursor.service.d/override.conf +++ /dev/null @@ -1,8 +0,0 @@ -[Service] -WorkingDirectory= -WorkingDirectory=/run/powerdns -RuntimeDirectory= -RuntimeDirectory=powerdns -RuntimeDirectoryPreserve=yes -ExecStart= -ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns --socket-dir=/run/powerdns diff --git a/src/op_mode/dns.py b/src/op_mode/dns.py index 2168aef89..309bef3b9 100755 --- a/src/op_mode/dns.py +++ b/src/op_mode/dns.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -15,17 +15,16 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. +import typing import sys +import vyos.opmode from tabulate import tabulate - from vyos.configquery import ConfigTreeQuery -from vyos.utils.process import cmd +from vyos.utils.process import cmd, rc_cmd -import vyos.opmode - -def _data_to_dict(data, sep="\t") -> dict: +def _forwarding_data_to_dict(data, sep="\t") -> dict: """ Return dictionary from plain text separated by tab @@ -52,15 +51,15 @@ def _data_to_dict(data, sep="\t") -> dict: return dictionary -def _get_raw_forwarding_statistics() -> dict: - command = cmd('rec_control --socket-dir=/run/powerdns get-all') - data = _data_to_dict(command) +def _get_forwarding_statistics_raw() -> dict: + command = cmd('rec_control get-all') + data = _forwarding_data_to_dict(command) data['cache-size'] = "{0:.2f}".format( int( - cmd('rec_control --socket-dir=/run/powerdns get cache-bytes')) / 1024 ) + cmd('rec_control get cache-bytes')) / 1024 ) return data -def _get_formatted_forwarding_statistics(data): +def _get_forwarding_statistics_formatted(data): cache_entries = data.get('cache-entries') max_cache_entries = data.get('max-cache-entries') cache_size = data.get('cache-size') @@ -69,19 +68,48 @@ def _get_formatted_forwarding_statistics(data): output = tabulate(data_entries, headers, numalign="left") return output +def _verify_forwarding(func): + """Decorator checks if DNS Forwarding config exists""" + from functools import wraps -def show_forwarding_statistics(raw: bool): - - config = ConfigTreeQuery() - if not config.exists('service dns forwarding'): - raise vyos.opmode.UnconfiguredSubsystem('DNS forwarding is not configured') + @wraps(func) + def _wrapper(*args, **kwargs): + config = ConfigTreeQuery() + if not config.exists('service dns forwarding'): + raise vyos.opmode.UnconfiguredSubsystem('DNS Forwarding is not configured') + return func(*args, **kwargs) + return _wrapper - dns_data = _get_raw_forwarding_statistics() +@_verify_forwarding +def show_forwarding_statistics(raw: bool): + dns_data = _get_forwarding_statistics_raw() if raw: return dns_data else: - return _get_formatted_forwarding_statistics(dns_data) + return _get_forwarding_statistics_formatted(dns_data) +@_verify_forwarding +def reset_forwarding(all: bool, domain: typing.Optional[str]): + """ + Reset DNS Forwarding cache + + :param all (bool): reset cache all domains + :param domain (str): reset cache for specified domain + """ + if all: + rc, output = rc_cmd('rec_control wipe-cache ".$"') + if rc != 0: + print(output) + return None + print('DNS Forwarding cache reset for all domains!') + return output + elif domain: + rc, output = rc_cmd(f'rec_control wipe-cache "{domain}$"') + if rc != 0: + print(output) + return None + print(f'DNS Forwarding cache reset for domain "{domain}"!') + return output if __name__ == '__main__': try: diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py deleted file mode 100755 index 55e20918f..000000000 --- a/src/op_mode/dns_forwarding_reset.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# File: vyos-show-version -# Purpose: -# Displays image version and system information. -# Used by the "run show version" command. - - -import os -import argparse - -from sys import exit -from vyos.config import Config -from vyos.utils.process import call - -PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns' - -parser = argparse.ArgumentParser() -parser.add_argument("-a", "--all", action="store_true", help="Reset all cache") -parser.add_argument("domain", type=str, nargs="?", help="Domain to reset cache entries for") - -if __name__ == '__main__': - args = parser.parse_args() - - # Do nothing if service is not configured - c = Config() - if not c.exists_effective(['service', 'dns', 'forwarding']): - print("DNS forwarding is not configured") - exit(0) - - if args.all: - call(f"{PDNS_CMD} wipe-cache \'.$\'") - exit(0) - - elif args.domain: - call(f"{PDNS_CMD} wipe-cache \'{0}$\'".format(args.domain)) - - else: - parser.print_help() - exit(1) diff --git a/src/op_mode/dns_forwarding_restart.sh b/src/op_mode/dns_forwarding_restart.sh deleted file mode 100755 index 64cc92115..000000000 --- a/src/op_mode/dns_forwarding_restart.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if cli-shell-api existsEffective service dns forwarding; then - echo "Restarting the DNS forwarding service" - systemctl restart pdns-recursor.service -else - echo "DNS forwarding is not configured" -fi diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py deleted file mode 100755 index 32b5c76a7..000000000 --- a/src/op_mode/dns_forwarding_statistics.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -import jinja2 -from sys import exit - -from vyos.config import Config -from vyos.utils.process import cmd - -PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns' - -OUT_TMPL_SRC = """ -DNS forwarding statistics: - -Cache entries: {{ cache_entries }} -Cache size: {{ cache_size }} kbytes - -""" - -if __name__ == '__main__': - # Do nothing if service is not configured - c = Config() - if not c.exists_effective('service dns forwarding'): - print("DNS forwarding is not configured") - exit(0) - - data = {} - - data['cache_entries'] = cmd(f'{PDNS_CMD} get cache-entries') - data['cache_size'] = "{0:.2f}".format( int(cmd(f'{PDNS_CMD} get cache-bytes')) / 1024 ) - - tmpl = jinja2.Template(OUT_TMPL_SRC) - print(tmpl.render(data)) diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index 36bb013fe..4dcffc412 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -327,6 +327,8 @@ def show_firewall_group(name=None): dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type) in_interface = dict_search_args(rule_conf, 'inbound_interface', 'group') out_interface = dict_search_args(rule_conf, 'outbound_interface', 'group') + dyn_group_source = dict_search_args(rule_conf, 'add_address_to_group', 'source_address', group_type) + dyn_group_dst = dict_search_args(rule_conf, 'add_address_to_group', 'destination_address', group_type) if source_group: if source_group[0] == "!": source_group = source_group[1:] @@ -348,6 +350,14 @@ def show_firewall_group(name=None): if group_name == out_interface: out.append(f'{item}-{name_type}-{priority}-{rule_id}') + if dyn_group_source: + if group_name == dyn_group_source: + out.append(f'{item}-{name_type}-{priority}-{rule_id}') + if dyn_group_dst: + if group_name == dyn_group_dst: + out.append(f'{item}-{name_type}-{priority}-{rule_id}') + + # Look references in route | route6 for name_type in ['route', 'route6']: if name_type not in policy: @@ -423,26 +433,37 @@ def show_firewall_group(name=None): rows = [] for group_type, group_type_conf in firewall['group'].items(): - for group_name, group_conf in group_type_conf.items(): - if name and name != group_name: - continue + ## + if group_type != 'dynamic_group': - references = find_references(group_type, group_name) - row = [group_name, group_type, '\n'.join(references) or 'N/D'] - if 'address' in group_conf: - row.append("\n".join(sorted(group_conf['address']))) - elif 'network' in group_conf: - row.append("\n".join(sorted(group_conf['network'], key=ipaddress.ip_network))) - elif 'mac_address' in group_conf: - row.append("\n".join(sorted(group_conf['mac_address']))) - elif 'port' in group_conf: - row.append("\n".join(sorted(group_conf['port']))) - elif 'interface' in group_conf: - row.append("\n".join(sorted(group_conf['interface']))) - else: - row.append('N/D') - rows.append(row) + for group_name, group_conf in group_type_conf.items(): + if name and name != group_name: + continue + references = find_references(group_type, group_name) + row = [group_name, group_type, '\n'.join(references) or 'N/D'] + if 'address' in group_conf: + row.append("\n".join(sorted(group_conf['address']))) + elif 'network' in group_conf: + row.append("\n".join(sorted(group_conf['network'], key=ipaddress.ip_network))) + elif 'mac_address' in group_conf: + row.append("\n".join(sorted(group_conf['mac_address']))) + elif 'port' in group_conf: + row.append("\n".join(sorted(group_conf['port']))) + elif 'interface' in group_conf: + row.append("\n".join(sorted(group_conf['interface']))) + else: + row.append('N/D') + rows.append(row) + + else: + for dynamic_type in ['address_group', 'ipv6_address_group']: + if dynamic_type in firewall['group']['dynamic_group']: + for dynamic_name, dynamic_conf in firewall['group']['dynamic_group'][dynamic_type].items(): + references = find_references(dynamic_type, dynamic_name) + row = [dynamic_name, dynamic_type + '(dynamic)', '\n'.join(references) or 'N/D'] + row.append('N/D') + rows.append(row) if rows: print('Firewall Groups\n') diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index e34a4b740..1ba90471e 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -271,8 +271,8 @@ SOCKET_PATH = "ipc://" + os.path.join(RUN_DIR, 'vyos-hostsd.sock') RESOLV_CONF_FILE = '/etc/resolv.conf' HOSTS_FILE = '/etc/hosts' -PDNS_REC_USER = PDNS_REC_GROUP = 'pdns' -PDNS_REC_RUN_DIR = '/run/powerdns' +PDNS_REC_USER_GROUP = 'pdns' +PDNS_REC_RUN_DIR = '/run/pdns-recursor' PDNS_REC_LUA_CONF_FILE = f'{PDNS_REC_RUN_DIR}/recursor.vyos-hostsd.conf.lua' PDNS_REC_ZONES_FILE = f'{PDNS_REC_RUN_DIR}/recursor.forward-zones.conf' @@ -436,18 +436,18 @@ def make_hosts(state): def make_pdns_rec_conf(state): logger.info(f"Writing {PDNS_REC_LUA_CONF_FILE}") - # on boot, /run/powerdns does not exist, so create it - makedir(PDNS_REC_RUN_DIR, user=PDNS_REC_USER, group=PDNS_REC_GROUP) + # on boot, /run/pdns-recursor does not exist, so create it + makedir(PDNS_REC_RUN_DIR, user=PDNS_REC_USER_GROUP, group=PDNS_REC_USER_GROUP) chmod_755(PDNS_REC_RUN_DIR) render(PDNS_REC_LUA_CONF_FILE, 'dns-forwarding/recursor.vyos-hostsd.conf.lua.j2', - state, user=PDNS_REC_USER, group=PDNS_REC_GROUP) + state, user=PDNS_REC_USER_GROUP, group=PDNS_REC_USER_GROUP) logger.info(f"Writing {PDNS_REC_ZONES_FILE}") render(PDNS_REC_ZONES_FILE, 'dns-forwarding/recursor.forward-zones.conf.j2', - state, user=PDNS_REC_USER, group=PDNS_REC_GROUP) + state, user=PDNS_REC_USER_GROUP, group=PDNS_REC_USER_GROUP) def set_host_name(state, data): if data['host_name']: |