From ced192c69e097efd6a4d924b75986d2ee649a62e Mon Sep 17 00:00:00 2001 From: 1cho1ce <1choice4u@proton.me> Date: Thu, 5 Oct 2023 15:56:59 +0000 Subject: [PATCH 1/2] Add Virtual DNS for IPv6 - Add IPv6 DNS if netvm has ipv6 feature enabled. For this there is a need to only add Virtual DNS servers to qubes that have netvm set and don't add DNS to every qube that provides network (sys-net/sys-usb/etc). - Add IPv6 Virtual DNS support. Also some IPv6 fixes and tests. - Remove feature supported-feature.ipv6 and add feature supported-feature.ipv6dns --- qubes/tests/api_admin.py | 36 ++++++++++++++++++++++++++++++++++++ qubes/tests/vm/qubesvm.py | 25 +++++++++++++++++++++++++ qubes/vm/mix/net.py | 15 ++++++++++++++- qubes/vm/qubesvm.py | 12 +++++++++++- 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/qubes/tests/api_admin.py b/qubes/tests/api_admin.py index e6f6fd8fb..6510ca797 100644 --- a/qubes/tests/api_admin.py +++ b/qubes/tests/api_admin.py @@ -243,27 +243,63 @@ def test_027_vm_property_get_all(self): self.assertEqual(value, expected) def test_028_vm_property_get_list(self): + self.vm.features['ipv6'] = '1' self.vm.provides_network = True value = self.call_mgmt_func( b'admin.vm.property.Get', b'test-vm1', b'dns') self.assertEqual(value, 'default=True type=str 10.139.1.1 10.139.1.2') + value = self.call_mgmt_func( + b'admin.vm.property.Get', + b'test-vm1', + b'dns6') + self.assertEqual(value, 'default=True type=str ') + self.vm.features['supported-feature.ipv6dns'] = '1' + value = self.call_mgmt_func( + b'admin.vm.property.Get', + b'test-vm1', + b'dns6') + self.assertEqual(value, 'default=True type=str fd09:24ef:4179::a8b:1 fd09:24ef:4179::a8b:2') def test_029_vm_property_get_list_none(self): + self.vm.features['ipv6'] = '1' value = self.call_mgmt_func( b'admin.vm.property.Get', b'test-vm1', b'dns') self.assertEqual(value, 'default=True type=str ') + value = self.call_mgmt_func( + b'admin.vm.property.Get', + b'test-vm1', + b'dns6') + self.assertEqual(value, 'default=True type=str ') + self.vm.features['supported-feature.ipv6dns'] = '1' + value = self.call_mgmt_func( + b'admin.vm.property.Get', + b'test-vm1', + b'dns6') + self.assertEqual(value, 'default=True type=str ') def test_029_vm_property_get_list_default(self): + self.vm.features['ipv6'] = '1' self.vm.provides_network = True value = self.call_mgmt_func( b'admin.vm.property.GetDefault', b'test-vm1', b'dns') self.assertEqual(value, 'type=str 10.139.1.1 10.139.1.2') + value = self.call_mgmt_func( + b'admin.vm.property.GetDefault', + b'test-vm1', + b'dns6') + self.assertEqual(value, 'type=str ') + self.vm.features['supported-feature.ipv6dns'] = '1' + value = self.call_mgmt_func( + b'admin.vm.property.GetDefault', + b'test-vm1', + b'dns6') + self.assertEqual(value, 'type=str fd09:24ef:4179::a8b:1 fd09:24ef:4179::a8b:2') def test_030_vm_property_set_vm(self): netvm = self.app.add_new_vm('AppVM', label='red', name='test-net', diff --git a/qubes/tests/vm/qubesvm.py b/qubes/tests/vm/qubesvm.py index 8bd2fb338..cf4df4133 100644 --- a/qubes/tests/vm/qubesvm.py +++ b/qubes/tests/vm/qubesvm.py @@ -1797,19 +1797,32 @@ def test_621_qdb_vm_with_network(self, mock_qubesdb, mock_urandom, vm.create_qdb_entries() self.assertEqual(test_qubesdb.data, expected) + test_qubesdb.data.clear() + with self.subTest('ipv6_dns'): + template.features['supported-feature.ipv6dns'] = '1' + netvm.features['ipv6'] = True + expected['/qubes-primary-dns6'] = 'fd09:24ef:4179::a8b:1' + expected['/qubes-secondary-dns6'] = 'fd09:24ef:4179::a8b:2' + vm.create_qdb_entries() + self.assertEqual(test_qubesdb.data, expected) + test_qubesdb.data.clear() with self.subTest('ipv6_just_appvm'): + template.features['supported-feature.ipv6dns'] = '1' del netvm.features['ipv6'] vm.features['ipv6'] = True expected['/qubes-ip6'] = \ qubes.config.qubes_ipv6_prefix.replace(':0000', '') + \ '::a89:3' del expected['/qubes-gateway6'] + del expected['/qubes-primary-dns6'] + del expected['/qubes-secondary-dns6'] vm.create_qdb_entries() self.assertEqual(test_qubesdb.data, expected) test_qubesdb.data.clear() with self.subTest('proxy_ipv4'): + del template.features['supported-feature.ipv6dns'] del vm.features['ipv6'] expected['/name'] = 'test-inst-netvm' expected['/qubes-vm-type'] = 'NetVM' @@ -1858,6 +1871,18 @@ def test_621_qdb_vm_with_network(self, mock_qubesdb, mock_urandom, netvm.create_qdb_entries() self.assertEqual(test_qubesdb.data, expected) + test_qubesdb.data.clear() + with self.subTest('proxy_ipv6_dns'): + template.features['supported-feature.ipv6dns'] = '1' + netvm.features['ipv6'] = True + expected['/qubes-netvm-primary-dns6'] = 'fd09:24ef:4179::a8b:1' + expected['/qubes-netvm-secondary-dns6'] = 'fd09:24ef:4179::a8b:2' + + with unittest.mock.patch('qubes.vm.qubesvm.QubesVM.is_running', + lambda _: True): + netvm.create_qdb_entries() + self.assertEqual(test_qubesdb.data, expected) + @unittest.mock.patch('qubes.utils.get_timezone') @unittest.mock.patch('qubes.utils.urandom') @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb') diff --git a/qubes/vm/mix/net.py b/qubes/vm/mix/net.py index 4df2d9a57..19bdb922d 100644 --- a/qubes/vm/mix/net.py +++ b/qubes/vm/mix/net.py @@ -254,6 +254,19 @@ def dns(self): return None + @qubes.stateless_property + def dns6(self): + '''IPv6 DNS servers set up for this domain.''' + if self.features.check_with_netvm('ipv6', False) and \ + self.features.check_with_template("supported-feature.ipv6dns", \ + False) and (self.netvm is not None or self.provides_network): + return StrSerializableTuple(( + 'fd09:24ef:4179::a8b:1', + 'fd09:24ef:4179::a8b:2', + )) + + return None + def __init__(self, *args, **kwargs): self._firewall = None super().__init__(*args, **kwargs) @@ -506,7 +519,7 @@ def on_property_set_ip(self, _event, name, newvalue=None, oldvalue=None): vm.fire_event( 'property-reset:visible_gateway', name='visible_gateway') - @qubes.events.handler('property-set:ip6', 'property-reset:ipv6') + @qubes.events.handler('property-set:ip6', 'property-reset:ip6') def on_property_set_ip6(self, _event, name, newvalue=None, oldvalue=None): # pylint: disable=unused-argument if newvalue == oldvalue: diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index bc0a9cd08..e072b3c9c 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -2252,7 +2252,12 @@ def create_qdb_entries(self): self.untrusted_qdb.write('/qubes-netvm-netmask', str(self.netmask)) for i, addr in zip(('primary', 'secondary'), self.dns): - self.untrusted_qdb.write('/qubes-netvm-{}-dns'.format(i), addr) + self.untrusted_qdb.write('/qubes-netvm-{}-dns'.format(i), \ + str(addr)) + if self.dns6: # pylint: disable=using-constant-test + for i, addr in zip(('primary', 'secondary'), self.dns6): + self.untrusted_qdb.write('/qubes-netvm-{}-dns6'.format(i), \ + str(addr)) if self.netvm is not None: self.untrusted_qdb.write('/qubes-mac', str(self.mac)) @@ -2270,6 +2275,11 @@ def create_qdb_entries(self): if self.visible_gateway6: # pylint: disable=using-constant-test self.untrusted_qdb.write('/qubes-gateway6', str(self.visible_gateway6)) + if self.dns6 and self.netvm.features.check_with_netvm('ipv6', \ + False): # pylint: disable=using-constant-test + for i, addr in zip(('primary', 'secondary'), self.dns6): + self.untrusted_qdb.write('/qubes-{}-dns6'.format(i), \ + str(addr)) tzname = qubes.utils.get_timezone() if tzname: From 4135aed4594e05055ff50a6bc75b1b7d082edb02 Mon Sep 17 00:00:00 2001 From: 3np <3np@example.com> Date: Mon, 7 Jul 2025 22:13:54 +0000 Subject: [PATCH 2/2] fixup test --- qubes/tests/api_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qubes/tests/api_admin.py b/qubes/tests/api_admin.py index 848aa9fbd..803c948be 100644 --- a/qubes/tests/api_admin.py +++ b/qubes/tests/api_admin.py @@ -379,12 +379,12 @@ def test_029_vm_property_get_list_default(self): value = self.call_mgmt_func( b"admin.vm.property.Get", b"test-vm1", b"dns6" ) - self.assertEqual(value, "type=str ") + self.assertEqual(value, "default=True type=str ") self.vm.features["supported-feature.ipv6dns"] = "1" value = self.call_mgmt_func( b"admin.vm.property.Get", b"test-vm1", b"dns6" ) - self.assertEqual(value, "type=str fd09:24ef:4179::a8b:1 fd09:24ef:4179::a8b:2") + self.assertEqual(value, "default=True type=str fd09:24ef:4179::a8b:1 fd09:24ef:4179::a8b:2") def test_030_vm_property_set_vm(self): netvm = self.app.add_new_vm(