mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
tailcfg,net/dns: add controlknob to disable battery split DNS on iOS (#12346)
Updates corp#15802. Adds the ability for control to disable the recently added change that uses split DNS in more cases on iOS. This will allow us to disable the feature if it leads to regression in production. We plan to remove this knob once we've verified that the feature works properly. Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
This commit is contained in:
parent
e88a5dbc92
commit
b65221999c
@ -81,6 +81,15 @@ type Knobs struct {
|
|||||||
// how to dial the destination address. When true, it also makes the DNS forwarder
|
// how to dial the destination address. When true, it also makes the DNS forwarder
|
||||||
// use UserDial instead of SystemDial when dialing resolvers.
|
// use UserDial instead of SystemDial when dialing resolvers.
|
||||||
UserDialUseRoutes atomic.Bool
|
UserDialUseRoutes atomic.Bool
|
||||||
|
|
||||||
|
// DisableSplitDNSWhenNoCustomResolvers indicates that the node's DNS manager
|
||||||
|
// should not adopt a split DNS configuration even though the Config of the
|
||||||
|
// resolver only contains routes that do not specify custom resolver(s), hence
|
||||||
|
// all DNS queries can be safely sent to the upstream DNS resolver and the
|
||||||
|
// node's DNS forwarder doesn't need to handle all DNS traffic.
|
||||||
|
// This is for now (2024-06-06) an iOS-specific battery life optimization,
|
||||||
|
// and this knob allows us to disable the optimization remotely if needed.
|
||||||
|
DisableSplitDNSWhenNoCustomResolvers atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
|
// UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
|
||||||
@ -91,22 +100,23 @@ func (k *Knobs) UpdateFromNodeAttributes(capMap tailcfg.NodeCapMap) {
|
|||||||
}
|
}
|
||||||
has := capMap.Contains
|
has := capMap.Contains
|
||||||
var (
|
var (
|
||||||
keepFullWG = has(tailcfg.NodeAttrDebugDisableWGTrim)
|
keepFullWG = has(tailcfg.NodeAttrDebugDisableWGTrim)
|
||||||
disableDRPO = has(tailcfg.NodeAttrDebugDisableDRPO)
|
disableDRPO = has(tailcfg.NodeAttrDebugDisableDRPO)
|
||||||
disableUPnP = has(tailcfg.NodeAttrDisableUPnP)
|
disableUPnP = has(tailcfg.NodeAttrDisableUPnP)
|
||||||
randomizeClientPort = has(tailcfg.NodeAttrRandomizeClientPort)
|
randomizeClientPort = has(tailcfg.NodeAttrRandomizeClientPort)
|
||||||
disableDeltaUpdates = has(tailcfg.NodeAttrDisableDeltaUpdates)
|
disableDeltaUpdates = has(tailcfg.NodeAttrDisableDeltaUpdates)
|
||||||
oneCGNAT opt.Bool
|
oneCGNAT opt.Bool
|
||||||
forceBackgroundSTUN = has(tailcfg.NodeAttrDebugForceBackgroundSTUN)
|
forceBackgroundSTUN = has(tailcfg.NodeAttrDebugForceBackgroundSTUN)
|
||||||
peerMTUEnable = has(tailcfg.NodeAttrPeerMTUEnable)
|
peerMTUEnable = has(tailcfg.NodeAttrPeerMTUEnable)
|
||||||
dnsForwarderDisableTCPRetries = has(tailcfg.NodeAttrDNSForwarderDisableTCPRetries)
|
dnsForwarderDisableTCPRetries = has(tailcfg.NodeAttrDNSForwarderDisableTCPRetries)
|
||||||
silentDisco = has(tailcfg.NodeAttrSilentDisco)
|
silentDisco = has(tailcfg.NodeAttrSilentDisco)
|
||||||
forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
|
forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
|
||||||
forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
|
forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
|
||||||
seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
|
seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
|
||||||
probeUDPLifetime = has(tailcfg.NodeAttrProbeUDPLifetime)
|
probeUDPLifetime = has(tailcfg.NodeAttrProbeUDPLifetime)
|
||||||
appCStoreRoutes = has(tailcfg.NodeAttrStoreAppCRoutes)
|
appCStoreRoutes = has(tailcfg.NodeAttrStoreAppCRoutes)
|
||||||
userDialUseRoutes = has(tailcfg.NodeAttrUserDialUseRoutes)
|
userDialUseRoutes = has(tailcfg.NodeAttrUserDialUseRoutes)
|
||||||
|
disableSplitDNSWhenNoCustomResolvers = has(tailcfg.NodeAttrDisableSplitDNSWhenNoCustomResolvers)
|
||||||
)
|
)
|
||||||
|
|
||||||
if has(tailcfg.NodeAttrOneCGNATEnable) {
|
if has(tailcfg.NodeAttrOneCGNATEnable) {
|
||||||
@ -131,6 +141,7 @@ func (k *Knobs) UpdateFromNodeAttributes(capMap tailcfg.NodeCapMap) {
|
|||||||
k.ProbeUDPLifetime.Store(probeUDPLifetime)
|
k.ProbeUDPLifetime.Store(probeUDPLifetime)
|
||||||
k.AppCStoreRoutes.Store(appCStoreRoutes)
|
k.AppCStoreRoutes.Store(appCStoreRoutes)
|
||||||
k.UserDialUseRoutes.Store(userDialUseRoutes)
|
k.UserDialUseRoutes.Store(userDialUseRoutes)
|
||||||
|
k.DisableSplitDNSWhenNoCustomResolvers.Store(disableSplitDNSWhenNoCustomResolvers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsDebugJSON returns k as something that can be marshalled with json.Marshal
|
// AsDebugJSON returns k as something that can be marshalled with json.Marshal
|
||||||
@ -140,21 +151,22 @@ func (k *Knobs) AsDebugJSON() map[string]any {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"DisableUPnP": k.DisableUPnP.Load(),
|
"DisableUPnP": k.DisableUPnP.Load(),
|
||||||
"DisableDRPO": k.DisableDRPO.Load(),
|
"DisableDRPO": k.DisableDRPO.Load(),
|
||||||
"KeepFullWGConfig": k.KeepFullWGConfig.Load(),
|
"KeepFullWGConfig": k.KeepFullWGConfig.Load(),
|
||||||
"RandomizeClientPort": k.RandomizeClientPort.Load(),
|
"RandomizeClientPort": k.RandomizeClientPort.Load(),
|
||||||
"OneCGNAT": k.OneCGNAT.Load(),
|
"OneCGNAT": k.OneCGNAT.Load(),
|
||||||
"ForceBackgroundSTUN": k.ForceBackgroundSTUN.Load(),
|
"ForceBackgroundSTUN": k.ForceBackgroundSTUN.Load(),
|
||||||
"DisableDeltaUpdates": k.DisableDeltaUpdates.Load(),
|
"DisableDeltaUpdates": k.DisableDeltaUpdates.Load(),
|
||||||
"PeerMTUEnable": k.PeerMTUEnable.Load(),
|
"PeerMTUEnable": k.PeerMTUEnable.Load(),
|
||||||
"DisableDNSForwarderTCPRetries": k.DisableDNSForwarderTCPRetries.Load(),
|
"DisableDNSForwarderTCPRetries": k.DisableDNSForwarderTCPRetries.Load(),
|
||||||
"SilentDisco": k.SilentDisco.Load(),
|
"SilentDisco": k.SilentDisco.Load(),
|
||||||
"LinuxForceIPTables": k.LinuxForceIPTables.Load(),
|
"LinuxForceIPTables": k.LinuxForceIPTables.Load(),
|
||||||
"LinuxForceNfTables": k.LinuxForceNfTables.Load(),
|
"LinuxForceNfTables": k.LinuxForceNfTables.Load(),
|
||||||
"SeamlessKeyRenewal": k.SeamlessKeyRenewal.Load(),
|
"SeamlessKeyRenewal": k.SeamlessKeyRenewal.Load(),
|
||||||
"ProbeUDPLifetime": k.ProbeUDPLifetime.Load(),
|
"ProbeUDPLifetime": k.ProbeUDPLifetime.Load(),
|
||||||
"AppCStoreRoutes": k.AppCStoreRoutes.Load(),
|
"AppCStoreRoutes": k.AppCStoreRoutes.Load(),
|
||||||
"UserDialUseRoutes": k.UserDialUseRoutes.Load(),
|
"UserDialUseRoutes": k.UserDialUseRoutes.Load(),
|
||||||
|
"DisableSplitDNSWhenNoCustomResolvers": k.DisableSplitDNSWhenNoCustomResolvers.Load(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ type Manager struct {
|
|||||||
|
|
||||||
resolver *resolver.Resolver
|
resolver *resolver.Resolver
|
||||||
os OSConfigurator
|
os OSConfigurator
|
||||||
|
knobs *controlknobs.Knobs
|
||||||
goos string // if empty, gets set to runtime.GOOS
|
goos string // if empty, gets set to runtime.GOOS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,11 +68,13 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, health *health.Tracker,
|
|||||||
if goos == "" {
|
if goos == "" {
|
||||||
goos = runtime.GOOS
|
goos = runtime.GOOS
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
logf: logf,
|
logf: logf,
|
||||||
resolver: resolver.New(logf, linkSel, dialer, knobs),
|
resolver: resolver.New(logf, linkSel, dialer, knobs),
|
||||||
os: oscfg,
|
os: oscfg,
|
||||||
health: health,
|
health: health,
|
||||||
|
knobs: knobs,
|
||||||
goos: goos,
|
goos: goos,
|
||||||
}
|
}
|
||||||
m.ctx, m.ctxCancel = context.WithCancel(context.Background())
|
m.ctx, m.ctxCancel = context.WithCancel(context.Background())
|
||||||
@ -273,8 +276,12 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
|
|||||||
// a query for 'work-laptop' might lead to search domain expansion, resolving
|
// a query for 'work-laptop' might lead to search domain expansion, resolving
|
||||||
// as 'work-laptop.aws.com' for example.
|
// as 'work-laptop.aws.com' for example.
|
||||||
if m.goos == "ios" && rcfg.RoutesRequireNoCustomResolvers() {
|
if m.goos == "ios" && rcfg.RoutesRequireNoCustomResolvers() {
|
||||||
for r := range rcfg.Routes {
|
if !m.disableSplitDNSOptimization() {
|
||||||
ocfg.MatchDomains = append(ocfg.MatchDomains, r)
|
for r := range rcfg.Routes {
|
||||||
|
ocfg.MatchDomains = append(ocfg.MatchDomains, r)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.logf("iOS split DNS is disabled by nodeattr")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var defaultRoutes []*dnstype.Resolver
|
var defaultRoutes []*dnstype.Resolver
|
||||||
@ -288,6 +295,10 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
|
|||||||
return rcfg, ocfg, nil
|
return rcfg, ocfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) disableSplitDNSOptimization() bool {
|
||||||
|
return m.knobs.DisableSplitDNSWhenNoCustomResolvers.Load()
|
||||||
|
}
|
||||||
|
|
||||||
// toIPsOnly returns only the IP portion of dnstype.Resolver.
|
// toIPsOnly returns only the IP portion of dnstype.Resolver.
|
||||||
// Only safe to use if the resolvers slice has been cleared of
|
// Only safe to use if the resolvers slice has been cleared of
|
||||||
// DoH or custom-port entries with something like hasDefaultIPResolversOnly.
|
// DoH or custom-port entries with something like hasDefaultIPResolversOnly.
|
||||||
|
@ -2286,6 +2286,17 @@ type Oauth2Token struct {
|
|||||||
// NodeAttrSSHBehaviorV1 forces SSH to use the V1 behavior (no su, run SFTP in-process)
|
// NodeAttrSSHBehaviorV1 forces SSH to use the V1 behavior (no su, run SFTP in-process)
|
||||||
// Added 2024-05-29 in Tailscale version 1.68.
|
// Added 2024-05-29 in Tailscale version 1.68.
|
||||||
NodeAttrSSHBehaviorV1 NodeCapability = "ssh-behavior-v1"
|
NodeAttrSSHBehaviorV1 NodeCapability = "ssh-behavior-v1"
|
||||||
|
|
||||||
|
// NodeAttrDisableSplitDNSWhenNoCustomResolvers indicates that the node's
|
||||||
|
// DNS manager should not adopt a split DNS configuration even though the
|
||||||
|
// Config of the resolver only contains routes that do not specify custom
|
||||||
|
// resolver(s), hence all DNS queries can be safely sent to the upstream
|
||||||
|
// DNS resolver and the node's DNS forwarder doesn't need to handle all
|
||||||
|
// DNS traffic.
|
||||||
|
// This is for now (2024-06-06) an iOS-specific battery life optimization,
|
||||||
|
// and this node attribute allows us to disable the optimization remotely
|
||||||
|
// if needed.
|
||||||
|
NodeAttrDisableSplitDNSWhenNoCustomResolvers NodeCapability = "disable-split-dns-when-no-custom-resolvers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetDNSRequest is a request to add a DNS record.
|
// SetDNSRequest is a request to add a DNS record.
|
||||||
|
Loading…
Reference in New Issue
Block a user