mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 17:49:02 +00:00
feature/featuretags: add features for c2n, peerapi, advertise/use routes/exit nodes
Saves 262 KB so far. I'm sure I missed some places, but shotizam says these were the low hanging fruit. Updates #12614 Change-Id: Ia31c01b454f627e6d0470229aae4e19d615e45e3 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
2cd518a8b6
commit
a208cb9fd5
@@ -1409,6 +1409,9 @@ func (c *Direct) answerPing(pr *tailcfg.PingRequest) {
|
|||||||
answerHeadPing(c.logf, httpc, pr)
|
answerHeadPing(c.logf, httpc, pr)
|
||||||
return
|
return
|
||||||
case "c2n":
|
case "c2n":
|
||||||
|
if !buildfeatures.HasC2N {
|
||||||
|
return
|
||||||
|
}
|
||||||
if !useNoise && !envknob.Bool("TS_DEBUG_PERMIT_HTTP_C2N") {
|
if !useNoise && !envknob.Bool("TS_DEBUG_PERMIT_HTTP_C2N") {
|
||||||
c.logf("refusing to answer c2n ping without noise")
|
c.logf("refusing to answer c2n ping without noise")
|
||||||
return
|
return
|
||||||
|
|||||||
13
feature/buildfeatures/feature_advertiseexitnode_disabled.go
Normal file
13
feature/buildfeatures/feature_advertiseexitnode_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_advertiseexitnode
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasAdvertiseExitNode is whether the binary was built with support for modular feature "Run an exit node".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_advertiseexitnode" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasAdvertiseExitNode = false
|
||||||
13
feature/buildfeatures/feature_advertiseexitnode_enabled.go
Normal file
13
feature/buildfeatures/feature_advertiseexitnode_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_advertiseexitnode
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasAdvertiseExitNode is whether the binary was built with support for modular feature "Run an exit node".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_advertiseexitnode" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasAdvertiseExitNode = true
|
||||||
13
feature/buildfeatures/feature_advertiseroutes_disabled.go
Normal file
13
feature/buildfeatures/feature_advertiseroutes_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_advertiseroutes
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasAdvertiseRoutes is whether the binary was built with support for modular feature "Advertise routes for other nodes to use".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_advertiseroutes" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasAdvertiseRoutes = false
|
||||||
13
feature/buildfeatures/feature_advertiseroutes_enabled.go
Normal file
13
feature/buildfeatures/feature_advertiseroutes_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_advertiseroutes
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasAdvertiseRoutes is whether the binary was built with support for modular feature "Advertise routes for other nodes to use".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_advertiseroutes" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasAdvertiseRoutes = true
|
||||||
13
feature/buildfeatures/feature_c2n_disabled.go
Normal file
13
feature/buildfeatures/feature_c2n_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_c2n
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasC2N is whether the binary was built with support for modular feature "Control-to-node (C2N) support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_c2n" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasC2N = false
|
||||||
13
feature/buildfeatures/feature_c2n_enabled.go
Normal file
13
feature/buildfeatures/feature_c2n_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_c2n
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasC2N is whether the binary was built with support for modular feature "Control-to-node (C2N) support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_c2n" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasC2N = true
|
||||||
13
feature/buildfeatures/feature_peerapiclient_disabled.go
Normal file
13
feature/buildfeatures/feature_peerapiclient_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_peerapiclient
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasPeerAPIClient is whether the binary was built with support for modular feature "PeerAPI client support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_peerapiclient" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasPeerAPIClient = false
|
||||||
13
feature/buildfeatures/feature_peerapiclient_enabled.go
Normal file
13
feature/buildfeatures/feature_peerapiclient_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_peerapiclient
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasPeerAPIClient is whether the binary was built with support for modular feature "PeerAPI client support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_peerapiclient" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasPeerAPIClient = true
|
||||||
13
feature/buildfeatures/feature_peerapiserver_disabled.go
Normal file
13
feature/buildfeatures/feature_peerapiserver_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_peerapiserver
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasPeerAPIServer is whether the binary was built with support for modular feature "PeerAPI server support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_peerapiserver" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasPeerAPIServer = false
|
||||||
13
feature/buildfeatures/feature_peerapiserver_enabled.go
Normal file
13
feature/buildfeatures/feature_peerapiserver_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_peerapiserver
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasPeerAPIServer is whether the binary was built with support for modular feature "PeerAPI server support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_peerapiserver" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasPeerAPIServer = true
|
||||||
13
feature/buildfeatures/feature_useexitnode_disabled.go
Normal file
13
feature/buildfeatures/feature_useexitnode_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_useexitnode
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasUseExitNode is whether the binary was built with support for modular feature "Use exit nodes".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_useexitnode" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasUseExitNode = false
|
||||||
13
feature/buildfeatures/feature_useexitnode_enabled.go
Normal file
13
feature/buildfeatures/feature_useexitnode_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_useexitnode
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasUseExitNode is whether the binary was built with support for modular feature "Use exit nodes".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_useexitnode" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasUseExitNode = true
|
||||||
13
feature/buildfeatures/feature_useroutes_disabled.go
Normal file
13
feature/buildfeatures/feature_useroutes_disabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_useroutes
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasUseRoutes is whether the binary was built with support for modular feature "Use routes advertised by other nodes".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_useroutes" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasUseRoutes = false
|
||||||
13
feature/buildfeatures/feature_useroutes_enabled.go
Normal file
13
feature/buildfeatures/feature_useroutes_enabled.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_useroutes
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasUseRoutes is whether the binary was built with support for modular feature "Use routes advertised by other nodes".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_useroutes" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasUseRoutes = true
|
||||||
@@ -82,6 +82,12 @@ type FeatureMeta struct {
|
|||||||
Sym string // exported Go symbol for boolean const
|
Sym string // exported Go symbol for boolean const
|
||||||
Desc string // human-readable description
|
Desc string // human-readable description
|
||||||
Deps []FeatureTag // other features this feature requires
|
Deps []FeatureTag // other features this feature requires
|
||||||
|
|
||||||
|
// ImplementationDetail is whether the feature is an internal implementation
|
||||||
|
// detail. That is, it's not something a user wuold care about having or not
|
||||||
|
// having, but we'd like to able to omit from builds if no other
|
||||||
|
// user-visible features depend on it.
|
||||||
|
ImplementationDetail bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Features are the known Tailscale features that can be selectively included or
|
// Features are the known Tailscale features that can be selectively included or
|
||||||
@@ -90,17 +96,45 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
"acme": {Sym: "ACME", Desc: "ACME TLS certificate management"},
|
"acme": {Sym: "ACME", Desc: "ACME TLS certificate management"},
|
||||||
"appconnectors": {Sym: "AppConnectors", Desc: "App Connectors support"},
|
"appconnectors": {Sym: "AppConnectors", Desc: "App Connectors support"},
|
||||||
"aws": {Sym: "AWS", Desc: "AWS integration"},
|
"aws": {Sym: "AWS", Desc: "AWS integration"},
|
||||||
"bakedroots": {Sym: "BakedRoots", Desc: "Embed CA (LetsEncrypt) x509 roots to use as fallback"},
|
"advertiseexitnode": {
|
||||||
"bird": {Sym: "Bird", Desc: "Bird BGP integration"},
|
Sym: "AdvertiseExitNode",
|
||||||
|
Desc: "Run an exit node",
|
||||||
|
Deps: []FeatureTag{
|
||||||
|
"peerapiserver", // to run the ExitDNS server
|
||||||
|
"advertiseroutes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"advertiseroutes": {
|
||||||
|
Sym: "AdvertiseRoutes",
|
||||||
|
Desc: "Advertise routes for other nodes to use",
|
||||||
|
Deps: []FeatureTag{
|
||||||
|
"c2n", // for control plane to probe health for HA subnet router leader election
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"bakedroots": {Sym: "BakedRoots", Desc: "Embed CA (LetsEncrypt) x509 roots to use as fallback"},
|
||||||
|
"bird": {Sym: "Bird", Desc: "Bird BGP integration"},
|
||||||
|
"c2n": {
|
||||||
|
Sym: "C2N",
|
||||||
|
Desc: "Control-to-node (C2N) support",
|
||||||
|
ImplementationDetail: true,
|
||||||
|
},
|
||||||
"captiveportal": {Sym: "CaptivePortal", Desc: "Captive portal detection"},
|
"captiveportal": {Sym: "CaptivePortal", Desc: "Captive portal detection"},
|
||||||
"capture": {Sym: "Capture", Desc: "Packet capture"},
|
"capture": {Sym: "Capture", Desc: "Packet capture"},
|
||||||
"cloud": {Sym: "Cloud", Desc: "detect cloud environment to learn instances IPs and DNS servers"},
|
|
||||||
"cli": {Sym: "CLI", Desc: "embed the CLI into the tailscaled binary"},
|
"cli": {Sym: "CLI", Desc: "embed the CLI into the tailscaled binary"},
|
||||||
"cliconndiag": {Sym: "CLIConnDiag", Desc: "CLI connection error diagnostics"},
|
"cliconndiag": {Sym: "CLIConnDiag", Desc: "CLI connection error diagnostics"},
|
||||||
"clientmetrics": {Sym: "ClientMetrics", Desc: "Client metrics support"},
|
"clientmetrics": {Sym: "ClientMetrics", Desc: "Client metrics support"},
|
||||||
"clientupdate": {Sym: "ClientUpdate", Desc: "Client auto-update support"},
|
"clientupdate": {
|
||||||
"completion": {Sym: "Completion", Desc: "CLI shell completion"},
|
Sym: "ClientUpdate",
|
||||||
"dbus": {Sym: "DBus", Desc: "Linux DBus support"},
|
Desc: "Client auto-update support",
|
||||||
|
Deps: []FeatureTag{"c2n"},
|
||||||
|
},
|
||||||
|
"completion": {Sym: "Completion", Desc: "CLI shell completion"},
|
||||||
|
"cloud": {Sym: "Cloud", Desc: "detect cloud environment to learn instances IPs and DNS servers"},
|
||||||
|
"dbus": {
|
||||||
|
Sym: "DBus",
|
||||||
|
Desc: "Linux DBus support",
|
||||||
|
ImplementationDetail: true,
|
||||||
|
},
|
||||||
"debug": {Sym: "Debug", Desc: "various debug support, for things that don't have or need their own more specific feature"},
|
"debug": {Sym: "Debug", Desc: "various debug support, for things that don't have or need their own more specific feature"},
|
||||||
"debugeventbus": {Sym: "DebugEventBus", Desc: "eventbus debug support"},
|
"debugeventbus": {Sym: "DebugEventBus", Desc: "eventbus debug support"},
|
||||||
"debugportmapper": {
|
"debugportmapper": {
|
||||||
@@ -144,6 +178,16 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
// by some other feature are missing, then it's an error by default unless you accept
|
// by some other feature are missing, then it's an error by default unless you accept
|
||||||
// that it's okay to proceed without that meta feature.
|
// that it's okay to proceed without that meta feature.
|
||||||
},
|
},
|
||||||
|
"peerapiclient": {
|
||||||
|
Sym: "PeerAPIClient",
|
||||||
|
Desc: "PeerAPI client support",
|
||||||
|
ImplementationDetail: true,
|
||||||
|
},
|
||||||
|
"peerapiserver": {
|
||||||
|
Sym: "PeerAPIServer",
|
||||||
|
Desc: "PeerAPI server support",
|
||||||
|
ImplementationDetail: true,
|
||||||
|
},
|
||||||
"portlist": {Sym: "PortList", Desc: "Optionally advertise listening service ports"},
|
"portlist": {Sym: "PortList", Desc: "Optionally advertise listening service ports"},
|
||||||
"portmapper": {Sym: "PortMapper", Desc: "NAT-PMP/PCP/UPnP port mapping support"},
|
"portmapper": {Sym: "PortMapper", Desc: "NAT-PMP/PCP/UPnP port mapping support"},
|
||||||
"posture": {Sym: "Posture", Desc: "Device posture checking support"},
|
"posture": {Sym: "Posture", Desc: "Device posture checking support"},
|
||||||
@@ -180,7 +224,7 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
"ssh": {
|
"ssh": {
|
||||||
Sym: "SSH",
|
Sym: "SSH",
|
||||||
Desc: "Tailscale SSH support",
|
Desc: "Tailscale SSH support",
|
||||||
Deps: []FeatureTag{"dbus", "netstack"},
|
Deps: []FeatureTag{"c2n", "dbus", "netstack"},
|
||||||
},
|
},
|
||||||
"synology": {
|
"synology": {
|
||||||
Sym: "Synology",
|
Sym: "Synology",
|
||||||
@@ -192,7 +236,13 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
Desc: "Linux system tray",
|
Desc: "Linux system tray",
|
||||||
Deps: []FeatureTag{"dbus"},
|
Deps: []FeatureTag{"dbus"},
|
||||||
},
|
},
|
||||||
"taildrop": {Sym: "Taildrop", Desc: "Taildrop (file sending) support"},
|
"taildrop": {
|
||||||
|
Sym: "Taildrop",
|
||||||
|
Desc: "Taildrop (file sending) support",
|
||||||
|
Deps: []FeatureTag{
|
||||||
|
"peerapiclient", "peerapiserver", // assume Taildrop is both sides for now
|
||||||
|
},
|
||||||
|
},
|
||||||
"tailnetlock": {Sym: "TailnetLock", Desc: "Tailnet Lock support"},
|
"tailnetlock": {Sym: "TailnetLock", Desc: "Tailnet Lock support"},
|
||||||
"tap": {Sym: "Tap", Desc: "Experimental Layer 2 (ethernet) support"},
|
"tap": {Sym: "Tap", Desc: "Experimental Layer 2 (ethernet) support"},
|
||||||
"tpm": {Sym: "TPM", Desc: "TPM support"},
|
"tpm": {Sym: "TPM", Desc: "TPM support"},
|
||||||
@@ -200,6 +250,15 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
Sym: "UnixSocketIdentity",
|
Sym: "UnixSocketIdentity",
|
||||||
Desc: "differentiate between users accessing the LocalAPI over unix sockets (if omitted, all users have full access)",
|
Desc: "differentiate between users accessing the LocalAPI over unix sockets (if omitted, all users have full access)",
|
||||||
},
|
},
|
||||||
|
"useroutes": {
|
||||||
|
Sym: "UseRoutes",
|
||||||
|
Desc: "Use routes advertised by other nodes",
|
||||||
|
},
|
||||||
|
"useexitnode": {
|
||||||
|
Sym: "UseExitNode",
|
||||||
|
Desc: "Use exit nodes",
|
||||||
|
Deps: []FeatureTag{"peerapiclient", "useroutes"},
|
||||||
|
},
|
||||||
"useproxy": {
|
"useproxy": {
|
||||||
Sym: "UseProxy",
|
Sym: "UseProxy",
|
||||||
Desc: "Support using system proxies as specified by env vars or the system configuration to reach Tailscale servers.",
|
Desc: "Support using system proxies as specified by env vars or the system configuration to reach Tailscale servers.",
|
||||||
|
|||||||
@@ -32,12 +32,17 @@ import (
|
|||||||
// c2nHandlers maps an HTTP method and URI path (without query parameters) to
|
// c2nHandlers maps an HTTP method and URI path (without query parameters) to
|
||||||
// its handler. The exact method+path match is preferred, but if no entry
|
// its handler. The exact method+path match is preferred, but if no entry
|
||||||
// exists for that, a map entry with an empty method is used as a fallback.
|
// exists for that, a map entry with an empty method is used as a fallback.
|
||||||
var c2nHandlers = map[methodAndPath]c2nHandler{
|
var c2nHandlers map[methodAndPath]c2nHandler
|
||||||
// Debug.
|
|
||||||
req("/echo"): handleC2NEcho,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
c2nHandlers = map[methodAndPath]c2nHandler{}
|
||||||
|
if buildfeatures.HasC2N {
|
||||||
|
// Echo is the basic "ping" handler as used by the control plane to probe
|
||||||
|
// whether a node is reachable. In particular, it's important for
|
||||||
|
// high-availability subnet routers for the control plane to probe which of
|
||||||
|
// several candidate nodes is reachable and actually alive.
|
||||||
|
RegisterC2N("/echo", handleC2NEcho)
|
||||||
|
}
|
||||||
if buildfeatures.HasSSH {
|
if buildfeatures.HasSSH {
|
||||||
RegisterC2N("/ssh/usernames", handleC2NSSHUsernames)
|
RegisterC2N("/ssh/usernames", handleC2NSSHUsernames)
|
||||||
}
|
}
|
||||||
@@ -69,6 +74,9 @@ func init() {
|
|||||||
// A pattern is like "GET /foo" (specific to an HTTP method) or "/foo" (all
|
// A pattern is like "GET /foo" (specific to an HTTP method) or "/foo" (all
|
||||||
// methods). It panics if the pattern is already registered.
|
// methods). It panics if the pattern is already registered.
|
||||||
func RegisterC2N(pattern string, h func(*LocalBackend, http.ResponseWriter, *http.Request)) {
|
func RegisterC2N(pattern string, h func(*LocalBackend, http.ResponseWriter, *http.Request)) {
|
||||||
|
if !buildfeatures.HasC2N {
|
||||||
|
return
|
||||||
|
}
|
||||||
k := req(pattern)
|
k := req(pattern)
|
||||||
if _, ok := c2nHandlers[k]; ok {
|
if _, ok := c2nHandlers[k]; ok {
|
||||||
panic(fmt.Sprintf("c2n: duplicate handler for %q", pattern))
|
panic(fmt.Sprintf("c2n: duplicate handler for %q", pattern))
|
||||||
|
|||||||
@@ -550,10 +550,12 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||||||
// Following changes are triggered via the eventbus.
|
// Following changes are triggered via the eventbus.
|
||||||
b.linkChange(&netmon.ChangeDelta{New: netMon.InterfaceState()})
|
b.linkChange(&netmon.ChangeDelta{New: netMon.InterfaceState()})
|
||||||
|
|
||||||
if tunWrap, ok := b.sys.Tun.GetOK(); ok {
|
if buildfeatures.HasPeerAPIServer {
|
||||||
tunWrap.PeerAPIPort = b.GetPeerAPIPort
|
if tunWrap, ok := b.sys.Tun.GetOK(); ok {
|
||||||
} else {
|
tunWrap.PeerAPIPort = b.GetPeerAPIPort
|
||||||
b.logf("[unexpected] failed to wire up PeerAPI port for engine %T", e)
|
} else {
|
||||||
|
b.logf("[unexpected] failed to wire up PeerAPI port for engine %T", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if buildfeatures.HasDebug {
|
if buildfeatures.HasDebug {
|
||||||
@@ -972,15 +974,17 @@ func (b *LocalBackend) linkChange(delta *netmon.ChangeDelta) {
|
|||||||
b.updateFilterLocked(prefs)
|
b.updateFilterLocked(prefs)
|
||||||
updateExitNodeUsageWarning(prefs, delta.New, b.health)
|
updateExitNodeUsageWarning(prefs, delta.New, b.health)
|
||||||
|
|
||||||
cn := b.currentNode()
|
if buildfeatures.HasPeerAPIServer {
|
||||||
nm := cn.NetMap()
|
cn := b.currentNode()
|
||||||
if peerAPIListenAsync && nm != nil && b.state == ipn.Running {
|
nm := cn.NetMap()
|
||||||
want := nm.GetAddresses().Len()
|
if peerAPIListenAsync && nm != nil && b.state == ipn.Running {
|
||||||
have := len(b.peerAPIListeners)
|
want := nm.GetAddresses().Len()
|
||||||
b.logf("[v1] linkChange: have %d peerAPIListeners, want %d", have, want)
|
have := len(b.peerAPIListeners)
|
||||||
if have < want {
|
b.logf("[v1] linkChange: have %d peerAPIListeners, want %d", have, want)
|
||||||
b.logf("linkChange: peerAPIListeners too low; trying again")
|
if have < want {
|
||||||
b.goTracker.Go(b.initPeerAPIListener)
|
b.logf("linkChange: peerAPIListeners too low; trying again")
|
||||||
|
b.goTracker.Go(b.initPeerAPIListener)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1368,7 +1372,7 @@ func peerStatusFromNode(ps *ipnstate.PeerStatus, n tailcfg.NodeView) {
|
|||||||
ps.PublicKey = n.Key()
|
ps.PublicKey = n.Key()
|
||||||
ps.ID = n.StableID()
|
ps.ID = n.StableID()
|
||||||
ps.Created = n.Created()
|
ps.Created = n.Created()
|
||||||
ps.ExitNodeOption = tsaddr.ContainsExitRoutes(n.AllowedIPs())
|
ps.ExitNodeOption = buildfeatures.HasUseExitNode && tsaddr.ContainsExitRoutes(n.AllowedIPs())
|
||||||
if n.Tags().Len() != 0 {
|
if n.Tags().Len() != 0 {
|
||||||
v := n.Tags()
|
v := n.Tags()
|
||||||
ps.Tags = &v
|
ps.Tags = &v
|
||||||
@@ -1897,6 +1901,9 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if exitNodeIDStr, _ := b.polc.GetString(pkey.ExitNodeID, ""); exitNodeIDStr != "" {
|
if exitNodeIDStr, _ := b.polc.GetString(pkey.ExitNodeID, ""); exitNodeIDStr != "" {
|
||||||
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
||||||
|
|
||||||
@@ -2002,7 +2009,7 @@ func (b *LocalBackend) sysPolicyChanged(policy policyclient.PolicyChange) {
|
|||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if policy.HasChanged(pkey.AllowedSuggestedExitNodes) {
|
if buildfeatures.HasUseExitNode && policy.HasChanged(pkey.AllowedSuggestedExitNodes) {
|
||||||
b.refreshAllowedSuggestions()
|
b.refreshAllowedSuggestions()
|
||||||
// Re-evaluate exit node suggestion now that the policy setting has changed.
|
// Re-evaluate exit node suggestion now that the policy setting has changed.
|
||||||
if _, err := b.SuggestExitNode(); err != nil && !errors.Is(err, ErrNoPreferredDERP) {
|
if _, err := b.SuggestExitNode(); err != nil && !errors.Is(err, ErrNoPreferredDERP) {
|
||||||
@@ -2073,6 +2080,9 @@ func (b *LocalBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (handled bo
|
|||||||
// mustationsAreWorthyOfRecalculatingSuggestedExitNode reports whether any mutation type in muts is
|
// mustationsAreWorthyOfRecalculatingSuggestedExitNode reports whether any mutation type in muts is
|
||||||
// worthy of recalculating the suggested exit node.
|
// worthy of recalculating the suggested exit node.
|
||||||
func mutationsAreWorthyOfRecalculatingSuggestedExitNode(muts []netmap.NodeMutation, cn *nodeBackend, sid tailcfg.StableNodeID) bool {
|
func mutationsAreWorthyOfRecalculatingSuggestedExitNode(muts []netmap.NodeMutation, cn *nodeBackend, sid tailcfg.StableNodeID) bool {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
for _, m := range muts {
|
for _, m := range muts {
|
||||||
n, ok := cn.NodeByID(m.NodeIDBeingMutated())
|
n, ok := cn.NodeByID(m.NodeIDBeingMutated())
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -2126,6 +2136,9 @@ func mutationsAreWorthyOfTellingIPNBus(muts []netmap.NodeMutation) bool {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) resolveAutoExitNodeLocked(prefs *ipn.Prefs) (prefsChanged bool) {
|
func (b *LocalBackend) resolveAutoExitNodeLocked(prefs *ipn.Prefs) (prefsChanged bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
// As of 2025-07-08, the only supported auto exit node expression is [ipn.AnyExitNode].
|
// As of 2025-07-08, the only supported auto exit node expression is [ipn.AnyExitNode].
|
||||||
//
|
//
|
||||||
// However, to maintain forward compatibility with future auto exit node expressions,
|
// However, to maintain forward compatibility with future auto exit node expressions,
|
||||||
@@ -2170,6 +2183,9 @@ func (b *LocalBackend) resolveAutoExitNodeLocked(prefs *ipn.Prefs) (prefsChanged
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) resolveExitNodeIPLocked(prefs *ipn.Prefs) (prefsChanged bool) {
|
func (b *LocalBackend) resolveExitNodeIPLocked(prefs *ipn.Prefs) (prefsChanged bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
// If we have a desired IP on file, try to find the corresponding node.
|
// If we have a desired IP on file, try to find the corresponding node.
|
||||||
if !prefs.ExitNodeIP.IsValid() {
|
if !prefs.ExitNodeIP.IsValid() {
|
||||||
return false
|
return false
|
||||||
@@ -2455,6 +2471,11 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var c2nHandler http.Handler
|
||||||
|
if buildfeatures.HasC2N {
|
||||||
|
c2nHandler = http.HandlerFunc(b.handleC2N)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(apenwarr): The only way to change the ServerURL is to
|
// TODO(apenwarr): The only way to change the ServerURL is to
|
||||||
// re-run b.Start, because this is the only place we create a
|
// re-run b.Start, because this is the only place we create a
|
||||||
// new controlclient. EditPrefs allows you to overwrite ServerURL,
|
// new controlclient. EditPrefs allows you to overwrite ServerURL,
|
||||||
@@ -2475,7 +2496,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
PopBrowserURL: b.tellClientToBrowseToURL,
|
PopBrowserURL: b.tellClientToBrowseToURL,
|
||||||
Dialer: b.Dialer(),
|
Dialer: b.Dialer(),
|
||||||
Observer: b,
|
Observer: b,
|
||||||
C2NHandler: http.HandlerFunc(b.handleC2N),
|
C2NHandler: c2nHandler,
|
||||||
DialPlan: &b.dialPlan, // pointer because it can't be copied
|
DialPlan: &b.dialPlan, // pointer because it can't be copied
|
||||||
ControlKnobs: b.sys.ControlKnobs(),
|
ControlKnobs: b.sys.ControlKnobs(),
|
||||||
Shutdown: ccShutdown,
|
Shutdown: ccShutdown,
|
||||||
@@ -2623,31 +2644,33 @@ func (b *LocalBackend) updateFilterLocked(prefs ipn.PrefsView) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if prefs.Valid() {
|
if prefs.Valid() {
|
||||||
for _, r := range prefs.AdvertiseRoutes().All() {
|
if buildfeatures.HasAdvertiseRoutes {
|
||||||
if r.Bits() == 0 {
|
for _, r := range prefs.AdvertiseRoutes().All() {
|
||||||
// When offering a default route to the world, we
|
if r.Bits() == 0 {
|
||||||
// filter out locally reachable LANs, so that the
|
// When offering a default route to the world, we
|
||||||
// default route effectively appears to be a "guest
|
// filter out locally reachable LANs, so that the
|
||||||
// wifi": you get internet access, but to additionally
|
// default route effectively appears to be a "guest
|
||||||
// get LAN access the LAN(s) need to be offered
|
// wifi": you get internet access, but to additionally
|
||||||
// explicitly as well.
|
// get LAN access the LAN(s) need to be offered
|
||||||
localInterfaceRoutes, hostIPs, err := interfaceRoutes()
|
// explicitly as well.
|
||||||
if err != nil {
|
localInterfaceRoutes, hostIPs, err := interfaceRoutes()
|
||||||
b.logf("getting local interface routes: %v", err)
|
if err != nil {
|
||||||
continue
|
b.logf("getting local interface routes: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s, err := shrinkDefaultRoute(r, localInterfaceRoutes, hostIPs)
|
||||||
|
if err != nil {
|
||||||
|
b.logf("computing default route filter: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
localNetsB.AddSet(s)
|
||||||
|
} else {
|
||||||
|
localNetsB.AddPrefix(r)
|
||||||
|
// When advertising a non-default route, we assume
|
||||||
|
// this is a corporate subnet that should be present
|
||||||
|
// in the audit logs.
|
||||||
|
logNetsB.AddPrefix(r)
|
||||||
}
|
}
|
||||||
s, err := shrinkDefaultRoute(r, localInterfaceRoutes, hostIPs)
|
|
||||||
if err != nil {
|
|
||||||
b.logf("computing default route filter: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
localNetsB.AddSet(s)
|
|
||||||
} else {
|
|
||||||
localNetsB.AddPrefix(r)
|
|
||||||
// When advertising a non-default route, we assume
|
|
||||||
// this is a corporate subnet that should be present
|
|
||||||
// in the audit logs.
|
|
||||||
logNetsB.AddPrefix(r)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2658,7 +2681,7 @@ func (b *LocalBackend) updateFilterLocked(prefs ipn.PrefsView) {
|
|||||||
// The correct filter rules are synthesized by the coordination server
|
// The correct filter rules are synthesized by the coordination server
|
||||||
// and sent down, but the address needs to be part of the 'local net' for the
|
// and sent down, but the address needs to be part of the 'local net' for the
|
||||||
// filter package to even bother checking the filter rules, so we set them here.
|
// filter package to even bother checking the filter rules, so we set them here.
|
||||||
if prefs.AppConnector().Advertise {
|
if buildfeatures.HasAppConnectors && prefs.AppConnector().Advertise {
|
||||||
localNetsB.Add(netip.MustParseAddr("0.0.0.0"))
|
localNetsB.Add(netip.MustParseAddr("0.0.0.0"))
|
||||||
localNetsB.Add(netip.MustParseAddr("::0"))
|
localNetsB.Add(netip.MustParseAddr("::0"))
|
||||||
}
|
}
|
||||||
@@ -3712,6 +3735,9 @@ func (b *LocalBackend) Ping(ctx context.Context, ip netip.Addr, pingType tailcfg
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) pingPeerAPI(ctx context.Context, ip netip.Addr) (peer tailcfg.NodeView, peerBase string, err error) {
|
func (b *LocalBackend) pingPeerAPI(ctx context.Context, ip netip.Addr) (peer tailcfg.NodeView, peerBase string, err error) {
|
||||||
|
if !buildfeatures.HasPeerAPIClient {
|
||||||
|
return peer, peerBase, feature.ErrUnavailable
|
||||||
|
}
|
||||||
var zero tailcfg.NodeView
|
var zero tailcfg.NodeView
|
||||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -4051,6 +4077,9 @@ var exitNodeMisconfigurationWarnable = health.Register(&health.Warnable{
|
|||||||
// updateExitNodeUsageWarning updates a warnable meant to notify users of
|
// updateExitNodeUsageWarning updates a warnable meant to notify users of
|
||||||
// configuration issues that could break exit node usage.
|
// configuration issues that could break exit node usage.
|
||||||
func updateExitNodeUsageWarning(p ipn.PrefsView, state *netmon.State, healthTracker *health.Tracker) {
|
func updateExitNodeUsageWarning(p ipn.PrefsView, state *netmon.State, healthTracker *health.Tracker) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return
|
||||||
|
}
|
||||||
var msg string
|
var msg string
|
||||||
if p.ExitNodeIP().IsValid() || p.ExitNodeID() != "" {
|
if p.ExitNodeIP().IsValid() || p.ExitNodeID() != "" {
|
||||||
warn, _ := netutil.CheckReversePathFiltering(state)
|
warn, _ := netutil.CheckReversePathFiltering(state)
|
||||||
@@ -4070,6 +4099,9 @@ func (b *LocalBackend) checkExitNodePrefsLocked(p *ipn.Prefs) error {
|
|||||||
if !tryingToUseExitNode {
|
if !tryingToUseExitNode {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return feature.ErrUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
if err := featureknob.CanUseExitNode(); err != nil {
|
if err := featureknob.CanUseExitNode(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -4110,6 +4142,9 @@ func (b *LocalBackend) SetUseExitNodeEnabled(actor ipnauth.Actor, v bool) (ipn.P
|
|||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
p0 := b.pm.CurrentPrefs()
|
p0 := b.pm.CurrentPrefs()
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return p0, nil
|
||||||
|
}
|
||||||
if v && p0.ExitNodeID() != "" {
|
if v && p0.ExitNodeID() != "" {
|
||||||
// Already on.
|
// Already on.
|
||||||
return p0, nil
|
return p0, nil
|
||||||
@@ -4240,6 +4275,9 @@ func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) changeDisablesExitNodeLocked(prefs ipn.PrefsView, change *ipn.MaskedPrefs) bool {
|
func (b *LocalBackend) changeDisablesExitNodeLocked(prefs ipn.PrefsView, change *ipn.MaskedPrefs) bool {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if !change.AutoExitNodeSet && !change.ExitNodeIDSet && !change.ExitNodeIPSet {
|
if !change.AutoExitNodeSet && !change.ExitNodeIDSet && !change.ExitNodeIPSet {
|
||||||
// The change does not affect exit node usage.
|
// The change does not affect exit node usage.
|
||||||
return false
|
return false
|
||||||
@@ -4577,6 +4615,9 @@ func (b *LocalBackend) setPrefsLockedOnEntry(newp *ipn.Prefs, unlock unlockOnce)
|
|||||||
// GetPeerAPIPort returns the port number for the peerapi server
|
// GetPeerAPIPort returns the port number for the peerapi server
|
||||||
// running on the provided IP.
|
// running on the provided IP.
|
||||||
func (b *LocalBackend) GetPeerAPIPort(ip netip.Addr) (port uint16, ok bool) {
|
func (b *LocalBackend) GetPeerAPIPort(ip netip.Addr) (port uint16, ok bool) {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
for _, pln := range b.peerAPIListeners {
|
for _, pln := range b.peerAPIListeners {
|
||||||
@@ -4936,10 +4977,12 @@ func (b *LocalBackend) authReconfig() {
|
|||||||
// Keep the dialer updated about whether we're supposed to use
|
// Keep the dialer updated about whether we're supposed to use
|
||||||
// an exit node's DNS server (so SOCKS5/HTTP outgoing dials
|
// an exit node's DNS server (so SOCKS5/HTTP outgoing dials
|
||||||
// can use it for name resolution)
|
// can use it for name resolution)
|
||||||
if dohURLOK {
|
if buildfeatures.HasUseExitNode {
|
||||||
b.dialer.SetExitDNSDoH(dohURL)
|
if dohURLOK {
|
||||||
} else {
|
b.dialer.SetExitDNSDoH(dohURL)
|
||||||
b.dialer.SetExitDNSDoH("")
|
} else {
|
||||||
|
b.dialer.SetExitDNSDoH("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := nmcfg.WGCfg(nm, b.logf, flags, prefs.ExitNodeID())
|
cfg, err := nmcfg.WGCfg(nm, b.logf, flags, prefs.ExitNodeID())
|
||||||
@@ -5064,6 +5107,9 @@ func (b *LocalBackend) TailscaleVarRoot() string {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) closePeerAPIListenersLocked() {
|
func (b *LocalBackend) closePeerAPIListenersLocked() {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
return
|
||||||
|
}
|
||||||
b.peerAPIServer = nil
|
b.peerAPIServer = nil
|
||||||
for _, pln := range b.peerAPIListeners {
|
for _, pln := range b.peerAPIListeners {
|
||||||
pln.Close()
|
pln.Close()
|
||||||
@@ -5079,6 +5125,9 @@ func (b *LocalBackend) closePeerAPIListenersLocked() {
|
|||||||
const peerAPIListenAsync = runtime.GOOS == "windows" || runtime.GOOS == "android"
|
const peerAPIListenAsync = runtime.GOOS == "windows" || runtime.GOOS == "android"
|
||||||
|
|
||||||
func (b *LocalBackend) initPeerAPIListener() {
|
func (b *LocalBackend) initPeerAPIListener() {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
return
|
||||||
|
}
|
||||||
b.logf("[v1] initPeerAPIListener: entered")
|
b.logf("[v1] initPeerAPIListener: entered")
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
@@ -5903,6 +5952,9 @@ func (b *LocalBackend) setNetInfo(ni *tailcfg.NetInfo) {
|
|||||||
// RefreshExitNode determines which exit node to use based on the current
|
// RefreshExitNode determines which exit node to use based on the current
|
||||||
// prefs and netmap and switches to it if needed.
|
// prefs and netmap and switches to it if needed.
|
||||||
func (b *LocalBackend) RefreshExitNode() {
|
func (b *LocalBackend) RefreshExitNode() {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return
|
||||||
|
}
|
||||||
if b.resolveExitNode() {
|
if b.resolveExitNode() {
|
||||||
b.authReconfig()
|
b.authReconfig()
|
||||||
}
|
}
|
||||||
@@ -5918,6 +5970,9 @@ func (b *LocalBackend) RefreshExitNode() {
|
|||||||
//
|
//
|
||||||
// b.mu must not be held.
|
// b.mu must not be held.
|
||||||
func (b *LocalBackend) resolveExitNode() (changed bool) {
|
func (b *LocalBackend) resolveExitNode() (changed bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
@@ -6468,6 +6523,9 @@ func (b *LocalBackend) SetDeviceAttrs(ctx context.Context, attrs tailcfg.AttrUpd
|
|||||||
//
|
//
|
||||||
// If exitNodeID is the zero valid, it returns "", false.
|
// If exitNodeID is the zero valid, it returns "", false.
|
||||||
func exitNodeCanProxyDNS(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, exitNodeID tailcfg.StableNodeID) (dohURL string, ok bool) {
|
func exitNodeCanProxyDNS(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, exitNodeID tailcfg.StableNodeID) (dohURL string, ok bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
if exitNodeID.IsZero() {
|
if exitNodeID.IsZero() {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
@@ -7084,6 +7142,9 @@ var ErrNoPreferredDERP = errors.New("no preferred DERP, try again later")
|
|||||||
//
|
//
|
||||||
// b.mu.lock() must be held.
|
// b.mu.lock() must be held.
|
||||||
func (b *LocalBackend) suggestExitNodeLocked() (response apitype.ExitNodeSuggestionResponse, err error) {
|
func (b *LocalBackend) suggestExitNodeLocked() (response apitype.ExitNodeSuggestionResponse, err error) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return response, feature.ErrUnavailable
|
||||||
|
}
|
||||||
lastReport := b.MagicConn().GetLastNetcheckReport(b.ctx)
|
lastReport := b.MagicConn().GetLastNetcheckReport(b.ctx)
|
||||||
prevSuggestion := b.lastSuggestedExitNode
|
prevSuggestion := b.lastSuggestedExitNode
|
||||||
|
|
||||||
@@ -7101,6 +7162,9 @@ func (b *LocalBackend) suggestExitNodeLocked() (response apitype.ExitNodeSuggest
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) SuggestExitNode() (response apitype.ExitNodeSuggestionResponse, err error) {
|
func (b *LocalBackend) SuggestExitNode() (response apitype.ExitNodeSuggestionResponse, err error) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return response, feature.ErrUnavailable
|
||||||
|
}
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
return b.suggestExitNodeLocked()
|
return b.suggestExitNodeLocked()
|
||||||
@@ -7117,6 +7181,9 @@ func (b *LocalBackend) getAllowedSuggestions() set.Set[tailcfg.StableNodeID] {
|
|||||||
// refreshAllowedSuggestions rebuilds the set of permitted exit nodes
|
// refreshAllowedSuggestions rebuilds the set of permitted exit nodes
|
||||||
// from the current [pkey.AllowedSuggestedExitNodes] value.
|
// from the current [pkey.AllowedSuggestedExitNodes] value.
|
||||||
func (b *LocalBackend) refreshAllowedSuggestions() {
|
func (b *LocalBackend) refreshAllowedSuggestions() {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return
|
||||||
|
}
|
||||||
b.allowedSuggestedExitNodesMu.Lock()
|
b.allowedSuggestedExitNodesMu.Lock()
|
||||||
defer b.allowedSuggestedExitNodesMu.Unlock()
|
defer b.allowedSuggestedExitNodesMu.Unlock()
|
||||||
b.allowedSuggestedExitNodes = fillAllowedSuggestions(b.polc)
|
b.allowedSuggestedExitNodes = fillAllowedSuggestions(b.polc)
|
||||||
|
|||||||
@@ -530,6 +530,9 @@ func (nb *nodeBackend) dnsConfigForNetmap(prefs ipn.PrefsView, selfExpired bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (nb *nodeBackend) exitNodeCanProxyDNS(exitNodeID tailcfg.StableNodeID) (dohURL string, ok bool) {
|
func (nb *nodeBackend) exitNodeCanProxyDNS(exitNodeID tailcfg.StableNodeID) (dohURL string, ok bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
nb.mu.Lock()
|
nb.mu.Lock()
|
||||||
defer nb.mu.Unlock()
|
defer nb.mu.Unlock()
|
||||||
return exitNodeCanProxyDNS(nb.netMap, nb.peers, exitNodeID)
|
return exitNodeCanProxyDNS(nb.netMap, nb.peers, exitNodeID)
|
||||||
@@ -769,18 +772,20 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.
|
|||||||
// If we're using an exit node and that exit node is new enough (1.19.x+)
|
// If we're using an exit node and that exit node is new enough (1.19.x+)
|
||||||
// to run a DoH DNS proxy, then send all our DNS traffic through it,
|
// to run a DoH DNS proxy, then send all our DNS traffic through it,
|
||||||
// unless we find resolvers with UseWithExitNode set, in which case we use that.
|
// unless we find resolvers with UseWithExitNode set, in which case we use that.
|
||||||
if dohURL, ok := exitNodeCanProxyDNS(nm, peers, prefs.ExitNodeID()); ok {
|
if buildfeatures.HasUseExitNode {
|
||||||
filtered := useWithExitNodeResolvers(nm.DNS.Resolvers)
|
if dohURL, ok := exitNodeCanProxyDNS(nm, peers, prefs.ExitNodeID()); ok {
|
||||||
if len(filtered) > 0 {
|
filtered := useWithExitNodeResolvers(nm.DNS.Resolvers)
|
||||||
addDefault(filtered)
|
if len(filtered) > 0 {
|
||||||
} else {
|
addDefault(filtered)
|
||||||
// If no default global resolvers with the override
|
} else {
|
||||||
// are configured, configure the exit node's resolver.
|
// If no default global resolvers with the override
|
||||||
addDefault([]*dnstype.Resolver{{Addr: dohURL}})
|
// are configured, configure the exit node's resolver.
|
||||||
}
|
addDefault([]*dnstype.Resolver{{Addr: dohURL}})
|
||||||
|
}
|
||||||
|
|
||||||
addSplitDNSRoutes(useWithExitNodeRoutes(nm.DNS.Routes))
|
addSplitDNSRoutes(useWithExitNodeRoutes(nm.DNS.Routes))
|
||||||
return dcfg
|
return dcfg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has set default resolvers ("override local DNS"), prefer to
|
// If the user has set default resolvers ("override local DNS"), prefer to
|
||||||
@@ -788,7 +793,7 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.
|
|||||||
// node resolvers, use those as the default.
|
// node resolvers, use those as the default.
|
||||||
if len(nm.DNS.Resolvers) > 0 {
|
if len(nm.DNS.Resolvers) > 0 {
|
||||||
addDefault(nm.DNS.Resolvers)
|
addDefault(nm.DNS.Resolvers)
|
||||||
} else {
|
} else if buildfeatures.HasUseExitNode {
|
||||||
if resolvers, ok := wireguardExitNodeDNSResolvers(nm, peers, prefs.ExitNodeID()); ok {
|
if resolvers, ok := wireguardExitNodeDNSResolvers(nm, peers, prefs.ExitNodeID()); ok {
|
||||||
addDefault(resolvers)
|
addDefault(resolvers)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
"golang.org/x/net/http/httpguts"
|
"golang.org/x/net/http/httpguts"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/feature"
|
||||||
"tailscale.com/feature/buildfeatures"
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/hostinfo"
|
"tailscale.com/hostinfo"
|
||||||
@@ -131,6 +132,9 @@ type peerAPIListener struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pln *peerAPIListener) Close() error {
|
func (pln *peerAPIListener) Close() error {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if pln.ln != nil {
|
if pln.ln != nil {
|
||||||
return pln.ln.Close()
|
return pln.ln.Close()
|
||||||
}
|
}
|
||||||
@@ -138,6 +142,9 @@ func (pln *peerAPIListener) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pln *peerAPIListener) serve() {
|
func (pln *peerAPIListener) serve() {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
return
|
||||||
|
}
|
||||||
if pln.ln == nil {
|
if pln.ln == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -319,6 +326,9 @@ func peerAPIRequestShouldGetSecurityHeaders(r *http.Request) bool {
|
|||||||
//
|
//
|
||||||
// It panics if the path is already registered.
|
// It panics if the path is already registered.
|
||||||
func RegisterPeerAPIHandler(path string, f func(PeerAPIHandler, http.ResponseWriter, *http.Request)) {
|
func RegisterPeerAPIHandler(path string, f func(PeerAPIHandler, http.ResponseWriter, *http.Request)) {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
return
|
||||||
|
}
|
||||||
if _, ok := peerAPIHandlers[path]; ok {
|
if _, ok := peerAPIHandlers[path]; ok {
|
||||||
panic(fmt.Sprintf("duplicate PeerAPI handler %q", path))
|
panic(fmt.Sprintf("duplicate PeerAPI handler %q", path))
|
||||||
}
|
}
|
||||||
@@ -337,6 +347,10 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !buildfeatures.HasPeerAPIServer {
|
||||||
|
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := h.validatePeerAPIRequest(r); err != nil {
|
if err := h.validatePeerAPIRequest(r); err != nil {
|
||||||
metricInvalidRequests.Add(1)
|
metricInvalidRequests.Add(1)
|
||||||
h.logf("invalid request from %v: %v", h.remoteAddr, err)
|
h.logf("invalid request from %v: %v", h.remoteAddr, err)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package ipnlocal
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
@@ -85,6 +86,9 @@ func (e *prefsMetricsEditEvent) record() error {
|
|||||||
// false otherwise. The caller is responsible for ensuring that the id belongs to
|
// false otherwise. The caller is responsible for ensuring that the id belongs to
|
||||||
// an exit node.
|
// an exit node.
|
||||||
func (e *prefsMetricsEditEvent) exitNodeType(id tailcfg.StableNodeID) (props []exitNodeProperty, isNode bool) {
|
func (e *prefsMetricsEditEvent) exitNodeType(id tailcfg.StableNodeID) (props []exitNodeProperty, isNode bool) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
var peer tailcfg.NodeView
|
var peer tailcfg.NodeView
|
||||||
|
|
||||||
if peer, isNode = e.node.PeerByStableID(id); isNode {
|
if peer, isNode = e.node.PeerByStableID(id); isNode {
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ var handler = map[string]LocalAPIHandler{
|
|||||||
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
|
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
|
||||||
// without a trailing slash:
|
// without a trailing slash:
|
||||||
"alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs, // see tailscale/corp#24690
|
"alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs, // see tailscale/corp#24690
|
||||||
"bugreport": (*Handler).serveBugReport,
|
|
||||||
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
|
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
|
||||||
"check-prefs": (*Handler).serveCheckPrefs,
|
"check-prefs": (*Handler).serveCheckPrefs,
|
||||||
"check-reverse-path-filtering": (*Handler).serveCheckReversePathFiltering,
|
"check-reverse-path-filtering": (*Handler).serveCheckReversePathFiltering,
|
||||||
@@ -90,21 +89,17 @@ var handler = map[string]LocalAPIHandler{
|
|||||||
"logtap": (*Handler).serveLogTap,
|
"logtap": (*Handler).serveLogTap,
|
||||||
"metrics": (*Handler).serveMetrics,
|
"metrics": (*Handler).serveMetrics,
|
||||||
"ping": (*Handler).servePing,
|
"ping": (*Handler).servePing,
|
||||||
"pprof": (*Handler).servePprof,
|
|
||||||
"prefs": (*Handler).servePrefs,
|
"prefs": (*Handler).servePrefs,
|
||||||
"query-feature": (*Handler).serveQueryFeature,
|
"query-feature": (*Handler).serveQueryFeature,
|
||||||
"reload-config": (*Handler).reloadConfig,
|
"reload-config": (*Handler).reloadConfig,
|
||||||
"reset-auth": (*Handler).serveResetAuth,
|
"reset-auth": (*Handler).serveResetAuth,
|
||||||
"set-dns": (*Handler).serveSetDNS,
|
|
||||||
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
||||||
"set-gui-visible": (*Handler).serveSetGUIVisible,
|
"set-gui-visible": (*Handler).serveSetGUIVisible,
|
||||||
"set-push-device-token": (*Handler).serveSetPushDeviceToken,
|
"set-push-device-token": (*Handler).serveSetPushDeviceToken,
|
||||||
"set-udp-gro-forwarding": (*Handler).serveSetUDPGROForwarding,
|
"set-udp-gro-forwarding": (*Handler).serveSetUDPGROForwarding,
|
||||||
"set-use-exit-node-enabled": (*Handler).serveSetUseExitNodeEnabled,
|
|
||||||
"shutdown": (*Handler).serveShutdown,
|
"shutdown": (*Handler).serveShutdown,
|
||||||
"start": (*Handler).serveStart,
|
"start": (*Handler).serveStart,
|
||||||
"status": (*Handler).serveStatus,
|
"status": (*Handler).serveStatus,
|
||||||
"suggest-exit-node": (*Handler).serveSuggestExitNode,
|
|
||||||
"update/check": (*Handler).serveUpdateCheck,
|
"update/check": (*Handler).serveUpdateCheck,
|
||||||
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
|
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
|
||||||
"usermetrics": (*Handler).serveUserMetrics,
|
"usermetrics": (*Handler).serveUserMetrics,
|
||||||
@@ -116,6 +111,17 @@ func init() {
|
|||||||
if buildfeatures.HasAppConnectors {
|
if buildfeatures.HasAppConnectors {
|
||||||
Register("appc-route-info", (*Handler).serveGetAppcRouteInfo)
|
Register("appc-route-info", (*Handler).serveGetAppcRouteInfo)
|
||||||
}
|
}
|
||||||
|
if buildfeatures.HasUseExitNode {
|
||||||
|
Register("suggest-exit-node", (*Handler).serveSuggestExitNode)
|
||||||
|
Register("set-use-exit-node-enabled", (*Handler).serveSetUseExitNodeEnabled)
|
||||||
|
}
|
||||||
|
if buildfeatures.HasACME {
|
||||||
|
Register("set-dns", (*Handler).serveSetDNS)
|
||||||
|
}
|
||||||
|
if buildfeatures.HasDebug {
|
||||||
|
Register("bugreport", (*Handler).serveBugReport)
|
||||||
|
Register("pprof", (*Handler).servePprof)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register registers a new LocalAPI handler for the given name.
|
// Register registers a new LocalAPI handler for the given name.
|
||||||
@@ -1291,6 +1297,10 @@ func (h *Handler) serveSetGUIVisible(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) serveSetUseExitNodeEnabled(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) serveSetUseExitNodeEnabled(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
if r.Method != httpm.POST {
|
if r.Method != httpm.POST {
|
||||||
http.Error(w, "use POST", http.StatusMethodNotAllowed)
|
http.Error(w, "use POST", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
@@ -1629,6 +1639,10 @@ func dnsMessageTypeForString(s string) (t dnsmessage.Type, err error) {
|
|||||||
|
|
||||||
// serveSuggestExitNode serves a POST endpoint for returning a suggested exit node.
|
// serveSuggestExitNode serves a POST endpoint for returning a suggested exit node.
|
||||||
func (h *Handler) serveSuggestExitNode(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) serveSuggestExitNode(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
if r.Method != httpm.GET {
|
if r.Method != httpm.GET {
|
||||||
http.Error(w, "only GET allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "only GET allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/atomicfile"
|
"tailscale.com/atomicfile"
|
||||||
"tailscale.com/drive"
|
"tailscale.com/drive"
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/net/netaddr"
|
"tailscale.com/net/netaddr"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
@@ -787,6 +788,9 @@ func (p *Prefs) AdvertisesExitNode() bool {
|
|||||||
// SetAdvertiseExitNode mutates p (if non-nil) to add or remove the two
|
// SetAdvertiseExitNode mutates p (if non-nil) to add or remove the two
|
||||||
// /0 exit node routes.
|
// /0 exit node routes.
|
||||||
func (p *Prefs) SetAdvertiseExitNode(runExit bool) {
|
func (p *Prefs) SetAdvertiseExitNode(runExit bool) {
|
||||||
|
if !buildfeatures.HasAdvertiseExitNode {
|
||||||
|
return
|
||||||
|
}
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
dns "golang.org/x/net/dns/dnsmessage"
|
dns "golang.org/x/net/dns/dnsmessage"
|
||||||
"tailscale.com/control/controlknobs"
|
"tailscale.com/control/controlknobs"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/feature"
|
||||||
"tailscale.com/feature/buildfeatures"
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/net/dns/publicdns"
|
"tailscale.com/net/dns/publicdns"
|
||||||
@@ -530,6 +531,9 @@ func (f *forwarder) send(ctx context.Context, fq *forwardQuery, rr resolverAndDe
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(rr.name.Addr, "http://") {
|
if strings.HasPrefix(rr.name.Addr, "http://") {
|
||||||
|
if !buildfeatures.HasPeerAPIClient {
|
||||||
|
return nil, feature.ErrUnavailable
|
||||||
|
}
|
||||||
return f.sendDoH(ctx, rr.name.Addr, f.dialer.PeerAPIHTTPClient(), fq.packet)
|
return f.sendDoH(ctx, rr.name.Addr, f.dialer.PeerAPIHTTPClient(), fq.packet)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(rr.name.Addr, "https://") {
|
if strings.HasPrefix(rr.name.Addr, "https://") {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/mdlayher/netlink"
|
"github.com/mdlayher/netlink"
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/net/netaddr"
|
"tailscale.com/net/netaddr"
|
||||||
"tailscale.com/util/lineiter"
|
"tailscale.com/util/lineiter"
|
||||||
)
|
)
|
||||||
@@ -41,6 +42,9 @@ ens18 00000000 0100000A 0003 0 0 0 00000000
|
|||||||
ens18 0000000A 00000000 0001 0 0 0 0000FFFF 0 0 0
|
ens18 0000000A 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||||
*/
|
*/
|
||||||
func likelyHomeRouterIPLinux() (ret netip.Addr, myIP netip.Addr, ok bool) {
|
func likelyHomeRouterIPLinux() (ret netip.Addr, myIP netip.Addr, ok bool) {
|
||||||
|
if !buildfeatures.HasPortMapper {
|
||||||
|
return
|
||||||
|
}
|
||||||
if procNetRouteErr.Load() {
|
if procNetRouteErr.Load() {
|
||||||
// If we failed to read /proc/net/route previously, don't keep trying.
|
// If we failed to read /proc/net/route previously, don't keep trying.
|
||||||
return ret, myIP, false
|
return ret, myIP, false
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/eventbus"
|
"tailscale.com/util/eventbus"
|
||||||
@@ -181,6 +182,9 @@ func (m *Monitor) SetTailscaleInterfaceName(ifName string) {
|
|||||||
// It's the same as interfaces.LikelyHomeRouterIP, but it caches the
|
// It's the same as interfaces.LikelyHomeRouterIP, but it caches the
|
||||||
// result until the monitor detects a network change.
|
// result until the monitor detects a network change.
|
||||||
func (m *Monitor) GatewayAndSelfIP() (gw, myIP netip.Addr, ok bool) {
|
func (m *Monitor) GatewayAndSelfIP() (gw, myIP netip.Addr, ok bool) {
|
||||||
|
if !buildfeatures.HasPortMapper {
|
||||||
|
return
|
||||||
|
}
|
||||||
if m.static {
|
if m.static {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -573,6 +573,9 @@ var disableLikelyHomeRouterIPSelf = envknob.RegisterBool("TS_DEBUG_DISABLE_LIKEL
|
|||||||
// the LAN using that gateway.
|
// the LAN using that gateway.
|
||||||
// This is used as the destination for UPnP, NAT-PMP, PCP, etc queries.
|
// This is used as the destination for UPnP, NAT-PMP, PCP, etc queries.
|
||||||
func LikelyHomeRouterIP() (gateway, myIP netip.Addr, ok bool) {
|
func LikelyHomeRouterIP() (gateway, myIP netip.Addr, ok bool) {
|
||||||
|
if !buildfeatures.HasPortMapper {
|
||||||
|
return
|
||||||
|
}
|
||||||
// If we don't have a way to get the home router IP, then we can't do
|
// If we don't have a way to get the home router IP, then we can't do
|
||||||
// anything; just return.
|
// anything; just return.
|
||||||
if likelyHomeRouterIP == nil {
|
if likelyHomeRouterIP == nil {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/net/netaddr"
|
"tailscale.com/net/netaddr"
|
||||||
"tailscale.com/net/neterror"
|
"tailscale.com/net/neterror"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
@@ -262,10 +263,13 @@ func NewClient(c Config) *Client {
|
|||||||
panic("nil EventBus")
|
panic("nil EventBus")
|
||||||
}
|
}
|
||||||
ret := &Client{
|
ret := &Client{
|
||||||
logf: c.Logf,
|
logf: c.Logf,
|
||||||
netMon: c.NetMon,
|
netMon: c.NetMon,
|
||||||
ipAndGateway: netmon.LikelyHomeRouterIP, // TODO(bradfitz): move this to method on netMon
|
onChange: c.OnChange,
|
||||||
onChange: c.OnChange,
|
}
|
||||||
|
if buildfeatures.HasPortMapper {
|
||||||
|
// TODO(bradfitz): move this to method on netMon
|
||||||
|
ret.ipAndGateway = netmon.LikelyHomeRouterIP
|
||||||
}
|
}
|
||||||
ret.pubClient = c.EventBus.Client("portmapper")
|
ret.pubClient = c.EventBus.Client("portmapper")
|
||||||
ret.updates = eventbus.Publish[portmappertype.Mapping](ret.pubClient)
|
ret.updates = eventbus.Publish[portmappertype.Mapping](ret.pubClient)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
|
"tailscale.com/feature"
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
"tailscale.com/net/dnscache"
|
"tailscale.com/net/dnscache"
|
||||||
"tailscale.com/net/netknob"
|
"tailscale.com/net/netknob"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
@@ -135,6 +137,9 @@ func (d *Dialer) TUNName() string {
|
|||||||
//
|
//
|
||||||
// For example, "http://100.68.82.120:47830/dns-query".
|
// For example, "http://100.68.82.120:47830/dns-query".
|
||||||
func (d *Dialer) SetExitDNSDoH(doh string) {
|
func (d *Dialer) SetExitDNSDoH(doh string) {
|
||||||
|
if !buildfeatures.HasUseExitNode {
|
||||||
|
return
|
||||||
|
}
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
if d.exitDNSDoHBase == doh {
|
if d.exitDNSDoHBase == doh {
|
||||||
@@ -372,7 +377,7 @@ func (d *Dialer) userDialResolve(ctx context.Context, network, addr string) (net
|
|||||||
}
|
}
|
||||||
|
|
||||||
var r net.Resolver
|
var r net.Resolver
|
||||||
if exitDNSDoH != "" {
|
if buildfeatures.HasUseExitNode && buildfeatures.HasPeerAPIClient && exitDNSDoH != "" {
|
||||||
r.PreferGo = true
|
r.PreferGo = true
|
||||||
r.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
|
r.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
return &dohConn{
|
return &dohConn{
|
||||||
@@ -509,6 +514,9 @@ func (d *Dialer) UserDial(ctx context.Context, network, addr string) (net.Conn,
|
|||||||
// network must a "tcp" type, and addr must be an ip:port. Name resolution
|
// network must a "tcp" type, and addr must be an ip:port. Name resolution
|
||||||
// is not supported.
|
// is not supported.
|
||||||
func (d *Dialer) dialPeerAPI(ctx context.Context, network, addr string) (net.Conn, error) {
|
func (d *Dialer) dialPeerAPI(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
if !buildfeatures.HasPeerAPIClient {
|
||||||
|
return nil, feature.ErrUnavailable
|
||||||
|
}
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp", "tcp6", "tcp4":
|
case "tcp", "tcp6", "tcp4":
|
||||||
default:
|
default:
|
||||||
@@ -551,6 +559,9 @@ func (d *Dialer) getPeerDialer() *net.Dialer {
|
|||||||
// The returned Client must not be mutated; it's owned by the Dialer
|
// The returned Client must not be mutated; it's owned by the Dialer
|
||||||
// and shared by callers.
|
// and shared by callers.
|
||||||
func (d *Dialer) PeerAPIHTTPClient() *http.Client {
|
func (d *Dialer) PeerAPIHTTPClient() *http.Client {
|
||||||
|
if !buildfeatures.HasPeerAPIClient {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
d.peerClientOnce.Do(func() {
|
d.peerClientOnce.Do(func() {
|
||||||
t := http.DefaultTransport.(*http.Transport).Clone()
|
t := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
t.Dial = nil
|
t.Dial = nil
|
||||||
|
|||||||
Reference in New Issue
Block a user