net/dns/{publicdns,resolver}: add NextDNS DoH support#5556
Conversation
dee6825 to
b6d6cbd
Compare
crawshaw
left a comment
There was a problem hiding this comment.
I'm not sure our code is going to do the right thing if your only DNS server is NextDNS, so there is no IPv4 address. It should work, because we have an address to give the OS, but I got lost trying to read through the code.
|
|
||
| // hasDefaultIPResolversOnly reports whether the only resolvers in c are | ||
| // DefaultResolvers, and that those resolvers are simple IP addresses. | ||
| // DefaultResolvers, and that those resolvers are simple IP addresses |
There was a problem hiding this comment.
I have spent a minute trying to decide if that should be an and or an or. I don't know. Maybe this function needs a different name? tailscaleOwnsDNS?
There was a problem hiding this comment.
From the code, it seems to be 'and'.
maisem
left a comment
There was a problem hiding this comment.
I assume you've tested that this works?
|
I've tested it but will do more after latest control changes. I also have some questions for NextDNS on how to test it better. |
b6d6cbd to
c0039fb
Compare
|
And added more tests. |
41aa508 to
e49c102
Compare
danderson
left a comment
There was a problem hiding this comment.
Looks reasonable. My main request is to try and clarify in the publicdns package the difference between IPs that can be used for UDP querying, and those which are just static pre-resolutions of DoH resolver names. It took me several confused reads through the forwarder code to figure out that there were two non-equivalent sets of IPs (IPs indicating you should DoH to NextDNS, and IPs to which you should send those DoH queries) being kicked around when configuring for NextDNS resolution.
|
|
||
| // hasDefaultIPResolversOnly reports whether the only resolvers in c are | ||
| // DefaultResolvers, and that those resolvers are simple IP addresses. | ||
| // DefaultResolvers, and that those resolvers are simple IP addresses |
There was a problem hiding this comment.
From the code, it seems to be 'and'.
net/dns/publicdns/publicdns.go
Outdated
| } | ||
|
|
||
| // DoHIPsOfBase returns a DoH base URL's IP addresses. | ||
| // It is basically the inverse of DoHEndpointFromIP. |
There was a problem hiding this comment.
Is it basically the inverse, or exactly the inverse? I think you can drop "basically".
There was a problem hiding this comment.
Okay, it's not exactly the inverse, because this one also returns IPv4s.
Can you make the docs of this function clarify that it's not returning IPs which are the equivalent of the DoH URL, it's returning a static resolution for the URL's hostname? It took me many re-reads to figure out that the returned IPs from this function cannot be used for UDP queries to NextDNS, only as a static resolution of the URL's hostname.
And drop the mention of being the inverse of DoHEndpointFromIP, that to me suggested that, just like DohEndpointFromIP simply lets you uplift a UDP DNS query into DoH, this one lets you go the other way - which would be true except for the IPv4 results which lose information.
| if b, ok := m[ip]; ok { | ||
| return b, false, true | ||
| } | ||
| if nextDNSv6RangeA.Contains(ip) || nextDNSv6RangeB.Contains(ip) { |
There was a problem hiding this comment.
What about the IPv4 NextDNS servers? Constants are declared for them, but they don't seem to be in KnownDOH or handled specially here. Shouldn't we at least do "naked" DoH without the client ID in that case?
There was a problem hiding this comment.
We only do DoH to NextDNS if we know the profile ID. The IPv4 addresses are only used for dialing DoH, not for regular DNS.
| { | ||
| base: "https://dns.nextdns.io/c3a884", | ||
| want: ips( | ||
| "45.90.28.0", |
There was a problem hiding this comment.
How will this work? If I translate the DoH name to IPs, the IPv4s lack a client ID, so presumably are not going to behave the same as the v6 endpoints? Should this function only return v6 addrs, so that you don't lose fidelity roundtripping from URL > IP > URL ?
There was a problem hiding this comment.
These are the IPs for dialing DoH.
On the wire from control to clients we'll only ever send the NextDNS v6 IP containing the profile ID in the lower bytes. We'll eat any NextDNS v4 address server side as if the user didn't specify it at all.
There was a problem hiding this comment.
Updated the comment on the func
net/dns/publicdns/publicdns.go
Outdated
| @@ -22,11 +29,63 @@ func KnownDoH() map[netip.Addr]string { | |||
| return knownDoH | |||
There was a problem hiding this comment.
I can't comment any higher, but KnownDOH is now broken, it doesn't return all known DoH. How does this affect callers?
There was a problem hiding this comment.
Removed it. It was only used by tests.
NextDNS is unique in that users create accounts and then get user-specific DNS IPs & DoH URLs. For DoH, the customer ID is in the URL path. For IPv6, the IP address includes the customer ID in the lower bits. For IPv4, there's a fragile "IP linking" mechanism to associate your public IPv4 with an assigned NextDNS IPv4 and that tuple maps to your customer ID. We don't use the IP linking mechanism. Instead, NextDNS is DoH-only. Which means using NextDNS necessarily shunts all DNS traffic through 100.100.100.100 (programming the OS to use 100.100.100.100 as the global resolver) because operating systems can't usually do DoH themselves. Once it's in Tailscale's DoH client, we then connect out to the known NextDNS IPv4/IPv6 anycast addresses. If the control plane sends the client a NextDNS IPv6 address, we then map it to the corresponding NextDNS DoH with the same client ID, and we dial that DoH server using the combination of v4/v6 anycast IPs. Updates #2452 Change-Id: I3439d798d21d5fc9df5a2701839910f5bef85463 Signed-off-by: Brad Fitzpatrick <[email protected]>
e49c102 to
e487bc0
Compare
NextDNS is unique in that users create accounts and then get
user-specific DNS IPs & DoH URLs.
For DoH, the customer ID is in the URL path.
For IPv6, the IP address includes the customer ID in the lower bits.
For IPv4, there's a fragile "IP linking" mechanism to associate your
public IPv4 with an assigned NextDNS IPv4 and that tuple maps to your
customer ID.
We don't use the IP linking mechanism.
Instead, NextDNS is DoH-only. Which means using NextDNS necessarily
shunts all DNS traffic through 100.100.100.100 (programming the OS to
use 100.100.100.100 as the global resolver) because operating systems
can't usually do DoH themselves.
Once it's in Tailscale's DoH client, we then connect out to the known
NextDNS IPv4/IPv6 anycast addresses.
If the control plane sends the client a NextDNS IPv6 address, we then
map it to the corresponding NextDNS DoH with the same client ID, and
we dial that DoH server using the combination of v4/v6 anycast IPs.
Updates #2452