mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-23 17:16:29 +00:00
net/dns, feature/featuretags: make NetworkManager, systemd-resolved, and DBus modular
Saves 360 KB (19951800 => 19591352 on linux/amd64 --extra-small --box binary) Updates #12614 Updates #17206 Change-Id: Iafd5b2536dd735111b447546cba335a7a64379ed Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
b54cdf9f38
commit
b3e9a128af
@@ -7,7 +7,6 @@ package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -15,13 +14,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/clientmetric"
|
||||
"tailscale.com/util/cmpver"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
@@ -36,6 +34,31 @@ func (kv kv) String() string {
|
||||
|
||||
var publishOnce sync.Once
|
||||
|
||||
// reconfigTimeout is the time interval within which Manager.{Up,Down} should complete.
|
||||
//
|
||||
// This is particularly useful because certain conditions can cause indefinite hangs
|
||||
// (such as improper dbus auth followed by contextless dbus.Object.Call).
|
||||
// Such operations should be wrapped in a timeout context.
|
||||
const reconfigTimeout = time.Second
|
||||
|
||||
// Set unless ts_omit_networkmanager
|
||||
var (
|
||||
optNewNMManager feature.Hook[func(ifName string) (OSConfigurator, error)]
|
||||
optNMIsUsingResolved feature.Hook[func() error]
|
||||
optNMVersionBetween feature.Hook[func(v1, v2 string) (bool, error)]
|
||||
)
|
||||
|
||||
// Set unless ts_omit_resolved
|
||||
var (
|
||||
optNewResolvedManager feature.Hook[func(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error)]
|
||||
)
|
||||
|
||||
// Set unless ts_omit_dbus
|
||||
var (
|
||||
optDBusPing feature.Hook[func(name, objectPath string) error]
|
||||
optDBusReadString feature.Hook[func(name, objectPath, iface, member string) (string, error)]
|
||||
)
|
||||
|
||||
// NewOSConfigurator created a new OS configurator.
|
||||
//
|
||||
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
||||
@@ -45,13 +68,25 @@ func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ policyclient.
|
||||
}
|
||||
|
||||
env := newOSConfigEnv{
|
||||
fs: directFS{},
|
||||
dbusPing: dbusPing,
|
||||
dbusReadString: dbusReadString,
|
||||
nmIsUsingResolved: nmIsUsingResolved,
|
||||
nmVersionBetween: nmVersionBetween,
|
||||
resolvconfStyle: resolvconfStyle,
|
||||
fs: directFS{},
|
||||
resolvconfStyle: resolvconfStyle,
|
||||
}
|
||||
if f, ok := optDBusPing.GetOk(); ok {
|
||||
env.dbusPing = f
|
||||
} else {
|
||||
env.dbusPing = func(_, _ string) error { return errors.ErrUnsupported }
|
||||
}
|
||||
if f, ok := optDBusReadString.GetOk(); ok {
|
||||
env.dbusReadString = f
|
||||
} else {
|
||||
env.dbusReadString = func(_, _, _, _ string) (string, error) { return "", errors.ErrUnsupported }
|
||||
}
|
||||
if f, ok := optNMIsUsingResolved.GetOk(); ok {
|
||||
env.nmIsUsingResolved = f
|
||||
} else {
|
||||
env.nmIsUsingResolved = func() error { return errors.ErrUnsupported }
|
||||
}
|
||||
env.nmVersionBetween, _ = optNMVersionBetween.GetOk() // GetOk to not panic if nil; unused if optNMIsUsingResolved returns an error
|
||||
mode, err := dnsMode(logf, health, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -66,17 +101,24 @@ func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ policyclient.
|
||||
case "direct":
|
||||
return newDirectManagerOnFS(logf, health, env.fs), nil
|
||||
case "systemd-resolved":
|
||||
return newResolvedManager(logf, health, interfaceName)
|
||||
if f, ok := optNewResolvedManager.GetOk(); ok {
|
||||
return f(logf, health, interfaceName)
|
||||
}
|
||||
return nil, fmt.Errorf("tailscaled was built without DNS %q support", mode)
|
||||
case "network-manager":
|
||||
return newNMManager(interfaceName)
|
||||
if f, ok := optNewNMManager.GetOk(); ok {
|
||||
return f(interfaceName)
|
||||
}
|
||||
return nil, fmt.Errorf("tailscaled was built without DNS %q support", mode)
|
||||
case "debian-resolvconf":
|
||||
return newDebianResolvconfManager(logf)
|
||||
case "openresolv":
|
||||
return newOpenresolvManager(logf)
|
||||
default:
|
||||
logf("[unexpected] detected unknown DNS mode %q, using direct manager as last resort", mode)
|
||||
return newDirectManagerOnFS(logf, health, env.fs), nil
|
||||
}
|
||||
|
||||
return newDirectManagerOnFS(logf, health, env.fs), nil
|
||||
}
|
||||
|
||||
// newOSConfigEnv are the funcs newOSConfigurator needs, pulled out for testing.
|
||||
@@ -292,50 +334,6 @@ func dnsMode(logf logger.Logf, health *health.Tracker, env newOSConfigEnv) (ret
|
||||
}
|
||||
}
|
||||
|
||||
func nmVersionBetween(first, last string) (bool, error) {
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
// DBus probably not running.
|
||||
return false, err
|
||||
}
|
||||
|
||||
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager"))
|
||||
v, err := nm.GetProperty("org.freedesktop.NetworkManager.Version")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
version, ok := v.Value().(string)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unexpected type %T for NM version", v.Value())
|
||||
}
|
||||
|
||||
outside := cmpver.Compare(version, first) < 0 || cmpver.Compare(version, last) > 0
|
||||
return !outside, nil
|
||||
}
|
||||
|
||||
func nmIsUsingResolved() error {
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
// DBus probably not running.
|
||||
return err
|
||||
}
|
||||
|
||||
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager"))
|
||||
v, err := nm.GetProperty("org.freedesktop.NetworkManager.DnsManager.Mode")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting NM mode: %w", err)
|
||||
}
|
||||
mode, ok := v.Value().(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for NM DNS mode", v.Value())
|
||||
}
|
||||
if mode != "systemd-resolved" {
|
||||
return errors.New("NetworkManager is not using systemd-resolved for DNS")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolvedIsActuallyResolver reports whether the system is using
|
||||
// systemd-resolved as the resolver. There are two different ways to
|
||||
// use systemd-resolved:
|
||||
@@ -396,44 +394,3 @@ func isLibnssResolveUsed(env newOSConfigEnv) error {
|
||||
}
|
||||
return fmt.Errorf("libnss_resolve not used")
|
||||
}
|
||||
|
||||
func dbusPing(name, objectPath string) error {
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
// DBus probably not running.
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
obj := conn.Object(name, dbus.ObjectPath(objectPath))
|
||||
call := obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0)
|
||||
return call.Err
|
||||
}
|
||||
|
||||
// dbusReadString reads a string property from the provided name and object
|
||||
// path. property must be in "interface.member" notation.
|
||||
func dbusReadString(name, objectPath, iface, member string) (string, error) {
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
// DBus probably not running.
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
obj := conn.Object(name, dbus.ObjectPath(objectPath))
|
||||
|
||||
var result dbus.Variant
|
||||
err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, iface, member).Store(&result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if s, ok := result.Value().(string); ok {
|
||||
return s, nil
|
||||
}
|
||||
return result.String(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user