From e64ab9ec34ce8cb221f3c82787d3641efa30aac0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 13:12:16 +0200 Subject: smoketest: T2903: test 802.1ad (Q-in-Q) ethertype --- smoketest/scripts/cli/base_interfaces_test.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 14ec7e137..047c19dd0 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -14,12 +14,15 @@ import os import unittest +import json from netifaces import ifaddresses, AF_INET, AF_INET6 from vyos.configsession import ConfigSession from vyos.ifconfig import Interface from vyos.util import read_file +from vyos.util import cmd +from vyos.util import vyos_dict_search from vyos.validate import is_intf_addr_assigned, is_ipv6_link_local class BasicInterfaceTest: @@ -212,8 +215,12 @@ class BasicInterfaceTest: self.session.set(base + ['address', address]) self.session.commit() + for interface in self._interfaces: for vif_s in self._qinq_range: + tmp = json.loads(cmd(f'ip -d -j link show dev {interface}.{vif_s}'))[0] + self.assertEqual(vyos_dict_search('linkinfo.info_data.protocol', tmp), '802.1ad') + for vif_c in self._vlan_range: vif = f'{interface}.{vif_s}.{vif_c}' for address in self._test_addr: -- cgit v1.2.3 From 993f6873c02f3f79013acedfe61ce705bdb3a4d0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 13:53:55 +0200 Subject: wwan: ifconfig: T2905: sync CLI nodes in dialup interfaces Both PPPoE and WWAN interfaces are dialer interfaces handled by ppp, but use different CLI nodes for the same functionality. PPPoE has "connect-on-demand" to initiate an "on-demand" dialing and WWAN uses "ondemand" for this purpose. Rename WWAN "ondemand" node to "connect-on-demand". --- data/templates/wwan/peer.tmpl | 2 +- .../include/interface-dial-on-demand.xml.i | 6 ++++++ interface-definitions/interfaces-pppoe.xml.in | 7 +------ interface-definitions/interfaces-wirelessmodem.xml.in | 7 +------ smoketest/scripts/cli/test_interfaces_wirelessmodem.py | 2 +- src/migration-scripts/interfaces/12-to-13 | 14 ++++++++++++++ 6 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 interface-definitions/include/interface-dial-on-demand.xml.i (limited to 'smoketest/scripts') diff --git a/data/templates/wwan/peer.tmpl b/data/templates/wwan/peer.tmpl index aa759f741..e23881bf8 100644 --- a/data/templates/wwan/peer.tmpl +++ b/data/templates/wwan/peer.tmpl @@ -21,7 +21,7 @@ noauth crtscts lock persist -{{ "demand" if ondemand is defined }} +{{ "demand" if connect_on_demand is defined }} connect '/usr/sbin/chat -v -t6 -f /etc/ppp/peers/chat.{{ ifname }}' diff --git a/interface-definitions/include/interface-dial-on-demand.xml.i b/interface-definitions/include/interface-dial-on-demand.xml.i new file mode 100644 index 000000000..c14ddf6f5 --- /dev/null +++ b/interface-definitions/include/interface-dial-on-demand.xml.i @@ -0,0 +1,6 @@ + + + Establishment connection automatically when traffic is sent + + + diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 8a6c61312..b6208e0b9 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -42,12 +42,7 @@ - - - Automatic establishment of PPPOE connection when traffic is sent - - - + #include Default route insertion behaviour (default: auto) diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in index d375b808d..96604ff00 100644 --- a/interface-definitions/interfaces-wirelessmodem.xml.in +++ b/interface-definitions/interfaces-wirelessmodem.xml.in @@ -80,12 +80,7 @@ - - - Only dial when traffic is available - - - + #include diff --git a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py index 40cd03b93..efc9c0e98 100755 --- a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py +++ b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py @@ -43,7 +43,7 @@ class WWANInterfaceTest(unittest.TestCase): def test_wlm_1(self): for interface in self._interfaces: self.session.set(base_path + [interface, 'no-peer-dns']) - self.session.set(base_path + [interface, 'ondemand']) + self.session.set(base_path + [interface, 'connect-on-demand']) # check validate() - APN must be configure with self.assertRaises(ConfigSessionError): diff --git a/src/migration-scripts/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13 index 17d1d0b0a..f866ca9a6 100755 --- a/src/migration-scripts/interfaces/12-to-13 +++ b/src/migration-scripts/interfaces/12-to-13 @@ -17,6 +17,8 @@ # - T2903: Change vif-s ethertype from numeric number to literal # - 0x88a8 -> 802.1ad # - 0x8100 -> 802.1q +# - T2905: Change WWAN "ondemand" node to "connect-on-demand" to have identical +# CLI nodes for both types of dialer interfaces from sys import exit, argv from vyos.configtree import ConfigTree @@ -32,6 +34,9 @@ if __name__ == '__main__': config = ConfigTree(config_file) + # + # T2903 + # for type in config.list_nodes(['interfaces']): for interface in config.list_nodes(['interfaces', type]): if not config.exists(['interfaces', type, interface, 'vif-s']): @@ -48,6 +53,15 @@ if __name__ == '__main__': config.set(base_path + ['protocol'], value=protocol) config.delete(base_path + ['ethertype']) + # + # T2905 + # + wwan_base = ['interfaces', 'wirelessmodem'] + if config.exists(wwan_base): + for interface in config.list_nodes(wwan_base): + if config.exists(wwan_base + [interface, 'ondemand']): + config.rename(wwan_base + [interface, 'ondemand'], 'connect-on-demand') + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 7ea3802aa3de99ec78fbe0cf24a8527b80c927db Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 17:30:27 +0200 Subject: smoketest: macsec: T2023: test MTU setting --- data/templates/macsec/wpa_supplicant.conf.tmpl | 2 +- smoketest/scripts/cli/test_interfaces_macsec.py | 39 +++++++++++++++---------- 2 files changed, 25 insertions(+), 16 deletions(-) (limited to 'smoketest/scripts') diff --git a/data/templates/macsec/wpa_supplicant.conf.tmpl b/data/templates/macsec/wpa_supplicant.conf.tmpl index 1731bf160..5b353def8 100644 --- a/data/templates/macsec/wpa_supplicant.conf.tmpl +++ b/data/templates/macsec/wpa_supplicant.conf.tmpl @@ -1,4 +1,4 @@ -# autogenerated by interfaces-macsec.py +### Autogenerated by interfaces-macsec.py ### # see full documentation: # https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index 0f1b6486d..30b040b97 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -23,8 +23,8 @@ from base_interfaces_test import BasicInterfaceTest from vyos.configsession import ConfigSessionError from vyos.util import read_file -def get_config_value(intf, key): - tmp = read_file(f'/run/wpa_supplicant/{intf}.conf') +def get_config_value(interface, key): + tmp = read_file(f'/run/wpa_supplicant/{interface}.conf') tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp) return tmp[0] @@ -49,52 +49,61 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): mode - both using different mandatory settings, lets test encryption as the basic authentication test has been performed using the base class tests """ - intf = 'macsec0' - src_intf = 'eth0' + interface = 'macsec0' + src_interface = 'eth0' mak_cak = '232e44b7fda6f8e2d88a07bf78a7aff4' mak_ckn = '40916f4b23e3d548ad27eedd2d10c6f98c2d21684699647d63d41b500dfe8836' replay_window = '64' - self.session.set(self._base_path + [intf, 'security', 'encrypt']) + self.session.set(self._base_path + [interface, 'security', 'encrypt']) # check validate() - Cipher suite must be set for MACsec with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'security', 'cipher', 'gcm-aes-128']) + self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) # check validate() - Physical source interface must be set for MACsec with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'source-interface', src_intf]) + self.session.set(self._base_path + [interface, 'source-interface', src_interface]) + + # check validate() - Physical source interface MTU must be higher then our MTU + self.session.set(self._base_path + [interface, 'mtu', '1500']) + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(self._base_path + [interface, 'mtu']) # check validate() - MACsec security keys mandartory when encryption is enabled with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'security', 'mka', 'cak', mak_cak]) + self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) # check validate() - MACsec security keys mandartory when encryption is enabled with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'security', 'mka', 'ckn', mak_ckn]) + self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) - self.session.set(self._base_path + [intf, 'security', 'replay-window', replay_window]) + self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) self.session.commit() - tmp = get_config_value(src_intf, 'macsec_integ_only') + tmp = get_config_value(src_interface, 'macsec_integ_only') self.assertTrue("0" in tmp) - tmp = get_config_value(src_intf, 'mka_cak') + tmp = get_config_value(src_interface, 'mka_cak') self.assertTrue(mak_cak in tmp) - tmp = get_config_value(src_intf, 'mka_ckn') + tmp = get_config_value(src_interface, 'mka_ckn') self.assertTrue(mak_ckn in tmp) # check that the default priority of 255 is programmed - tmp = get_config_value(src_intf, 'mka_priority') + tmp = get_config_value(src_interface, 'mka_priority') self.assertTrue("255" in tmp) - tmp = get_config_value(src_intf, 'macsec_replay_window') + tmp = get_config_value(src_interface, 'macsec_replay_window') self.assertTrue(replay_window in tmp) + tmp = read_file(f'/sys/class/net/{interface}/mtu') + self.assertEqual(tmp, '1460') + # Check for running process self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) -- cgit v1.2.3 From 4ce3720109cdd6648e5251da98bf11902c443e2e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 17:30:53 +0200 Subject: smoketest: kernel: check for mandatory bond/lacp and bridge options --- smoketest/scripts/system/test_kernel_options.py | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 smoketest/scripts/system/test_kernel_options.py (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py new file mode 100755 index 000000000..6316521f6 --- /dev/null +++ b/smoketest/scripts/system/test_kernel_options.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 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 . + +import re +import platform +import unittest + +from vyos.util import read_file + +kernel = platform.release() +config = read_file(f'/boot/config-{kernel}') + +class TestKernelModules(unittest.TestCase): + """ VyOS makes use of a lot of Kernel drivers, modules and features. The + required modules which are essential for VyOS should be tested that they are + available in the Kernel that is run. """ + + def test_bond_interface(self): + """ The bond/lacp interface must be enabled in the OS Kernel """ + for option in ['CONFIG_BONDING']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + + def test_bridge_interface(self): + """ The bridge interface must be enabled in the OS Kernel """ + for option in ['CONFIG_BRIDGE', + 'CONFIG_BRIDGE_IGMP_SNOOPING', + 'CONFIG_BRIDGE_VLAN_FILTERING']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + +if __name__ == '__main__': + unittest.main() + -- cgit v1.2.3 From feb491584d84636883000634bd4bba9b014b2c83 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 22 Sep 2020 18:43:34 +0200 Subject: smoketest: macsec: T2023: check that source-interface is not used by any other interface --- smoketest/scripts/cli/test_interfaces_macsec.py | 137 ++++++++++++++++-------- 1 file changed, 91 insertions(+), 46 deletions(-) (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index 30b040b97..fab5433de 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -16,10 +16,12 @@ import re import unittest + from psutil import process_iter +from base_interfaces_test import BasicInterfaceTest +from netifaces import interfaces from vyos.ifconfig import Section -from base_interfaces_test import BasicInterfaceTest from vyos.configsession import ConfigSessionError from vyos.util import read_file @@ -32,80 +34,123 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): def setUp(self): super().setUp() self._base_path = ['interfaces', 'macsec'] - self._options = { - 'macsec0': ['source-interface eth0', - 'security cipher gcm-aes-128'] - } + self._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] } # if we have a physical eth1 interface, add a second macsec instance if 'eth1' in Section.interfaces("ethernet"): - macsec = { 'macsec1': ['source-interface eth1', 'security cipher gcm-aes-128'] } + macsec = { 'macsec1': [f'source-interface eth1', 'security cipher gcm-aes-128'] } self._options.update(macsec) self._interfaces = list(self._options) def test_encryption(self): - """ MACsec can be operating in authentication and encryption - mode - both using different mandatory settings, lets test - encryption as the basic authentication test has been performed - using the base class tests """ - interface = 'macsec0' - src_interface = 'eth0' + """ MACsec can be operating in authentication and encryption mode - both + using different mandatory settings, lets test encryption as the basic + authentication test has been performed using the base class tests """ + mak_cak = '232e44b7fda6f8e2d88a07bf78a7aff4' mak_ckn = '40916f4b23e3d548ad27eedd2d10c6f98c2d21684699647d63d41b500dfe8836' replay_window = '64' - self.session.set(self._base_path + [interface, 'security', 'encrypt']) - # check validate() - Cipher suite must be set for MACsec - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) + for interface, option_value in self._options.items(): + for option in option_value: + if option.split()[0] == 'source-interface': + src_interface = option.split()[1] - # check validate() - Physical source interface must be set for MACsec - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'source-interface', src_interface]) + self.session.set(self._base_path + [interface] + option.split()) - # check validate() - Physical source interface MTU must be higher then our MTU - self.session.set(self._base_path + [interface, 'mtu', '1500']) - with self.assertRaises(ConfigSessionError): + # Encrypt link + self.session.set(self._base_path + [interface, 'security', 'encrypt']) + + # check validate() - Physical source interface MTU must be higher then our MTU + self.session.set(self._base_path + [interface, 'mtu', '1500']) + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(self._base_path + [interface, 'mtu']) + + # check validate() - MACsec security keys mandartory when encryption is enabled + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) + + # check validate() - MACsec security keys mandartory when encryption is enabled + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) + + self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) + + # final commit of settings self.session.commit() - self.session.delete(self._base_path + [interface, 'mtu']) - # check validate() - MACsec security keys mandartory when encryption is enabled + tmp = get_config_value(src_interface, 'macsec_integ_only') + self.assertTrue("0" in tmp) + + tmp = get_config_value(src_interface, 'mka_cak') + self.assertTrue(mak_cak in tmp) + + tmp = get_config_value(src_interface, 'mka_ckn') + self.assertTrue(mak_ckn in tmp) + + # check that the default priority of 255 is programmed + tmp = get_config_value(src_interface, 'mka_priority') + self.assertTrue("255" in tmp) + + tmp = get_config_value(src_interface, 'macsec_replay_window') + self.assertTrue(replay_window in tmp) + + tmp = read_file(f'/sys/class/net/{interface}/mtu') + self.assertEqual(tmp, '1460') + + # Check for running process + self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) + + def test_mandatory_toptions(self): + interface = 'macsec1' + self.session.set(self._base_path + [interface]) + + # check validate() - source interface is mandatory with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) + self.session.set(self._base_path + [interface, 'source-interface', 'eth0']) - # check validate() - MACsec security keys mandartory when encryption is enabled + # check validate() - cipher is mandatory with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) + self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) - self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) + # final commit and verify self.session.commit() + self.assertIn(interface, interfaces()) - tmp = get_config_value(src_interface, 'macsec_integ_only') - self.assertTrue("0" in tmp) + def test_source_interface(self): + """ Ensure source-interface can bot be part of any other bond or bridge """ - tmp = get_config_value(src_interface, 'mka_cak') - self.assertTrue(mak_cak in tmp) + base_bridge = ['interfaces', 'bridge', 'br200'] + base_bond = ['interfaces', 'bonding', 'bond200'] - tmp = get_config_value(src_interface, 'mka_ckn') - self.assertTrue(mak_ckn in tmp) + for interface, option_value in self._options.items(): + for option in option_value: + self.session.set(self._base_path + [interface] + option.split()) + if option.split()[0] == 'source-interface': + src_interface = option.split()[1] - # check that the default priority of 255 is programmed - tmp = get_config_value(src_interface, 'mka_priority') - self.assertTrue("255" in tmp) + self.session.set(base_bridge + ['member', 'interface', src_interface]) + # check validate() - Source interface must not already be a member of a bridge + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(base_bridge) - tmp = get_config_value(src_interface, 'macsec_replay_window') - self.assertTrue(replay_window in tmp) + self.session.set(base_bond + ['member', 'interface', src_interface]) + # check validate() - Source interface must not already be a member of a bridge + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(base_bond) - tmp = read_file(f'/sys/class/net/{interface}/mtu') - self.assertEqual(tmp, '1460') - - # Check for running process - self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) + # final commit and verify + self.session.commit() + self.assertIn(interface, interfaces()) if __name__ == '__main__': unittest.main() + -- cgit v1.2.3 From 4db00f1cd820f4fc462ce3537d692694224e02a4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 18:23:08 +0200 Subject: smoketest: dns: forwarding: T2921: add initial testcases --- .../scripts/cli/test_service_dns_forwarding.py | 163 +++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100755 smoketest/scripts/cli/test_service_dns_forwarding.py (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py new file mode 100755 index 000000000..0ae27a4d4 --- /dev/null +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-2020 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 . + +import re +import os +import unittest + +from psutil import process_iter + +from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.util import read_file +from vyos.util import process_named_running + +CONFIG_FILE = '/run/powerdns/recursor.conf' +FORWARD_FILE = '/run/powerdns/recursor.forward-zones.conf' +PROCESS_NAME= 'pdns-r/worker' + +base_path = ['service', 'dns', 'forwarding'] + +allow_from = ['192.0.2.0/24', '2001:db8::/32'] +listen_adress = ['127.0.0.1', '::1'] + +def get_config_value(key, file=CONFIG_FILE): + tmp = read_file(file) + tmp = re.findall(r'\n{}=+(.*)'.format(key), tmp) + return tmp[0] + +class TestServicePowerDNS(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + # Delete DNS forwarding configuration + self.session.delete(base_path) + self.session.commit() + del self.session + + def test_basic_forwarding(self): + """ Check basic DNS forwarding settings """ + cache_size = '20' + negative_ttl = '120' + + self.session.set(base_path + ['cache-size', cache_size]) + self.session.set(base_path + ['negative-ttl', negative_ttl]) + + # check validate() - allow from must be defined + with self.assertRaises(ConfigSessionError): + self.session.commit() + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + + # check validate() - listen-address must be defined + with self.assertRaises(ConfigSessionError): + self.session.commit() + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + # configure DNSSEC + self.session.set(base_path + ['dnssec', 'validate']) + + # commit changes + self.session.commit() + + # Check configured cache-size + tmp = get_config_value('max-cache-entries') + self.assertEqual(tmp, cache_size) + + # Networks allowed to query this server + tmp = get_config_value('allow-from') + self.assertEqual(tmp, ','.join(allow_from)) + + # Addresses to listen for DNS queries + tmp = get_config_value('local-address') + self.assertEqual(tmp, ','.join(listen_adress)) + + # Maximum amount of time negative entries are cached + tmp = get_config_value('max-negative-ttl') + self.assertEqual(tmp, negative_ttl) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_dnssec(self): + """ DNSSEC option testing """ + + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + options = ['off', 'process-no-validate', 'process', 'log-fail', 'validate'] + for option in options: + self.session.set(base_path + ['dnssec', option]) + + # commit changes + self.session.commit() + + tmp = get_config_value('dnssec') + self.assertEqual(tmp, option) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_external_nameserver(self): + """ Externe Domain Name Servers (DNS) addresses """ + + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + nameservers = ['192.0.2.1', '192.0.2.2'] + for nameserver in nameservers: + self.session.set(base_path + ['name-server', nameserver]) + + # commit changes + self.session.commit() + + tmp = get_config_value(r'\+.', file=FORWARD_FILE) + self.assertEqual(tmp, ', '.join(nameservers)) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_domain_forwarding(self): + """ Externe Domain Name Servers (DNS) addresses """ + + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + domains = ['vyos.io', 'vyos.net'] + nameservers = ['192.0.2.1', '192.0.2.2'] + for domain in domains: + for nameserver in nameservers: + self.session.set(base_path + ['domain', domain, 'server', nameserver]) + + # commit changes + self.session.commit() + + for domain in domains: + tmp = get_config_value(domain, file=FORWARD_FILE) + self.assertEqual(tmp, ', '.join(nameservers)) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3 From 58ead7415a3fe8d786bdb6fd2a99d0a57770dbd7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 19:54:15 +0200 Subject: smoketest: (re-)use process_named_running() from vyos.util --- smoketest/scripts/cli/test_interfaces_macsec.py | 6 +++--- smoketest/scripts/cli/test_interfaces_wireless.py | 10 ++++------ smoketest/scripts/cli/test_service_dns_dynamic.py | 21 +++++---------------- .../scripts/cli/test_service_dns_forwarding.py | 2 -- smoketest/scripts/cli/test_service_mdns-repeater.py | 4 ++-- smoketest/scripts/cli/test_service_pppoe-server.py | 8 ++++---- smoketest/scripts/cli/test_service_router-advert.py | 6 ++---- smoketest/scripts/cli/test_service_snmp.py | 14 ++++++++------ smoketest/scripts/cli/test_service_ssh.py | 15 +++++++-------- smoketest/scripts/cli/test_system_lcd.py | 9 +++++---- smoketest/scripts/cli/test_system_ntp.py | 13 ++++++++----- smoketest/scripts/cli/test_vpn_openconnect.py | 8 +++----- 12 files changed, 51 insertions(+), 65 deletions(-) (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index fab5433de..6d1be86ba 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -17,13 +17,13 @@ import re import unittest -from psutil import process_iter from base_interfaces_test import BasicInterfaceTest from netifaces import interfaces -from vyos.ifconfig import Section from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section from vyos.util import read_file +from vyos.util import process_named_running def get_config_value(interface, key): tmp = read_file(f'/run/wpa_supplicant/{interface}.conf') @@ -103,7 +103,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): self.assertEqual(tmp, '1460') # Check for running process - self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('wpa_supplicant')) def test_mandatory_toptions(self): interface = 'macsec1' diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py index 691f633b7..0e93b6432 100755 --- a/smoketest/scripts/cli/test_interfaces_wireless.py +++ b/smoketest/scripts/cli/test_interfaces_wireless.py @@ -19,8 +19,7 @@ import re import unittest from base_interfaces_test import BasicInterfaceTest -from psutil import process_iter - +from vyos.util import process_named_running from vyos.util import check_kmod from vyos.util import read_file @@ -54,10 +53,10 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): for option, option_value in self._options.items(): if 'type access-point' in option_value: # Check for running process - self.assertIn('hostapd', (p.name() for p in process_iter())) + self.assertTrue(process_named_running('hostapd')) elif 'type station' in option_value: # Check for running process - self.assertIn('wpa_supplicant', (p.name() for p in process_iter())) + self.assertTrue(process_named_running('wpa_supplicant')) else: self.assertTrue(False) @@ -137,8 +136,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): self.assertIn(value, tmp) # Check for running process - self.assertIn('hostapd', (p.name() for p in process_iter())) - + self.assertTrue(process_named_running('hostapd')) if __name__ == '__main__': check_kmod('mac80211_hwsim') diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index be52360ed..51fa38912 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -19,10 +19,11 @@ import os import unittest from getpass import getuser -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file +PROCESS_NAME = 'ddclient' DDCLIENT_CONF = '/run/ddclient/ddclient.conf' base_path = ['service', 'dns', 'dynamic'] @@ -32,17 +33,6 @@ def get_config_value(key): tmp = tmp[0].rstrip(',') return tmp -def check_process(): - """ - Check for running process, process name changes dynamically e.g. - "ddclient - sleeping for 270 seconds", thus we need a different approach - """ - running = False - for p in process_iter(): - if "ddclient" in p.name(): - running = True - return running - class TestServiceDDNS(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -104,8 +94,7 @@ class TestServiceDDNS(unittest.TestCase): self.assertTrue(pwd == "'" + password + "'") # Check for running process - self.assertTrue(check_process()) - + self.assertTrue(process_named_running(PROCESS_NAME)) def test_rfc2136(self): """ Check if DDNS service can be configured and runs """ @@ -135,7 +124,7 @@ class TestServiceDDNS(unittest.TestCase): # TODO: inspect generated configuration file # Check for running process - self.assertTrue(check_process()) + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index 0ae27a4d4..717b5b56d 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -18,8 +18,6 @@ import re import os import unittest -from psutil import process_iter - from vyos.configsession import ConfigSession, ConfigSessionError from vyos.util import read_file from vyos.util import process_named_running diff --git a/smoketest/scripts/cli/test_service_mdns-repeater.py b/smoketest/scripts/cli/test_service_mdns-repeater.py index 18900b6d2..de73b9914 100755 --- a/smoketest/scripts/cli/test_service_mdns-repeater.py +++ b/smoketest/scripts/cli/test_service_mdns-repeater.py @@ -17,8 +17,8 @@ import os import unittest -from psutil import process_iter from vyos.configsession import ConfigSession +from vyos.util import process_named_running base_path = ['service', 'mdns', 'repeater'] intf_base = ['interfaces', 'dummy'] @@ -45,7 +45,7 @@ class TestServiceMDNSrepeater(unittest.TestCase): self.session.commit() # Check for running process - self.assertTrue("mdns-repeater" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('mdns-repeater')) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 901ca792d..3a6b12ef4 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -14,15 +14,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re import os import unittest from configparser import ConfigParser -from psutil import process_iter from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError +from vyos.util import process_named_running +process_name = 'accel-pppd' base_path = ['service', 'pppoe-server'] local_if = ['interfaces', 'dummy', 'dum667'] pppoe_conf = '/run/accel-pppd/pppoe.conf' @@ -116,7 +116,7 @@ class TestServicePPPoEServer(unittest.TestCase): self.assertEqual(conf['connlimit']['limit'], '20/min') # Check for running process - self.assertTrue('accel-pppd' in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(process_name)) def test_radius_auth(self): """ Test configuration of RADIUS authentication for PPPoE server """ @@ -161,7 +161,7 @@ class TestServicePPPoEServer(unittest.TestCase): self.assertFalse(conf['ppp'].getboolean('ccp')) # Check for running process - self.assertTrue('accel-pppd' in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(process_name)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index ec2110c8a..238f59e6d 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -18,9 +18,9 @@ import re import os import unittest -from psutil import process_iter from vyos.configsession import ConfigSession from vyos.util import read_file +from vyos.util import process_named_running RADVD_CONF = '/run/radvd/radvd.conf' @@ -90,10 +90,8 @@ class TestServiceRADVD(unittest.TestCase): tmp = get_config_value('AdvOnLink') self.assertEqual(tmp, 'off') - - # Check for running process - self.assertTrue('radvd' in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('radvd')) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py index fb5f5393f..067a3c76b 100755 --- a/smoketest/scripts/cli/test_service_snmp.py +++ b/smoketest/scripts/cli/test_service_snmp.py @@ -19,12 +19,15 @@ import re import unittest from vyos.validate import is_ipv4 -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file +from vyos.util import process_named_running +PROCESS_NAME = 'snmpd' SNMPD_CONF = '/etc/snmp/snmpd.conf' + base_path = ['service', 'snmp'] def get_config_value(key): @@ -78,7 +81,7 @@ class TestSNMPService(unittest.TestCase): self.assertTrue(expected in config) # Check for running process - self.assertTrue("snmpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_snmpv3_sha(self): @@ -113,7 +116,7 @@ class TestSNMPService(unittest.TestCase): # TODO: read in config file and check values # Check for running process - self.assertTrue("snmpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_snmpv3_md5(self): """ Check if SNMPv3 can be configured with MD5 authentication and service runs""" @@ -147,8 +150,7 @@ class TestSNMPService(unittest.TestCase): # TODO: read in config file and check values # Check for running process - self.assertTrue("snmpd" in (p.name() for p in process_iter())) - + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index 79850fe44..0cd00ccce 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -18,10 +18,12 @@ import re import os import unittest -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file +from vyos.util import process_named_running +PROCESS_NAME = 'sshd' SSHD_CONF = '/run/ssh/sshd_config' base_path = ['service', 'ssh'] @@ -30,9 +32,6 @@ def get_config_value(key): tmp = re.findall(f'\n?{key}\s+(.*)', tmp) return tmp -def is_service_running(): - return 'sshd' in (p.name() for p in process_iter()) - class TestServiceSSH(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -62,7 +61,7 @@ class TestServiceSSH(unittest.TestCase): self.assertEqual('22', port) # Check for running process - self.assertTrue(is_service_running()) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_ssh_single(self): """ Check if SSH service can be configured and runs """ @@ -101,7 +100,7 @@ class TestServiceSSH(unittest.TestCase): self.assertTrue("100" in keepalive) # Check for running process - self.assertTrue(is_service_running()) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_ssh_multi(self): """ Check if SSH service can be configured and runs with multiple @@ -128,7 +127,7 @@ class TestServiceSSH(unittest.TestCase): self.assertIn(address, tmp) # Check for running process - self.assertTrue(is_service_running()) + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_system_lcd.py b/smoketest/scripts/cli/test_system_lcd.py index 931a91c53..9385799b0 100755 --- a/smoketest/scripts/cli/test_system_lcd.py +++ b/smoketest/scripts/cli/test_system_lcd.py @@ -18,9 +18,10 @@ import os import unittest from configparser import ConfigParser -from psutil import process_iter from vyos.configsession import ConfigSession +from vyos.util import process_named_running +config_file = '/run/LCDd/LCDd.conf' base_path = ['system', 'lcd'] class TestSystemLCD(unittest.TestCase): @@ -42,13 +43,13 @@ class TestSystemLCD(unittest.TestCase): # load up ini-styled LCDd.conf conf = ConfigParser() - conf.read('/run/LCDd/LCDd.conf') + conf.read(config_file) self.assertEqual(conf['CFontzPacket']['Model'], '533') self.assertEqual(conf['CFontzPacket']['Device'], '/dev/ttyS1') - # both processes running - self.assertTrue('LCDd' in (p.name() for p in process_iter())) + # Check for running process + self.assertTrue(process_named_running('LCDd')) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py index 856a28916..2a7c64870 100755 --- a/smoketest/scripts/cli/test_system_ntp.py +++ b/smoketest/scripts/cli/test_system_ntp.py @@ -18,11 +18,14 @@ import re import os import unittest -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError -from vyos.template import vyos_address_from_cidr, vyos_netmask_from_cidr +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.template import vyos_address_from_cidr +from vyos.template import vyos_netmask_from_cidr from vyos.util import read_file +from vyos.util import process_named_running +PROCESS_NAME = 'ntpd' NTP_CONF = '/etc/ntp.conf' base_path = ['system', 'ntp'] @@ -63,7 +66,7 @@ class TestSystemNTP(unittest.TestCase): self.assertTrue(test in tmp) # Check for running process - self.assertTrue("ntpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_ntp_clients(self): """ Test the allowed-networks statement """ @@ -102,7 +105,7 @@ class TestSystemNTP(unittest.TestCase): self.assertEqual(tmp, test) # Check for running process - self.assertTrue("ntpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py index d2b82d686..2ba6aabf9 100755 --- a/smoketest/scripts/cli/test_vpn_openconnect.py +++ b/smoketest/scripts/cli/test_vpn_openconnect.py @@ -14,13 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re import os import unittest -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError -from vyos.util import read_file +from vyos.configsession import ConfigSession +from vyos.util import process_named_running OCSERV_CONF = '/run/ocserv/ocserv.conf' base_path = ['vpn', 'openconnect'] @@ -52,7 +50,7 @@ class TestVpnOpenconnect(unittest.TestCase): self.session.commit() # Check for running process - self.assertTrue("ocserv-main" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('ocserv-main')) if __name__ == '__main__': unittest.main() -- cgit v1.2.3 From 44698fb03cf2017e56adbd161c66be585822b87a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 20:35:25 +0200 Subject: smoketest: dns: dynamic: add missing import statement Commit 58ead741 ("smoketest: (re-)use process_named_running() from vyos.util") missed an import statement for process_named_running(). This has been fixed. --- smoketest/scripts/cli/test_service_dns_dynamic.py | 1 + 1 file changed, 1 insertion(+) (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index 51fa38912..c7ac87135 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -22,6 +22,7 @@ from getpass import getuser from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import read_file +from vyos.util import process_named_running PROCESS_NAME = 'ddclient' DDCLIENT_CONF = '/run/ddclient/ddclient.conf' -- cgit v1.2.3 From f39f5dde342aa5e14d1fb4155920c61ac5fd11b1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 19:21:36 +0200 Subject: dns: forwarding: T2921: migrate to get_config_dict() --- data/configd-include.json | 1 + data/templates/dns-forwarding/recursor.conf.tmpl | 2 +- .../recursor.forward-zones.conf.tmpl | 4 +- .../recursor.vyos-hostsd.conf.lua.tmpl | 4 +- interface-definitions/dns-forwarding.xml.in | 11 +- .../scripts/cli/test_service_dns_forwarding.py | 35 ++++- src/conf_mode/dns_forwarding.py | 172 ++++++++------------- src/services/vyos-hostsd | 21 +-- src/utils/vyos-hostsd-client | 6 +- 9 files changed, 122 insertions(+), 134 deletions(-) (limited to 'smoketest/scripts') diff --git a/data/configd-include.json b/data/configd-include.json index 0c75657e0..2711a29b8 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -2,6 +2,7 @@ "bcast_relay.py", "dhcp_relay.py", "dhcpv6_relay.py", +"dns_forwarding.py", "dynamic_dns.py", "firewall_options.py", "host_name.py", diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl index d233b8abc..b0ae3cc61 100644 --- a/data/templates/dns-forwarding/recursor.conf.tmpl +++ b/data/templates/dns-forwarding/recursor.conf.tmpl @@ -21,7 +21,7 @@ max-cache-entries={{ cache_size }} max-negative-ttl={{ negative_ttl }} # ignore-hosts-file -export-etc-hosts={{ export_hosts_file }} +export-etc-hosts={{ 'no' if ignore_hosts_file is defined else 'yes' }} # listen-address local-address={{ listen_address | join(',') }} diff --git a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl index e62b9bb81..90f35ae1c 100644 --- a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl +++ b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl @@ -19,10 +19,10 @@ +.={{ n.dot_zone_ns }} {% endif %} -{% if forward_zones %} +{% if forward_zones is defined %} # zones added via 'service dns forwarding domain' {% for zone, zonedata in forward_zones.items() %} -{% if zonedata['recursion-desired'] %}+{% endif %}{{ zone }}={{ zonedata['nslist']|join(', ') }} +{{ "+" if zonedata['recursion_desired'] is defined }}{{ zone }}={{ zonedata['server']|join(', ') }} {% endfor %} {% endif %} diff --git a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl index 8fefae0b2..784d5c360 100644 --- a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl +++ b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl @@ -13,10 +13,10 @@ addNTA("{{ a }}.", "{{ tag }} alias") {% endfor %} {% endif %} -{% if forward_zones %} +{% if forward_zones is defined %} -- from 'service dns forwarding domain' {% for zone, zonedata in forward_zones.items() %} -{% if zonedata['addNTA'] %} +{% if zonedata['addnta'] is defined %} addNTA("{{ zone }}", "static") {% endif %} {% endfor %} diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index aaf8bb27d..07e63d54a 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -16,7 +16,7 @@ - DNS forwarding cache size + DNS forwarding cache size (default: 10000) 0-10000 DNS forwarding cache size @@ -25,6 +25,7 @@ + 10000 @@ -37,7 +38,7 @@ - DNSSEC mode + DNSSEC mode (default: process-no-validate) off process-no-validate process log-fail validate @@ -62,9 +63,10 @@ Full blown DNSSEC validation. Send SERVFAIL to clients on bogus responses. - (off|process-no-validate|process|log-fail|validate) + ^(off|process-no-validate|process|log-fail|validate)$ + process-no-validate @@ -146,7 +148,7 @@ - Maximum amount of time negative entries are cached + Maximum amount of time negative entries are cached (default: 3600) 0-7200 Seconds to cache NXDOMAIN entries @@ -155,6 +157,7 @@ + 3600 diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index 717b5b56d..5e2f3dfbd 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -24,6 +24,7 @@ from vyos.util 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' PROCESS_NAME= 'pdns-r/worker' base_path = ['service', 'dns', 'forwarding'] @@ -69,6 +70,9 @@ class TestServicePowerDNS(unittest.TestCase): # configure DNSSEC self.session.set(base_path + ['dnssec', 'validate']) + # Do not use local /etc/hosts file in name resolution + self.session.set(base_path + ['ignore-hosts-file']) + # commit changes self.session.commit() @@ -88,6 +92,10 @@ class TestServicePowerDNS(unittest.TestCase): tmp = get_config_value('max-negative-ttl') self.assertEqual(tmp, negative_ttl) + # Do not use local /etc/hosts file in name resolution + tmp = get_config_value('export-etc-hosts') + self.assertEqual(tmp, 'no') + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -130,6 +138,11 @@ class TestServicePowerDNS(unittest.TestCase): tmp = get_config_value(r'\+.', file=FORWARD_FILE) self.assertEqual(tmp, ', '.join(nameservers)) + # Do not use local /etc/hosts file in name resolution + # default: yes + tmp = get_config_value('export-etc-hosts') + self.assertEqual(tmp, 'yes') + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -141,21 +154,39 @@ class TestServicePowerDNS(unittest.TestCase): for address in listen_adress: self.session.set(base_path + ['listen-address', address]) - domains = ['vyos.io', 'vyos.net'] + domains = ['vyos.io', 'vyos.net', 'vyos.com'] nameservers = ['192.0.2.1', '192.0.2.2'] for domain in domains: for nameserver in nameservers: self.session.set(base_path + ['domain', domain, 'server', nameserver]) + # Test 'recursion-desired' flag for only one domain + if domain == domains[0]: + self.session.set(base_path + ['domain', domain, 'recursion-desired']) + + # Test 'negative trust anchor' flag for the second domain only + if domain == domains[1]: + self.session.set(base_path + ['domain', domain, 'addnta']) + # commit changes self.session.commit() + # Test configured name-servers + hosts_conf = read_file(HOSTSD_FILE) for domain in domains: - tmp = get_config_value(domain, file=FORWARD_FILE) + # Test 'recursion-desired' flag for the first domain only + if domain == domains[0]: key =f'\+{domain}' + else: key =f'{domain}' + tmp = get_config_value(key, file=FORWARD_FILE) self.assertEqual(tmp, ', '.join(nameservers)) + # Test 'negative trust anchor' flag for the second domain only + if domain == domains[1]: + self.assertIn(f'addNTA("{domain}", "static")', hosts_conf) + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() + diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index d6eb76d91..5101c1e79 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -17,14 +17,17 @@ import os from sys import exit -from copy import deepcopy from vyos.config import Config +from vyos.configdict import dict_merge from vyos.hostsd_client import Client as hostsd_client -from vyos import ConfigError -from vyos.util import call, chown +from vyos.util import call +from vyos.util import chown +from vyos.util import vyos_dict_search from vyos.template import render +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() @@ -35,116 +38,63 @@ pdns_rec_hostsd_lua_conf_file = f'{pdns_rec_run_dir}/recursor.vyos-hostsd.conf.l 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' -default_config_data = { - 'allow_from': [], - 'cache_size': 10000, - 'export_hosts_file': 'yes', - 'listen_address': [], - 'name_servers': [], - 'negative_ttl': 3600, - 'system': False, - 'domains': {}, - 'dnssec': 'process-no-validate', - 'dhcp_interfaces': [] -} - hostsd_tag = 'static' -def get_config(conf): - dns = deepcopy(default_config_data) +def get_config(config=None): + if config: + conf = config + else: + conf = Config() base = ['service', 'dns', 'forwarding'] - if not conf.exists(base): return None - conf.set_level(base) - - if conf.exists(['allow-from']): - dns['allow_from'] = conf.return_values(['allow-from']) - - if conf.exists(['cache-size']): - cache_size = conf.return_value(['cache-size']) - dns['cache_size'] = cache_size - - if conf.exists('negative-ttl'): - negative_ttl = conf.return_value(['negative-ttl']) - dns['negative_ttl'] = negative_ttl - - if conf.exists(['domain']): - for domain in conf.list_nodes(['domain']): - conf.set_level(base + ['domain', domain]) - entry = { - 'nslist': bracketize_ipv6_addrs(conf.return_values(['server'])), - 'addNTA': conf.exists(['addnta']), - 'recursion-desired': conf.exists(['recursion-desired']) - } - dns['domains'][domain] = entry - - conf.set_level(base) + dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + dns = dict_merge(default_values, dns) - if conf.exists(['ignore-hosts-file']): - dns['export_hosts_file'] = "no" + # some additions to the default dictionary + if 'system' in dns: + base_nameservers = ['system', 'name-server'] + if conf.exists(base_nameservers): + dns.update({'system_name_server': conf.return_values(base_nameservers)}) - if conf.exists(['name-server']): - dns['name_servers'] = bracketize_ipv6_addrs( - conf.return_values(['name-server'])) - - if conf.exists(['system']): - dns['system'] = True - - if conf.exists(['listen-address']): - dns['listen_address'] = conf.return_values(['listen-address']) - - if conf.exists(['dnssec']): - dns['dnssec'] = conf.return_value(['dnssec']) - - if conf.exists(['dhcp']): - dns['dhcp_interfaces'] = conf.return_values(['dhcp']) + base_nameservers_dhcp = ['system', 'name-servers-dhcp'] + if conf.exists(base_nameservers_dhcp): + dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)}) return dns -def bracketize_ipv6_addrs(addrs): - """Wraps each IPv6 addr in addrs in [], leaving IPv4 addrs untouched.""" - return ['[{0}]'.format(a) if a.count(':') > 1 else a for a in addrs] - -def verify(conf, dns): +def verify(dns): # bail out early - looks like removal from running config - if dns is None: + if not dns: return None - if not dns['listen_address']: - raise ConfigError( - "Error: DNS forwarding requires a listen-address") - - if not dns['allow_from']: - raise ConfigError( - "Error: DNS forwarding requires an allow-from network") - - if dns['domains']: - for domain in dns['domains']: - if not dns['domains'][domain]['nslist']: - raise ConfigError(( - f'Error: No server configured for domain {domain}')) - - no_system_nameservers = False - conf.set_level([]) - if dns['system'] and not ( - conf.exists(['system', 'name-server']) or - conf.exists(['system', 'name-servers-dhcp']) ): - no_system_nameservers = True - print(("DNS forwarding warning: No 'system name-server' or " - "'system name-servers-dhcp' set\n")) - - if (no_system_nameservers or not dns['system']) and not ( - dns['name_servers'] or dns['dhcp_interfaces']): - print(("DNS forwarding warning: No 'dhcp', 'name-server' or 'system' " - "nameservers set. Forwarding will operate as a recursor.\n")) + if 'listen_address' not in dns: + raise ConfigError('DNS forwarding requires a listen-address') + + if 'allow_from' not in dns: + raise ConfigError('DNS forwarding requires an allow-from network') + + # we can not use vyos_dict_search() when testing for domain servers + # as a domain will contains dot's which is out dictionary delimiter. + if 'domain' in dns: + for domain in dns['domain']: + if 'server' not in dns['domain'][domain]: + raise ConfigError(f'No server configured for domain {domain}!') + + if 'system' in dns: + if not ('system_name_server' in dns or 'system_name_server_dhcp' in dns): + print("Warning: No 'system name-server' or 'system " \ + "name-servers-dhcp' configured") return None def generate(dns): # bail out early - looks like removal from running config - if dns is None: + if not dns: return None render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.tmpl', @@ -154,17 +104,18 @@ def generate(dns): dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group) # if vyos-hostsd didn't create its files yet, create them (empty) - for f in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]: - with open(f, 'a'): + for file in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]: + with open(file, 'a'): pass - chown(f, user=pdns_rec_user, group=pdns_rec_group) + chown(file, user=pdns_rec_user, group=pdns_rec_group) return None def apply(dns): - if dns is None: + if not dns: # DNS forwarding is removed in the commit - call("systemctl stop pdns-recursor.service") + call('systemctl stop pdns-recursor.service') + if os.path.isfile(pdns_rec_config_file): os.unlink(pdns_rec_config_file) else: @@ -174,8 +125,8 @@ def apply(dns): # add static nameservers to hostsd so they can be joined with other # sources hc.delete_name_servers([hostsd_tag]) - if dns['name_servers']: - hc.add_name_servers({hostsd_tag: dns['name_servers']}) + if 'name_server' in dns: + hc.add_name_servers({hostsd_tag: dns['name_server']}) # delete all nameserver tags hc.delete_name_server_tags_recursor(hc.get_name_server_tags_recursor()) @@ -184,32 +135,33 @@ def apply(dns): # our own tag (static) hc.add_name_server_tags_recursor([hostsd_tag]) - if dns['system']: + if 'system' in dns: hc.add_name_server_tags_recursor(['system']) else: hc.delete_name_server_tags_recursor(['system']) # add dhcp nameserver tags for configured interfaces - for intf in dns['dhcp_interfaces']: - hc.add_name_server_tags_recursor(['dhcp-' + intf, 'dhcpv6-' + intf ]) + if 'system_name_server_dhcp' in dns: + for interface in dns['system_name_server_dhcp']: + hc.add_name_server_tags_recursor(['dhcp-' + interface, + 'dhcpv6-' + interface ]) # hostsd will generate the forward-zones file # the list and keys() are required as get returns a dict, not list hc.delete_forward_zones(list(hc.get_forward_zones().keys())) - if dns['domains']: - hc.add_forward_zones(dns['domains']) + if 'domain' in dns: + hc.add_forward_zones(dns['domain']) # call hostsd to generate forward-zones and its lua-config-file hc.apply() ### finally (re)start pdns-recursor - call("systemctl restart pdns-recursor.service") + call('systemctl restart pdns-recursor.service') if __name__ == '__main__': try: - conf = Config() - c = get_config(conf) - verify(conf, c) + c = get_config() + verify(c) generate(c) apply(c) except ConfigError as e: diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index 0079f7e5c..59dbeda17 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -107,16 +107,17 @@ # ### forward_zones ## Additional zones added to pdns-recursor forward-zones-file. -## If recursion-desired is true, '+' will be prepended to the zone line. -## If addNTA is true, a NTA will be added via lua-config-file. +## If recursion_desired is true, '+' will be prepended to the zone line. +## If addnta is true, a NTA (Negative Trust Anchor) will be added via +## lua-config-file. # # { 'type': 'forward_zones', # 'op': 'add', # 'data': { # '': { -# 'nslist': ['', ...], -# 'addNTA': , -# 'recursion-desired': +# 'server': ['', ...], +# 'addnta': , +# 'recursion_desired': # } # ... # } @@ -305,12 +306,12 @@ tag_regex_schema = op_type_schema.extend({ forward_zone_add_schema = op_type_schema.extend({ 'data': { str: { - 'nslist': [str], - 'addNTA': bool, - 'recursion-desired': bool + 'server': [str], + 'addnta': Any({}, None), + 'recursion_desired': Any({}, None), } } - }, required=True) + }, required=False) hosts_add_schema = op_type_schema.extend({ 'data': { @@ -586,7 +587,7 @@ if __name__ == '__main__': context = zmq.Context() socket = context.socket(zmq.REP) - + # Set the right permissions on the socket, then change it back o_mask = os.umask(0o007) socket.bind(SOCKET_PATH) diff --git a/src/utils/vyos-hostsd-client b/src/utils/vyos-hostsd-client index 48ebc83f7..d4d38315a 100755 --- a/src/utils/vyos-hostsd-client +++ b/src/utils/vyos-hostsd-client @@ -99,9 +99,9 @@ try: raise ValueError("--nameservers is required for this operation") client.add_forward_zones( { args.add_forward_zone: { - 'nslist': args.nameservers, - 'addNTA': args.addnta, - 'recursion-desired': args.recursion_desired + 'server': args.nameservers, + 'addnta': args.addnta, + 'recursion_desired': args.recursion_desired } }) elif args.delete_forward_zones: -- cgit v1.2.3 From dfa949c5b758e2954ed5c6ad455fe586965cd156 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 10:06:27 +0200 Subject: smoketest: platform: check for required virtio/vmxnet drivers --- smoketest/scripts/system/test_kernel_options.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'smoketest/scripts') diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py index 6316521f6..043567c4f 100755 --- a/smoketest/scripts/system/test_kernel_options.py +++ b/smoketest/scripts/system/test_kernel_options.py @@ -42,6 +42,22 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', config) self.assertTrue(tmp) + def test_qemu_support(self): + """ The bond/lacp interface must be enabled in the OS Kernel """ + for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO', + 'CONFIG_VIRTIO_NET', 'CONFIG_VIRTIO_CONSOLE', + 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI', + 'CONFIG_VIRTIO_BALLOON', 'CONFIG_CRYPTO_DEV_VIRTIO', + 'CONFIG_X86_PLATFORM_DEVICES']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + + def test_vmware_support(self): + """ The bond/lacp interface must be enabled in the OS Kernel """ + for option in ['CONFIG_VMXNET3']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + if __name__ == '__main__': unittest.main() -- cgit v1.2.3