Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNS leakage when only one DNS server is set in a NetVM #9011

Closed
Rudd-O opened this issue Mar 4, 2024 · 6 comments · Fixed by QubesOS/qubes-core-agent-linux#505
Closed

DNS leakage when only one DNS server is set in a NetVM #9011

Rudd-O opened this issue Mar 4, 2024 · 6 comments · Fixed by QubesOS/qubes-core-agent-linux#505
Labels
affects-4.2 This issue affects Qubes OS 4.2. C: Debian/Ubuntu C: networking diagnosed Technical diagnosis has been performed (see issue comments). P: major Priority: major. Between "default" and "critical" in severity. pr submitted A pull request has been submitted for this issue. T: bug Type: bug report. A problem or defect resulting in unintended behavior in something that exists.

Comments

@Rudd-O
Copy link

Rudd-O commented Mar 4, 2024

Seen in 4.2.

One of my machines has a NetVM with a physical network interface, manually set to have only one DNS server (local). In this scenario, the Qubes dnat-dns chain contains the following:

	chain dnat-dns {
		type nat hook prerouting priority dstnat; policy accept;
		ip daddr 10.139.1.1 udp dport 53 dnat to 10.250.7.2
		ip daddr 10.139.1.1 tcp dport 53 dnat to 10.250.7.2
	}

That's sensible. Only one DNS server is mapped.

