net/dns: avoid using NetworkManager as much as possible. (#1945)
Addresses #1699 as best as possible. Signed-off-by: David Anderson <danderson@tailscale.com>pull/2108/head
parent
f944614c5c
commit
144c68b80b
|
@ -5,7 +5,6 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -79,6 +78,18 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
|
||||||
// "unmanaged" interfaces - meaning NM 1.26.6 and later
|
// "unmanaged" interfaces - meaning NM 1.26.6 and later
|
||||||
// actively ignore DNS configuration we give it. So, for those
|
// actively ignore DNS configuration we give it. So, for those
|
||||||
// NM versions, we can and must use resolved directly.
|
// 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")
|
old, err := nmVersionOlderThan("1.26.6")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Failed to figure out NM's version, can't make a correct
|
// 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)
|
return newResolvedManager(logf, interfaceName)
|
||||||
case "resolvconf":
|
case "resolvconf":
|
||||||
dbg("rc", "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 {
|
if _, err := exec.LookPath("resolvconf"); err != nil {
|
||||||
dbg("resolvconf", "no")
|
dbg("resolvconf", "no")
|
||||||
return newDirectManager()
|
return newDirectManager()
|
||||||
|
@ -120,21 +111,19 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
|
||||||
dbg("resolvconf", "yes")
|
dbg("resolvconf", "yes")
|
||||||
return newResolvconfManager(logf)
|
return newResolvconfManager(logf)
|
||||||
case "NetworkManager":
|
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")
|
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()
|
return newDirectManager()
|
||||||
default:
|
default:
|
||||||
dbg("rc", "unknown")
|
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) {
|
func nmVersionOlderThan(want string) (bool, error) {
|
||||||
conn, err := dbus.SystemBus()
|
conn, err := dbus.SystemBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue