ipnlocal: don't configure resolver with an invalid PeerAPI DNS URL

Updates tailscale/corp#23782

Once the node key of the current exit node expires, we should stop attempting to forward DNS queries to it, because a connection cannot be established to an expired node. Here we change the logic in `exitNodeCanProxyDNS` so that it no longer attempts to use an invalid URL `/dns-query` as the DNS resolver if an IP is no longer available in the network map for the current exit node. Instead, we return false, and log that this issue occurred.

This doesn't fix the problem, but it at least mitigates it by allowing DNS queries in the broken state to go to the local default resolver (e.g. 192.168.1.1) instead of endlessly getting forwarded to an DNS server URL that doesn't exist.

Next steps include:
- add a user-facing health warning when this verifies, instead of just a log line
- understand why re-authenticating the exit node doesn't trigger a refresh of the DNS config.

Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
angott/23782
Andrea Gottardo 3 weeks ago
parent 94c79659fa
commit f25ab855ba

@ -4089,7 +4089,7 @@ func (b *LocalBackend) authReconfig() {
hasPAC := b.prevIfState.HasPAC() hasPAC := b.prevIfState.HasPAC()
disableSubnetsIfPAC := nm.HasCap(tailcfg.NodeAttrDisableSubnetsIfPAC) disableSubnetsIfPAC := nm.HasCap(tailcfg.NodeAttrDisableSubnetsIfPAC)
userDialUseRoutes := nm.HasCap(tailcfg.NodeAttrUserDialUseRoutes) userDialUseRoutes := nm.HasCap(tailcfg.NodeAttrUserDialUseRoutes)
dohURL, dohURLOK := exitNodeCanProxyDNS(nm, b.peers, prefs.ExitNodeID()) dohURL, dohURLOK := exitNodeCanProxyDNS(nm, b.peers, prefs.ExitNodeID(), b.logf)
dcfg := dnsConfigForNetmap(nm, b.peers, prefs, b.keyExpired, b.logf, version.OS()) dcfg := dnsConfigForNetmap(nm, b.peers, prefs, b.keyExpired, b.logf, version.OS())
// If the current node is an app connector, ensure the app connector machine is started // If the current node is an app connector, ensure the app connector machine is started
b.reconfigAppConnectorLocked(nm, prefs) b.reconfigAppConnectorLocked(nm, prefs)
@ -4307,7 +4307,7 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.
// If we're using an exit node and that exit node is new enough (1.19.x+) // If we're using an exit node and that exit node is new enough (1.19.x+)
// to run a DoH DNS proxy, then send all our DNS traffic through it. // to run a DoH DNS proxy, then send all our DNS traffic through it.
if dohURL, ok := exitNodeCanProxyDNS(nm, peers, prefs.ExitNodeID()); ok { if dohURL, ok := exitNodeCanProxyDNS(nm, peers, prefs.ExitNodeID(), logf); ok {
addDefault([]*dnstype.Resolver{{Addr: dohURL}}) addDefault([]*dnstype.Resolver{{Addr: dohURL}})
return dcfg return dcfg
} }
@ -6215,13 +6215,18 @@ func (b *LocalBackend) SetExpirySooner(ctx context.Context, expiry time.Time) er
// to exitNodeID's DoH service, if available. // to exitNodeID's DoH service, if available.
// //
// If exitNodeID is the zero valid, it returns "", false. // If exitNodeID is the zero valid, it returns "", false.
func exitNodeCanProxyDNS(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, exitNodeID tailcfg.StableNodeID) (dohURL string, ok bool) { func exitNodeCanProxyDNS(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, exitNodeID tailcfg.StableNodeID, logf logger.Logf) (dohURL string, ok bool) {
if exitNodeID.IsZero() { if exitNodeID.IsZero() {
return "", false return "", false
} }
for _, p := range peers { for _, p := range peers {
if p.StableID() == exitNodeID && peerCanProxyDNS(p) { if p.StableID() == exitNodeID && peerCanProxyDNS(p) {
return peerAPIBase(nm, p) + "/dns-query", true ipPort := peerAPIBase(nm, p)
if ipPort == "" {
logf("exitNodeCanProxyDNS(%v) = false; no ipPort available", p.StableID())
return "", false
}
return ipPort + "/dns-query", true
} }
} }
return "", false return "", false

Loading…
Cancel
Save