diff --git a/net/dns/manager_linux.go b/net/dns/manager_linux.go index 6f5bbad5a..184ef55d1 100644 --- a/net/dns/manager_linux.go +++ b/net/dns/manager_linux.go @@ -5,7 +5,6 @@ package dns import ( - "bytes" "context" "errors" "fmt" @@ -79,6 +78,18 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat // "unmanaged" interfaces - meaning NM 1.26.6 and later // actively ignore DNS configuration we give it. So, for those // NM versions, we can and must use resolved directly. + // + // In a perfect world, we'd avoid this by replacing + // configuration out from under NM entirely (e.g. using + // directManager to overwrite resolv.conf), but in a world + // where resolved runs, we need to get correct configuration + // into resolved regardless of what's in resolv.conf (because + // resolved can also be queried over dbus, or via an NSS + // module that bypasses /etc/resolv.conf). Given that we must + // get correct configuration into resolved, we have no choice + // but to use NM, and accept the loss of IPv6 configuration + // that comes with it (see + // https://github.com/tailscale/tailscale/issues/1699) old, err := nmVersionOlderThan("1.26.6") if err != nil { // Failed to figure out NM's version, can't make a correct @@ -93,26 +104,6 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat return newResolvedManager(logf, interfaceName) case "resolvconf": dbg("rc", "resolvconf") - if err := resolvconfSourceIsNM(bs); err == nil { - dbg("src-is-nm", "yes") - if err := dbusPing("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/DnsManager"); err == nil { - dbg("nm", "yes") - old, err := nmVersionOlderThan("1.26.6") - if err != nil { - return nil, fmt.Errorf("checking NetworkManager version: %v", err) - } - if old { - dbg("nm-old", "yes") - return newNMManager(interfaceName) - } else { - dbg("nm-old", "no") - } - } else { - dbg("nm", "no") - } - } else { - dbg("src-is-nm", "no") - } if _, err := exec.LookPath("resolvconf"); err != nil { dbg("resolvconf", "no") return newDirectManager() @@ -120,21 +111,19 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat dbg("resolvconf", "yes") return newResolvconfManager(logf) case "NetworkManager": + // You'd think we would use newNMManager somewhere in + // here. However, as explained in + // https://github.com/tailscale/tailscale/issues/1699 , using + // NetworkManager for DNS configuration carries with it the + // cost of losing IPv6 configuration on the Tailscale network + // interface. So, when we can avoid it, we bypass + // NetworkManager by replacing resolv.conf directly. + // + // If you ever try to put NMManager back here, keep in mind + // that versions >=1.26.6 will ignore DNS configuration + // anyway, so you still need a fallback path that uses + // directManager. dbg("rc", "nm") - if err := dbusPing("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/DnsManager"); err != nil { - dbg("nm", "no") - return newDirectManager() - } - dbg("nm", "yes") - old, err := nmVersionOlderThan("1.26.6") - if err != nil { - return nil, fmt.Errorf("checking NetworkManager version: %v", err) - } - if old { - dbg("nm-old", "yes") - return newNMManager(interfaceName) - } - dbg("nm-old", "no") return newDirectManager() default: dbg("rc", "unknown") @@ -142,45 +131,6 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat } } -func resolvconfSourceIsNM(resolvDotConf []byte) error { - b := bytes.NewBuffer(resolvDotConf) - cfg, err := readResolv(b) - if err != nil { - return fmt.Errorf("parsing /etc/resolv.conf: %w", err) - } - - var ( - paths = []string{ - "/etc/resolvconf/run/interface/NetworkManager", - "/run/resolvconf/interface/NetworkManager", - "/var/run/resolvconf/interface/NetworkManager", - "/run/resolvconf/interfaces/NetworkManager", - "/var/run/resolvconf/interfaces/NetworkManager", - } - nmCfg OSConfig - found bool - ) - for _, path := range paths { - nmCfg, err = readResolvFile(path) - if os.IsNotExist(err) { - continue - } else if err != nil { - return err - } - found = true - break - } - if !found { - return errors.New("NetworkManager resolvconf snippet not found") - } - - if !nmCfg.Equal(cfg) { - return errors.New("NetworkManager config not applied by resolvconf") - } - - return nil -} - func nmVersionOlderThan(want string) (bool, error) { conn, err := dbus.SystemBus() if err != nil {