mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-08 23:49:56 +00:00
various: create a catch-all NRPT rule when "Override local DNS" is enabled on Windows
Without this rule, Windows 8.1 and newer devices issue parallel DNS requests to DNS servers associated with all network adapters, even when "Override local DNS" is enabled and/or a Mullvad exit node is being used, resulting in DNS leaks. This also adds "disable-local-dns-override-via-nrpt" nodeAttr that can be used to disable the new behavior if needed. Fixes tailscale/corp#20718 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
@@ -485,7 +485,7 @@ func (m *Manager) FlushCaches() error {
|
||||
// in case the Tailscale daemon terminated without closing the router.
|
||||
// No other state needs to be instantiated before this runs.
|
||||
func CleanUp(logf logger.Logf, netMon *netmon.Monitor, interfaceName string) {
|
||||
oscfg, err := NewOSConfigurator(logf, nil, interfaceName)
|
||||
oscfg, err := NewOSConfigurator(logf, nil, nil, interfaceName)
|
||||
if err != nil {
|
||||
logf("creating dns cleanup: %v", err)
|
||||
return
|
||||
|
@@ -8,12 +8,16 @@ import (
|
||||
"os"
|
||||
|
||||
"go4.org/mem"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/mak"
|
||||
)
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, ifName string) (OSConfigurator, error) {
|
||||
// NewOSConfigurator creates a new OS configurator.
|
||||
//
|
||||
// The health tracker and the knobs may be nil and are ignored on this platform.
|
||||
func NewOSConfigurator(logf logger.Logf, _ *health.Tracker, _ *controlknobs.Knobs, ifName string) (OSConfigurator, error) {
|
||||
return &darwinConfigurator{logf: logf, ifName: ifName}, nil
|
||||
}
|
||||
|
||||
|
@@ -6,10 +6,14 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
func NewOSConfigurator(logger.Logf, *health.Tracker, string) (OSConfigurator, error) {
|
||||
// NewOSConfigurator creates a new OS configurator.
|
||||
//
|
||||
// The health tracker and the knobs may be nil and are ignored on this platform.
|
||||
func NewOSConfigurator(logger.Logf, *health.Tracker, *controlknobs.Knobs, string) (OSConfigurator, error) {
|
||||
return NewNoopManager()
|
||||
}
|
||||
|
@@ -7,11 +7,15 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ string) (OSConfigurator, error) {
|
||||
// NewOSConfigurator creates a new OS configurator.
|
||||
//
|
||||
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ *controlknobs.Knobs, _ string) (OSConfigurator, error) {
|
||||
bs, err := os.ReadFile("/etc/resolv.conf")
|
||||
if os.IsNotExist(err) {
|
||||
return newDirectManager(logf, health), nil
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/types/logger"
|
||||
@@ -31,7 +32,10 @@ func (kv kv) String() string {
|
||||
|
||||
var publishOnce sync.Once
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string) (ret OSConfigurator, err error) {
|
||||
// NewOSConfigurator created a new OS configurator.
|
||||
//
|
||||
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ *controlknobs.Knobs, interfaceName string) (ret OSConfigurator, err error) {
|
||||
env := newOSConfigEnv{
|
||||
fs: directFS{},
|
||||
dbusPing: dbusPing,
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
@@ -20,7 +21,10 @@ func (kv kv) String() string {
|
||||
return fmt.Sprintf("%s=%s", kv.k, kv.v)
|
||||
}
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error) {
|
||||
// NewOSConfigurator created a new OS configurator.
|
||||
//
|
||||
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ *controlknobs.Knobs, interfaceName string) (OSConfigurator, error) {
|
||||
return newOSConfigurator(logf, health, interfaceName,
|
||||
newOSConfigEnv{
|
||||
rcIsResolvd: rcIsResolvd,
|
||||
|
@@ -22,6 +22,7 @@ import (
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
"tailscale.com/atomicfile"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/types/logger"
|
||||
@@ -38,6 +39,7 @@ var configureWSL = envknob.RegisterBool("TS_DEBUG_CONFIGURE_WSL")
|
||||
type windowsManager struct {
|
||||
logf logger.Logf
|
||||
guid string
|
||||
knobs *controlknobs.Knobs // or nil
|
||||
nrptDB *nrptRuleDatabase
|
||||
wslManager *wslManager
|
||||
|
||||
@@ -45,10 +47,14 @@ type windowsManager struct {
|
||||
closing bool
|
||||
}
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error) {
|
||||
// NewOSConfigurator created a new OS configurator.
|
||||
//
|
||||
// The health tracker and the knobs may be nil.
|
||||
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, knobs *controlknobs.Knobs, interfaceName string) (OSConfigurator, error) {
|
||||
ret := &windowsManager{
|
||||
logf: logf,
|
||||
guid: interfaceName,
|
||||
knobs: knobs,
|
||||
wslManager: newWSLManager(logf, health),
|
||||
}
|
||||
|
||||
@@ -288,6 +294,10 @@ func (m *windowsManager) setPrimaryDNS(resolvers []netip.Addr, domains []dnsname
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *windowsManager) disableLocalDNSOverrideViaNRPT() bool {
|
||||
return m.knobs != nil && m.knobs.DisableLocalDNSOverrideViaNRPT.Load()
|
||||
}
|
||||
|
||||
func (m *windowsManager) SetDNS(cfg OSConfig) error {
|
||||
// We can configure Windows DNS in one of two ways:
|
||||
//
|
||||
@@ -322,7 +332,17 @@ func (m *windowsManager) SetDNS(cfg OSConfig) error {
|
||||
}
|
||||
|
||||
if len(cfg.MatchDomains) == 0 {
|
||||
if err := m.setSplitDNS(nil, nil); err != nil {
|
||||
var resolvers []netip.Addr
|
||||
var domains []dnsname.FQDN
|
||||
if !m.disableLocalDNSOverrideViaNRPT() {
|
||||
// Create a default catch-all rule to make ourselves the actual primary resolver.
|
||||
// Without this rule, Windows 8.1 and newer devices issue parallel DNS requests to DNS servers
|
||||
// associated with all network adapters, even when "Override local DNS" is enabled and/or
|
||||
// a Mullvad exit node is being used, resulting in DNS leaks.
|
||||
resolvers = cfg.Nameservers
|
||||
domains = []dnsname.FQDN{"."}
|
||||
}
|
||||
if err := m.setSplitDNS(resolvers, domains); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.setHosts(nil); err != nil {
|
||||
@@ -331,8 +351,6 @@ func (m *windowsManager) SetDNS(cfg OSConfig) error {
|
||||
if err := m.setPrimaryDNS(cfg.Nameservers, cfg.SearchDomains); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if m.nrptDB == nil {
|
||||
return errors.New("cannot set per-domain resolvers on Windows 7")
|
||||
} else {
|
||||
if err := m.setSplitDNS(cfg.Nameservers, cfg.MatchDomains); err != nil {
|
||||
return err
|
||||
|
@@ -84,7 +84,7 @@ func TestManagerWindowsGPCopy(t *testing.T) {
|
||||
}
|
||||
defer delIfKey()
|
||||
|
||||
cfg, err := NewOSConfigurator(logf, nil, fakeInterface.String())
|
||||
cfg, err := NewOSConfigurator(logf, nil, nil, fakeInterface.String())
|
||||
if err != nil {
|
||||
t.Fatalf("NewOSConfigurator: %v\n", err)
|
||||
}
|
||||
@@ -213,7 +213,7 @@ func runTest(t *testing.T, isLocal bool) {
|
||||
}
|
||||
defer delIfKey()
|
||||
|
||||
cfg, err := NewOSConfigurator(logf, nil, fakeInterface.String())
|
||||
cfg, err := NewOSConfigurator(logf, nil, nil, fakeInterface.String())
|
||||
if err != nil {
|
||||
t.Fatalf("NewOSConfigurator: %v\n", err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user