From 1e5c608faec8c156f5fc4f710722b7f93dc73e0e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 19 Apr 2021 21:58:26 -0700 Subject: [PATCH] ipn/ipnlocal: plumb fallback DNS in as a workaround for split DNS issues. Cause of #1743. Signed-off-by: David Anderson --- ipn/ipnlocal/local.go | 37 +++++++++++++++++++++++++++++++------ tailcfg/tailcfg.go | 1 + 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index cd115d13e..645674cad 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1581,14 +1581,18 @@ func (b *LocalBackend) authReconfig() { // If CorpDNS is false, dcfg remains the zero value. if uc.CorpDNS { - for _, resolver := range nm.DNS.Resolvers { - res, err := parseResolver(resolver) - if err != nil { - b.logf(err.Error()) - continue + addDefault := func(resolvers []tailcfg.DNSResolver) { + for _, resolver := range resolvers { + res, err := parseResolver(resolver) + if err != nil { + b.logf("skipping bad resolver: %v", err.Error()) + continue + } + dcfg.DefaultResolvers = append(dcfg.DefaultResolvers, res) } - dcfg.DefaultResolvers = append(dcfg.DefaultResolvers, res) } + + addDefault(nm.DNS.Resolvers) if len(nm.DNS.Routes) > 0 { dcfg.Routes = map[dnsname.FQDN][]netaddr.IPPort{} } @@ -1635,6 +1639,27 @@ func (b *LocalBackend) authReconfig() { set(peer.Name, peer.Addresses) } } + + // Set FallbackResolvers as the default resolvers in the + // scenarios that can't handle a purely split-DNS config. See + // https://github.com/tailscale/tailscale/issues/1743 for + // details. + switch { + case len(dcfg.DefaultResolvers) != 0: + // Default resolvers already set. + case len(dcfg.Routes) == 0 && len(dcfg.Hosts) == 0 && len(dcfg.AuthoritativeSuffixes) == 0: + // No settings requiring split DNS, no problem. + case (version.OS() == "iOS" || version.OS() == "macOS") && !uc.ExitNodeID.IsZero(): + // On Apple OSes, if your NetworkExtension provides a + // default route, underlying primary resolvers are + // automatically removed, so we MUST provide a set of + // resolvers capable of resolving the entire world. + // https://github.com/tailscale/tailscale/issues/1713 + addDefault(nm.DNS.FallbackResolvers) + case version.OS() == "android": + // We don't support split DNS at all on Android yet. + addDefault(nm.DNS.FallbackResolvers) + } } err = b.e.Reconfig(cfg, rcfg, &dcfg) diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 08e4c962b..81419040a 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -852,6 +852,7 @@ type DNSConfig struct { // FallbackResolvers is like Resolvers, but is only used if a // split DNS configuration is requested in a configuration that // doesn't work yet without explicit default resolvers. + // https://github.com/tailscale/tailscale/issues/1743 FallbackResolvers []DNSResolver `json:",omitempty"` // Domains are the search domains to use. // Search domains must be FQDNs, but *without* the trailing dot.