However, since the configuration of the qubes attached to this NetVM in fact contains internal Qubes IPs 10.139.1.1 and 10.139.1.2, sometimes (when the local DNS server fails to respond on time) the qubes issue a DNS request to 10.139.1.2, which is in fact routed out of the VM because nothing intercepts it (the dnat-dns chain doesn't have an entry for it.

(I caught this by observing very funny incomplete NAT entries in my border router, all mentioning internal Qubes IPs.)

Ideally, the dnat-dns chain adds rules for both IPs, and if it needs to repeat the same single DNS server IP address in both pairs of rules, then it should do so.

Thanks in advance for fixing this security issue.

In the meantime, to mitigate this, I've configured my custom firewalling to drop such traffic at the border router:

border-router:
  input_rules: *global_input
  forward_rules: *global_forward
  custom_chains:
    ip:
      custom-prerouting-dstnat:
        preamble: type nat hook prerouting priority dstnat -1; policy accept;
        rules:
          nodnsleak:
          - from: intranet
            to: 10.136.0.0/14
            action: drop
            log_prefix: "Dropping internal Qubes traffic: "
@Rudd-O Rudd-O added P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. T: bug Type: bug report. A problem or defect resulting in unintended behavior in something that exists. labels Mar 4, 2024
@andrewdavidwong andrewdavidwong added needs diagnosis Requires technical diagnosis from developer. Replace with "diagnosed" or remove if otherwise closed. C: networking affects-4.2 This issue affects Qubes OS 4.2. labels Mar 4, 2024
@ben-grande
Copy link

One of my machines has a NetVM with a physical network interface, manually set to have only one DNS server (local).

Only the dnat-dns rules you showed? Note that /etc/resolv.conf has the dns servers configured by the qube preference: qvm-prefs QUBE dns.

I don't see a leak if the firewall rules are not considering there are two dns servers configured, not only one.

It is the behaviour of resolv.conf to fallback to the secondary dns server when the first one fails.

You can:

  • complete the firewall rules accouting for both qubes dns servers 10.139.1.1, 10.139.1.2.
  • qubes network hook to override the resolv.conf with your dns server 10.250.7.2, it needs to be a hook because that file is updated for every IP change and using the qvm-service disable-dns-server empties the file on every run qubes-setup-dnat-to-ns.

You can do both or either one.

From resolv.conf man page:

nameserver Name server IP address
...
If there are multiple servers, the resolver library queries them in the order listed. If no nameserver entries are present, the default is to use the name server on the local machine. (The algorithm used is to try a name server, and if the query times out, try the next, until out of name servers, then repeat trying all the name servers until a maximum number of retries are made.)

@3hhh
Copy link

3hhh commented Jun 16, 2024

I can confirm this bug.

With a single DNS server (e.g. 1.2.3.4) in a sys-net resolv.conf Qubes OS generates the following nft rules in sys-net:

	chain dnat-dns {
		type nat hook prerouting priority dstnat; policy accept;
		ip daddr 10.139.1.1 udp dport 53 dnat to 1.2.3.4
		ip daddr 10.139.1.1 tcp dport 53 dnat to 1.2.3.4
	}

Instead, it should generate:

	chain dnat-dns {
		type nat hook prerouting priority dstnat; policy accept;
		ip daddr 10.139.1.1 udp dport 53 dnat to 1.2.3.4
		ip daddr 10.139.1.1 tcp dport 53 dnat to 1.2.3.4
		ip daddr 10.139.1.2 udp dport 53 dnat to 1.2.3.4
		ip daddr 10.139.1.2 tcp dport 53 dnat to 1.2.3.4
	}

EDIT: 10.139.1.2 is obviously in place to support an optional secondary DNS server. It may make sense to re-think this idea and support an arbitrary number of DNS servers by getting rid of 10.139.1.2 and just do the DNS dispatching locally in sys-net, e.g. via systemd-resolved (also see #5225).

@3hhh
Copy link

3hhh commented Jun 17, 2024

Looks like it only affects debian, cf. https://forum.qubes-os.org/t/github-issue-9011-dns-leakage-when-only-one-dns-server-is-set-in-a-netvm/27022/13

@alimirjamali
Copy link

I am working on a patch for this. I believe @DemiMarie wrote the qubes-setup-dnat-to-ns during transition from iptabels to nftables. I have one remaining unsolved question. What is the resolve1_proxy doing in dbus solution? It is not used. Is it OK if it is deleted?

Here is the patch (so far):

diff --git a/network/qubes-setup-dnat-to-ns b/network/qubes-setup-dnat-to-ns
index 7a760a91..754dbe45 100755
--- a/network/qubes-setup-dnat-to-ns
+++ b/network/qubes-setup-dnat-to-ns
@@ -71,7 +71,10 @@ def get_dns_resolved():
     dns.sort(key=lambda x: x[0] != 0)
     # Only keep IPv4 entries. systemd-resolved is trusted to return valid
     # addresses.
-    return [IPv4Address(bytes(addr)) for _g, family, addr in dns if family == 2]
+    ''' We only need abridged IPv4 DNS entries for ifindex == 0 (Interface 0).
+    Not disconnected or unused network interfaces '''
+    return [IPv4Address(bytes(addr)) for ifindex, family, addr in dns 
+            if family == 2 and ifindex == 0]
 
 def install_firewall_rules(dns):
     qdb = qubesdb.QubesDB()
@@ -98,7 +101,17 @@ def install_firewall_rules(dns):
         'chain dnat-dns {',
         'type nat hook prerouting priority dstnat; policy accept;',
     ]
-    for vm_nameserver, dest in zip(qubesdb_dns, get_dns_resolved()):
+    dns_resolved = get_dns_resolved()
+    if not dns_resolved:
+        ''' User has no IPv4 DNS set in sys-net. Decide to drop IPv4 DNS requests
+        to qubes-netvm-primary-dns & qubes-netvm-secondary-dns addresses or dnat
+        them to 127.0.0.1 (maybe useful)'''
+        pass
+    else:
+        while len(qubesdb_dns) > len(dns_resolved):
+            ''' Ensure that upstream DNS pool is larger than qubesdb_dns pool '''
+            dns_resolved = dns_resolved + dns_resolved
+    for vm_nameserver, dest in zip(qubesdb_dns, dns_resolved):
         dns_ = str(dest)
         res += [
             f"ip daddr {vm_nameserver} udp dport 53 dnat to {dns_}",

@DemiMarie
Copy link

@alimirjamali Yes, it is safe to delete the unused variable.

@alimirjamali
Copy link

I submitted the patch. I appreciate if this could get a little bit higher priority for review as it is a security and privacy matter for people who are using Debian for their sys-net. Diagnosis details and security implications is available in the 1st post of forum thread.

@andrewdavidwong andrewdavidwong added P: major Priority: major. Between "default" and "critical" in severity. diagnosed Technical diagnosis has been performed (see issue comments). pr submitted A pull request has been submitted for this issue. and removed P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. needs diagnosis Requires technical diagnosis from developer. Replace with "diagnosed" or remove if otherwise closed. labels Jun 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects-4.2 This issue affects Qubes OS 4.2. C: Debian/Ubuntu C: networking diagnosed Technical diagnosis has been performed (see issue comments). P: major Priority: major. Between "default" and "critical" in severity. pr submitted A pull request has been submitted for this issue. T: bug Type: bug report. A problem or defect resulting in unintended behavior in something that exists.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants