feature, net/tshttpproxy: pull out support for using proxies as a feature

Saves 139 KB.

Also Synology support, which I saw had its own large-ish proxy parsing
support on Linux, but support for proxies without Synology proxy
support is reasonable, so I pulled that out as its own thing.

Updates #12614

Change-Id: I22de285a3def7be77fdcf23e2bec7c83c9655593
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2025-09-30 09:12:42 -07:00 committed by Brad Fitzpatrick
parent 9b997c8f2f
commit 442a3a779d
45 changed files with 267 additions and 79 deletions

View File

@ -27,6 +27,7 @@ import (
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/envknob/featureknob" "tailscale.com/envknob/featureknob"
"tailscale.com/feature" "tailscale.com/feature"
"tailscale.com/feature/buildfeatures"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
@ -496,6 +497,10 @@ func (s *Server) authorizeRequest(w http.ResponseWriter, r *http.Request) (ok bo
// Client using system-specific auth. // Client using system-specific auth.
switch distro.Get() { switch distro.Get() {
case distro.Synology: case distro.Synology:
if !buildfeatures.HasSynology {
// Synology support not built in.
return false
}
authorized, _ := authorizeSynology(r) authorized, _ := authorizeSynology(r)
return authorized return authorized
case distro.QNAP: case distro.QNAP:

View File

@ -55,7 +55,7 @@ import (
"github.com/hdevalence/ed25519consensus" "github.com/hdevalence/ed25519consensus"
"golang.org/x/crypto/blake2s" "golang.org/x/crypto/blake2s"
"tailscale.com/net/tshttpproxy" "tailscale.com/feature"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/httpm" "tailscale.com/util/httpm"
"tailscale.com/util/must" "tailscale.com/util/must"
@ -330,7 +330,7 @@ func fetch(url string, limit int64) ([]byte, error) {
// limit bytes. On success, the returned value is a BLAKE2s hash of the file. // limit bytes. On success, the returned value is a BLAKE2s hash of the file.
func (c *Client) download(ctx context.Context, url, dst string, limit int64) ([]byte, int64, error) { func (c *Client) download(ctx context.Context, url, dst string, limit int64) ([]byte, int64, error) {
tr := http.DefaultTransport.(*http.Transport).Clone() tr := http.DefaultTransport.(*http.Transport).Clone()
tr.Proxy = tshttpproxy.ProxyFromEnvironment tr.Proxy = feature.HookProxyFromEnvironment.GetOrNil()
defer tr.CloseIdleConnections() defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr} hc := &http.Client{Transport: tr}

View File

@ -2,16 +2,13 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519 filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
github.com/beorn7/perks/quantile from github.com/prometheus/client_golang/prometheus github.com/beorn7/perks/quantile from github.com/prometheus/client_golang/prometheus
💣 github.com/cespare/xxhash/v2 from github.com/prometheus/client_golang/prometheus 💣 github.com/cespare/xxhash/v2 from github.com/prometheus/client_golang/prometheus
github.com/coder/websocket from tailscale.com/cmd/derper+ github.com/coder/websocket from tailscale.com/cmd/derper+
github.com/coder/websocket/internal/errd from github.com/coder/websocket github.com/coder/websocket/internal/errd from github.com/coder/websocket
github.com/coder/websocket/internal/util from github.com/coder/websocket github.com/coder/websocket/internal/util from github.com/coder/websocket
github.com/coder/websocket/internal/xsync from github.com/coder/websocket github.com/coder/websocket/internal/xsync from github.com/coder/websocket
W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil+ W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil
github.com/fxamacker/cbor/v2 from tailscale.com/tka github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/go-json-experiment/json from tailscale.com/types/opt+ github.com/go-json-experiment/json from tailscale.com/types/opt+
github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+ github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+
@ -89,7 +86,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/drive from tailscale.com/client/local+ tailscale.com/drive from tailscale.com/client/local+
tailscale.com/envknob from tailscale.com/client/local+ tailscale.com/envknob from tailscale.com/client/local+
tailscale.com/feature from tailscale.com/tsweb+ tailscale.com/feature from tailscale.com/tsweb+
tailscale.com/feature/buildfeatures from tailscale.com/feature tailscale.com/feature/buildfeatures from tailscale.com/feature+
tailscale.com/health from tailscale.com/net/tlsdial+ tailscale.com/health from tailscale.com/net/tlsdial+
tailscale.com/hostinfo from tailscale.com/net/netmon+ tailscale.com/hostinfo from tailscale.com/net/netmon+
tailscale.com/ipn from tailscale.com/client/local tailscale.com/ipn from tailscale.com/client/local
@ -113,7 +110,6 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/net/tlsdial from tailscale.com/derp/derphttp tailscale.com/net/tlsdial from tailscale.com/derp/derphttp
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/ipn+ tailscale.com/net/tsaddr from tailscale.com/ipn+
💣 tailscale.com/net/tshttpproxy from tailscale.com/derp/derphttp+
tailscale.com/net/udprelay/status from tailscale.com/client/local tailscale.com/net/udprelay/status from tailscale.com/client/local
tailscale.com/net/wsconn from tailscale.com/cmd/derper tailscale.com/net/wsconn from tailscale.com/cmd/derper
tailscale.com/paths from tailscale.com/client/local tailscale.com/paths from tailscale.com/client/local
@ -146,7 +142,6 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/util/cibuild from tailscale.com/health tailscale.com/util/cibuild from tailscale.com/health
tailscale.com/util/clientmetric from tailscale.com/net/netmon+ tailscale.com/util/clientmetric from tailscale.com/net/netmon+
tailscale.com/util/cloudenv from tailscale.com/hostinfo+ tailscale.com/util/cloudenv from tailscale.com/hostinfo+
W tailscale.com/util/cmpver from tailscale.com/net/tshttpproxy
tailscale.com/util/ctxkey from tailscale.com/tsweb+ tailscale.com/util/ctxkey from tailscale.com/tsweb+
💣 tailscale.com/util/deephash from tailscale.com/util/syspolicy/setting 💣 tailscale.com/util/deephash from tailscale.com/util/syspolicy/setting
L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics
@ -195,7 +190,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
L golang.org/x/net/bpf from github.com/mdlayher/netlink+ L golang.org/x/net/bpf from github.com/mdlayher/netlink+
golang.org/x/net/dns/dnsmessage from net+ golang.org/x/net/dns/dnsmessage from net+
golang.org/x/net/http/httpguts from net/http+ golang.org/x/net/http/httpguts from net/http+
golang.org/x/net/http/httpproxy from net/http+ golang.org/x/net/http/httpproxy from net/http
golang.org/x/net/http2/hpack from net/http+ golang.org/x/net/http2/hpack from net/http+
golang.org/x/net/idna from golang.org/x/crypto/acme/autocert+ golang.org/x/net/idna from golang.org/x/crypto/acme/autocert+
golang.org/x/net/internal/socks from golang.org/x/net/proxy golang.org/x/net/internal/socks from golang.org/x/net/proxy

View File

@ -701,9 +701,11 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+ tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet
tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet
tailscale.com/feature/condregister/useproxy from tailscale.com/tsnet
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/syspolicy from tailscale.com/logpolicy tailscale.com/feature/syspolicy from tailscale.com/logpolicy
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
tailscale.com/hostinfo from tailscale.com/client/web+ tailscale.com/hostinfo from tailscale.com/client/web+
@ -777,7 +779,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/client/web+ tailscale.com/net/tsaddr from tailscale.com/client/web+
tailscale.com/net/tsdial from tailscale.com/control/controlclient+ tailscale.com/net/tsdial from tailscale.com/control/controlclient+
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+ 💣 tailscale.com/net/tshttpproxy from tailscale.com/feature/useproxy
tailscale.com/net/tstun from tailscale.com/tsd+ tailscale.com/net/tstun from tailscale.com/tsd+
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
tailscale.com/net/udprelay/status from tailscale.com/client/local tailscale.com/net/udprelay/status from tailscale.com/client/local

View File

@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS // Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
//go:build linux && !ts_omit_acme //go:build linux && !ts_omit_acme && !ts_omit_synology
package cli package cli

View File

@ -28,17 +28,17 @@ import (
"time" "time"
"github.com/peterbourgon/ff/v3/ffcli" "github.com/peterbourgon/ff/v3/ffcli"
"golang.org/x/net/http/httpproxy"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"tailscale.com/client/tailscale/apitype" "tailscale.com/client/tailscale/apitype"
"tailscale.com/control/controlhttp" "tailscale.com/control/controlhttp"
"tailscale.com/feature"
_ "tailscale.com/feature/condregister/useproxy"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/internal/noiseconn" "tailscale.com/internal/noiseconn"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/net/ace" "tailscale.com/net/ace"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/net/tshttpproxy"
"tailscale.com/paths" "tailscale.com/paths"
"tailscale.com/safesocket" "tailscale.com/safesocket"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -992,15 +992,11 @@ func runTS2021(ctx context.Context, args []string) error {
if err != nil { if err != nil {
return err return err
} }
envConf := httpproxy.FromEnvironment() if proxyFromEnv, ok := feature.HookProxyFromEnvironment.GetOk(); ok {
if *envConf == (httpproxy.Config{}) { proxy, err := proxyFromEnv(&http.Request{URL: u})
log.Printf("HTTP proxy env: (none)")
} else {
log.Printf("HTTP proxy env: %+v", envConf)
}
proxy, err := tshttpproxy.ProxyFromEnvironment(&http.Request{URL: u})
log.Printf("tshttpproxy.ProxyFromEnvironment = (%v, %v)", proxy, err) log.Printf("tshttpproxy.ProxyFromEnvironment = (%v, %v)", proxy, err)
} }
}
machinePrivate := key.NewMachine() machinePrivate := key.NewMachine()
var dialer net.Dialer var dialer net.Dialer

View File

@ -96,9 +96,11 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/feature/capture/dissector from tailscale.com/cmd/tailscale/cli tailscale.com/feature/capture/dissector from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/condregister/oauthkey from tailscale.com/cmd/tailscale/cli tailscale.com/feature/condregister/oauthkey from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/condregister/portmapper from tailscale.com/cmd/tailscale/cli tailscale.com/feature/condregister/portmapper from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/condregister/useproxy from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/syspolicy from tailscale.com/cmd/tailscale/cli tailscale.com/feature/syspolicy from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/health from tailscale.com/net/tlsdial+ tailscale.com/health from tailscale.com/net/tlsdial+
tailscale.com/health/healthmsg from tailscale.com/cmd/tailscale/cli tailscale.com/health/healthmsg from tailscale.com/cmd/tailscale/cli
tailscale.com/hostinfo from tailscale.com/client/web+ tailscale.com/hostinfo from tailscale.com/client/web+
@ -130,7 +132,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/net/tlsdial from tailscale.com/cmd/tailscale/cli+ tailscale.com/net/tlsdial from tailscale.com/cmd/tailscale/cli+
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/client/web+ tailscale.com/net/tsaddr from tailscale.com/client/web+
💣 tailscale.com/net/tshttpproxy from tailscale.com/clientupdate/distsign+ 💣 tailscale.com/net/tshttpproxy from tailscale.com/feature/useproxy
tailscale.com/net/udprelay/status from tailscale.com/client/local+ tailscale.com/net/udprelay/status from tailscale.com/client/local+
tailscale.com/paths from tailscale.com/client/local+ tailscale.com/paths from tailscale.com/client/local+
💣 tailscale.com/safesocket from tailscale.com/client/local+ 💣 tailscale.com/safesocket from tailscale.com/client/local+

View File

@ -21,10 +21,11 @@ import (
"time" "time"
"tailscale.com/derp/derphttp" "tailscale.com/derp/derphttp"
"tailscale.com/feature"
"tailscale.com/feature/buildfeatures"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/net/tshttpproxy"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/util/eventbus" "tailscale.com/util/eventbus"
@ -124,10 +125,15 @@ func getURL(ctx context.Context, urlStr string) error {
if err != nil { if err != nil {
return fmt.Errorf("http.NewRequestWithContext: %v", err) return fmt.Errorf("http.NewRequestWithContext: %v", err)
} }
proxyURL, err := tshttpproxy.ProxyFromEnvironment(req) var proxyURL *url.URL
if buildfeatures.HasUseProxy {
if proxyFromEnv, ok := feature.HookProxyFromEnvironment.GetOk(); ok {
proxyURL, err = proxyFromEnv(req)
if err != nil { if err != nil {
return fmt.Errorf("tshttpproxy.ProxyFromEnvironment: %v", err) return fmt.Errorf("tshttpproxy.ProxyFromEnvironment: %v", err)
} }
}
}
log.Printf("proxy: %v", proxyURL) log.Printf("proxy: %v", proxyURL)
tr := &http.Transport{ tr := &http.Transport{
Proxy: func(*http.Request) (*url.URL, error) { return proxyURL, nil }, Proxy: func(*http.Request) (*url.URL, error) { return proxyURL, nil },
@ -135,7 +141,10 @@ func getURL(ctx context.Context, urlStr string) error {
DisableKeepAlives: true, DisableKeepAlives: true,
} }
if proxyURL != nil { if proxyURL != nil {
auth, err := tshttpproxy.GetAuthHeader(proxyURL) var auth string
if f, ok := feature.HookProxyGetAuthHeader.GetOk(); ok {
auth, err = f(proxyURL)
}
if err == nil && auth != "" { if err == nil && auth != "" {
tr.ProxyConnectHeader.Set("Proxy-Authorization", auth) tr.ProxyConnectHeader.Set("Proxy-Authorization", auth)
} }

View File

@ -57,6 +57,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/buildfeatures from tailscale.com/cmd/tailscaled+ tailscale.com/feature/buildfeatures from tailscale.com/cmd/tailscaled+
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister
tailscale.com/feature/condregister/useproxy from tailscale.com/feature/condregister
tailscale.com/health from tailscale.com/cmd/tailscaled+ tailscale.com/health from tailscale.com/cmd/tailscaled+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
tailscale.com/hostinfo from tailscale.com/cmd/tailscaled+ tailscale.com/hostinfo from tailscale.com/cmd/tailscaled+
@ -110,7 +111,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/ipn+ tailscale.com/net/tsaddr from tailscale.com/ipn+
tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+ tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+
tailscale.com/net/tshttpproxy from tailscale.com/cmd/tailscaled+
tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+ tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
tailscale.com/omit from tailscale.com/ipn/conffile tailscale.com/omit from tailscale.com/ipn/conffile
@ -219,7 +219,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/net/bpf from github.com/mdlayher/genetlink+ golang.org/x/net/bpf from github.com/mdlayher/genetlink+
golang.org/x/net/dns/dnsmessage from net+ golang.org/x/net/dns/dnsmessage from net+
golang.org/x/net/http/httpguts from golang.org/x/net/http2+ golang.org/x/net/http/httpguts from golang.org/x/net/http2+
golang.org/x/net/http/httpproxy from net/http+ golang.org/x/net/http/httpproxy from net/http
golang.org/x/net/http2 from tailscale.com/control/controlclient+ golang.org/x/net/http2 from tailscale.com/control/controlclient+
golang.org/x/net/http2/hpack from golang.org/x/net/http2+ golang.org/x/net/http2/hpack from golang.org/x/net/http2+
golang.org/x/net/icmp from tailscale.com/net/ping golang.org/x/net/icmp from tailscale.com/net/ping

View File

@ -78,6 +78,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
tailscale.com/feature/condregister/oauthkey from tailscale.com/cmd/tailscale/cli tailscale.com/feature/condregister/oauthkey from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister+ tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister+
tailscale.com/feature/condregister/useproxy from tailscale.com/cmd/tailscale/cli+
tailscale.com/health from tailscale.com/cmd/tailscaled+ tailscale.com/health from tailscale.com/cmd/tailscaled+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
tailscale.com/hostinfo from tailscale.com/cmd/tailscaled+ tailscale.com/hostinfo from tailscale.com/cmd/tailscaled+
@ -133,7 +134,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/ipn+ tailscale.com/net/tsaddr from tailscale.com/ipn+
tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+ tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+
tailscale.com/net/tshttpproxy from tailscale.com/cmd/tailscaled+
tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+ tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
tailscale.com/net/udprelay/status from tailscale.com/client/local tailscale.com/net/udprelay/status from tailscale.com/client/local
@ -246,7 +246,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/net/bpf from github.com/mdlayher/genetlink+ golang.org/x/net/bpf from github.com/mdlayher/genetlink+
golang.org/x/net/dns/dnsmessage from net+ golang.org/x/net/dns/dnsmessage from net+
golang.org/x/net/http/httpguts from golang.org/x/net/http2+ golang.org/x/net/http/httpguts from golang.org/x/net/http2+
golang.org/x/net/http/httpproxy from net/http+ golang.org/x/net/http/httpproxy from net/http
golang.org/x/net/http2 from tailscale.com/cmd/tailscale/cli+ golang.org/x/net/http2 from tailscale.com/cmd/tailscale/cli+
golang.org/x/net/http2/hpack from golang.org/x/net/http2+ golang.org/x/net/http2/hpack from golang.org/x/net/http2+
golang.org/x/net/icmp from tailscale.com/net/ping golang.org/x/net/icmp from tailscale.com/net/ping

View File

@ -276,6 +276,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/clientupdate from tailscale.com/feature/condregister tailscale.com/feature/clientupdate from tailscale.com/feature/condregister
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister
tailscale.com/feature/condregister/useproxy from tailscale.com/feature/condregister
tailscale.com/feature/debugportmapper from tailscale.com/feature/condregister tailscale.com/feature/debugportmapper from tailscale.com/feature/condregister
tailscale.com/feature/doctor from tailscale.com/feature/condregister tailscale.com/feature/doctor from tailscale.com/feature/condregister
tailscale.com/feature/drive from tailscale.com/feature/condregister tailscale.com/feature/drive from tailscale.com/feature/condregister
@ -289,6 +290,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/taildrop from tailscale.com/feature/condregister tailscale.com/feature/taildrop from tailscale.com/feature/condregister
L tailscale.com/feature/tap from tailscale.com/feature/condregister L tailscale.com/feature/tap from tailscale.com/feature/condregister
tailscale.com/feature/tpm from tailscale.com/feature/condregister tailscale.com/feature/tpm from tailscale.com/feature/condregister
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister
tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
@ -357,7 +359,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/client/web+ tailscale.com/net/tsaddr from tailscale.com/client/web+
tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+ tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+
💣 tailscale.com/net/tshttpproxy from tailscale.com/clientupdate/distsign+ 💣 tailscale.com/net/tshttpproxy from tailscale.com/feature/useproxy
tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+ tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+
tailscale.com/net/udprelay from tailscale.com/feature/relayserver tailscale.com/net/udprelay from tailscale.com/feature/relayserver
tailscale.com/net/udprelay/endpoint from tailscale.com/feature/relayserver+ tailscale.com/net/udprelay/endpoint from tailscale.com/feature/relayserver+

View File

@ -222,3 +222,16 @@ func TestOmitGRO(t *testing.T) {
}, },
}.Check(t) }.Check(t)
} }
func TestOmitUseProxy(t *testing.T) {
deptest.DepChecker{
GOOS: "linux",
GOARCH: "amd64",
Tags: "ts_omit_useproxy,ts_include_cli",
OnDep: func(dep string) {
if strings.Contains(dep, "tshttproxy") {
t.Errorf("unexpected dep: %q", dep)
}
},
}.Check(t)
}

View File

@ -17,10 +17,10 @@ import (
"net/http/httputil" "net/http/httputil"
"strings" "strings"
"tailscale.com/feature"
"tailscale.com/net/proxymux" "tailscale.com/net/proxymux"
"tailscale.com/net/socks5" "tailscale.com/net/socks5"
"tailscale.com/net/tsdial" "tailscale.com/net/tsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
@ -104,7 +104,9 @@ func mkProxyStartFunc(socksListener, httpListener net.Listener) proxyStartFunc {
}() }()
addrs = append(addrs, socksListener.Addr().String()) addrs = append(addrs, socksListener.Addr().String())
} }
tshttpproxy.SetSelfProxy(addrs...) if set, ok := feature.HookProxySetSelfProxy.GetOk(); ok {
set(addrs...)
}
} }
} }

View File

@ -143,9 +143,11 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+ tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet
tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet
tailscale.com/feature/condregister/useproxy from tailscale.com/tsnet
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/syspolicy from tailscale.com/logpolicy tailscale.com/feature/syspolicy from tailscale.com/logpolicy
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
tailscale.com/hostinfo from tailscale.com/client/web+ tailscale.com/hostinfo from tailscale.com/client/web+
@ -205,7 +207,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/client/web+ tailscale.com/net/tsaddr from tailscale.com/client/web+
tailscale.com/net/tsdial from tailscale.com/control/controlclient+ tailscale.com/net/tsdial from tailscale.com/control/controlclient+
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+ 💣 tailscale.com/net/tshttpproxy from tailscale.com/feature/useproxy
tailscale.com/net/tstun from tailscale.com/tsd+ tailscale.com/net/tstun from tailscale.com/tsd+
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
tailscale.com/net/udprelay/status from tailscale.com/client/local tailscale.com/net/udprelay/status from tailscale.com/client/local

View File

@ -42,7 +42,6 @@ import (
"tailscale.com/net/netx" "tailscale.com/net/netx"
"tailscale.com/net/tlsdial" "tailscale.com/net/tlsdial"
"tailscale.com/net/tsdial" "tailscale.com/net/tsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tempfork/httprec" "tailscale.com/tempfork/httprec"
"tailscale.com/tka" "tailscale.com/tka"
@ -275,8 +274,12 @@ func NewDirect(opts Options) (*Direct, error) {
var interceptedDial *atomic.Bool var interceptedDial *atomic.Bool
if httpc == nil { if httpc == nil {
tr := http.DefaultTransport.(*http.Transport).Clone() tr := http.DefaultTransport.(*http.Transport).Clone()
tr.Proxy = tshttpproxy.ProxyFromEnvironment if buildfeatures.HasUseProxy {
tshttpproxy.SetTransportGetProxyConnectHeader(tr) tr.Proxy = feature.HookProxyFromEnvironment.GetOrNil()
if f, ok := feature.HookProxySetTransportGetProxyConnectHeader.GetOk(); ok {
f(tr)
}
}
tr.TLSClientConfig = tlsdial.Config(opts.HealthTracker, tr.TLSClientConfig) tr.TLSClientConfig = tlsdial.Config(opts.HealthTracker, tr.TLSClientConfig)
var dialFunc netx.DialFunc var dialFunc netx.DialFunc
dialFunc, interceptedDial = makeScreenTimeDetectingDialFunc(opts.Dialer.SystemDial) dialFunc, interceptedDial = makeScreenTimeDetectingDialFunc(opts.Dialer.SystemDial)

View File

@ -39,6 +39,8 @@ import (
"tailscale.com/control/controlbase" "tailscale.com/control/controlbase"
"tailscale.com/control/controlhttp/controlhttpcommon" "tailscale.com/control/controlhttp/controlhttpcommon"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/feature"
"tailscale.com/feature/buildfeatures"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/net/ace" "tailscale.com/net/ace"
"tailscale.com/net/dnscache" "tailscale.com/net/dnscache"
@ -47,7 +49,6 @@ import (
"tailscale.com/net/netx" "tailscale.com/net/netx"
"tailscale.com/net/sockstats" "tailscale.com/net/sockstats"
"tailscale.com/net/tlsdial" "tailscale.com/net/tlsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/syncs" "tailscale.com/syncs"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tstime" "tailscale.com/tstime"
@ -81,7 +82,7 @@ func (a *Dialer) getProxyFunc() func(*http.Request) (*url.URL, error) {
if a.proxyFunc != nil { if a.proxyFunc != nil {
return a.proxyFunc return a.proxyFunc
} }
return tshttpproxy.ProxyFromEnvironment return feature.HookProxyFromEnvironment.GetOrNil()
} }
// httpsFallbackDelay is how long we'll wait for a.HTTPPort to work before // httpsFallbackDelay is how long we'll wait for a.HTTPPort to work before
@ -463,8 +464,12 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, optAddr netip.Ad
tr.Proxy = nil tr.Proxy = nil
tr.DialContext = dialer tr.DialContext = dialer
} else { } else {
if buildfeatures.HasUseProxy {
tr.Proxy = a.getProxyFunc() tr.Proxy = a.getProxyFunc()
tshttpproxy.SetTransportGetProxyConnectHeader(tr) if set, ok := feature.HookProxySetTransportGetProxyConnectHeader.GetOk(); ok {
set(tr)
}
}
tr.DialContext = dnscache.Dialer(dialer, dns) tr.DialContext = dnscache.Dialer(dialer, dns)
} }
// Disable HTTP2, since h2 can't do protocol switching. // Disable HTTP2, since h2 can't do protocol switching.

View File

@ -32,6 +32,8 @@ import (
"tailscale.com/derp" "tailscale.com/derp"
"tailscale.com/derp/derpconst" "tailscale.com/derp/derpconst"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/feature"
"tailscale.com/feature/buildfeatures"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/net/dnscache" "tailscale.com/net/dnscache"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
@ -39,7 +41,6 @@ import (
"tailscale.com/net/netx" "tailscale.com/net/netx"
"tailscale.com/net/sockstats" "tailscale.com/net/sockstats"
"tailscale.com/net/tlsdial" "tailscale.com/net/tlsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/syncs" "tailscale.com/syncs"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tstime" "tailscale.com/tstime"
@ -734,9 +735,13 @@ func (c *Client) dialNode(ctx context.Context, n *tailcfg.DERPNode) (net.Conn, e
Path: "/", // unused Path: "/", // unused
}, },
} }
if proxyURL, err := tshttpproxy.ProxyFromEnvironment(proxyReq); err == nil && proxyURL != nil { if buildfeatures.HasUseProxy {
if proxyFromEnv, ok := feature.HookProxyFromEnvironment.GetOk(); ok {
if proxyURL, err := proxyFromEnv(proxyReq); err == nil && proxyURL != nil {
return c.dialNodeUsingProxy(ctx, n, proxyURL) return c.dialNodeUsingProxy(ctx, n, proxyURL)
} }
}
}
type res struct { type res struct {
c net.Conn c net.Conn
@ -865,11 +870,15 @@ func (c *Client) dialNodeUsingProxy(ctx context.Context, n *tailcfg.DERPNode, pr
target := net.JoinHostPort(n.HostName, "443") target := net.JoinHostPort(n.HostName, "443")
var authHeader string var authHeader string
if v, err := tshttpproxy.GetAuthHeader(pu); err != nil { if buildfeatures.HasUseProxy {
if getAuthHeader, ok := feature.HookProxyGetAuthHeader.GetOk(); ok {
if v, err := getAuthHeader(pu); err != nil {
c.logf("derphttp: error getting proxy auth header for %v: %v", proxyURL, err) c.logf("derphttp: error getting proxy auth header for %v: %v", proxyURL, err)
} else if v != "" { } else if v != "" {
authHeader = fmt.Sprintf("Proxy-Authorization: %s\r\n", v) authHeader = fmt.Sprintf("Proxy-Authorization: %s\r\n", v)
} }
}
}
if _, err := fmt.Fprintf(proxyConn, "CONNECT %s HTTP/1.1\r\nHost: %s\r\n%s\r\n", target, target, authHeader); err != nil { if _, err := fmt.Fprintf(proxyConn, "CONNECT %s HTTP/1.1\r\nHost: %s\r\n%s\r\n", target, target, authHeader); err != nil {
if ctx.Err() != nil { if ctx.Err() != nil {

View File

@ -7,7 +7,7 @@
package buildfeatures package buildfeatures
// HasOutboundProxy is whether the binary was built with support for modular feature "Outbound localhost HTTP/SOCK5 proxy support". // HasOutboundProxy is whether the binary was built with support for modular feature "Support running an outbound localhost HTTP/SOCK5 proxy support that sends traffic over Tailscale".
// Specifically, it's whether the binary was NOT built with the "ts_omit_outboundproxy" build tag. // Specifically, it's whether the binary was NOT built with the "ts_omit_outboundproxy" build tag.
// It's a const so it can be used for dead code elimination. // It's a const so it can be used for dead code elimination.
const HasOutboundProxy = false const HasOutboundProxy = false

View File

@ -7,7 +7,7 @@
package buildfeatures package buildfeatures
// HasOutboundProxy is whether the binary was built with support for modular feature "Outbound localhost HTTP/SOCK5 proxy support". // HasOutboundProxy is whether the binary was built with support for modular feature "Support running an outbound localhost HTTP/SOCK5 proxy support that sends traffic over Tailscale".
// Specifically, it's whether the binary was NOT built with the "ts_omit_outboundproxy" build tag. // Specifically, it's whether the binary was NOT built with the "ts_omit_outboundproxy" build tag.
// It's a const so it can be used for dead code elimination. // It's a const so it can be used for dead code elimination.
const HasOutboundProxy = true const HasOutboundProxy = true

View 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_synology
package buildfeatures
// HasSynology is whether the binary was built with support for modular feature "Synology NAS integration (applies to Linux builds only)".
// Specifically, it's whether the binary was NOT built with the "ts_omit_synology" build tag.
// It's a const so it can be used for dead code elimination.
const HasSynology = false

View 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_synology
package buildfeatures
// HasSynology is whether the binary was built with support for modular feature "Synology NAS integration (applies to Linux builds only)".
// Specifically, it's whether the binary was NOT built with the "ts_omit_synology" build tag.
// It's a const so it can be used for dead code elimination.
const HasSynology = true

View 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_useproxy
package buildfeatures
// HasUseProxy is whether the binary was built with support for modular feature "Support using system proxies as specified by env vars or the system configuration to reach Tailscale servers.".
// Specifically, it's whether the binary was NOT built with the "ts_omit_useproxy" build tag.
// It's a const so it can be used for dead code elimination.
const HasUseProxy = false

View 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_useproxy
package buildfeatures
// HasUseProxy is whether the binary was built with support for modular feature "Support using system proxies as specified by env vars or the system configuration to reach Tailscale servers.".
// Specifically, it's whether the binary was NOT built with the "ts_omit_useproxy" build tag.
// It's a const so it can be used for dead code elimination.
const HasUseProxy = true

View File

@ -6,9 +6,13 @@
// to ensure all conditional features are registered. // to ensure all conditional features are registered.
package condregister package condregister
import (
// Portmapper is special in that the CLI also needs to link it in, // Portmapper is special in that the CLI also needs to link it in,
// so it's pulled out into its own package, rather than using a maybe_*.go // so it's pulled out into its own package, rather than using a maybe_*.go
// file in condregister. // file in condregister.
import (
_ "tailscale.com/feature/condregister/portmapper" _ "tailscale.com/feature/condregister/portmapper"
// HTTP proxy support is also needed by the CLI, and tsnet, so it's its
// own package too.
_ "tailscale.com/feature/condregister/useproxy"
) )

View File

@ -0,0 +1,6 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package useproxy registers support for using proxies
// if it's not disabled via the ts_omit_useproxy build tag.
package useproxy

View File

@ -0,0 +1,8 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_useproxy
package useproxy
import _ "tailscale.com/feature/useproxy"

View File

@ -50,7 +50,8 @@ func (h *Hook[Func]) Set(f Func) {
} }
// Get returns the hook function, or panics if it hasn't been set. // Get returns the hook function, or panics if it hasn't been set.
// Use IsSet to check if it's been set. // Use IsSet to check if it's been set, or use GetOrNil if you're
// okay with a nil return value.
func (h *Hook[Func]) Get() Func { func (h *Hook[Func]) Get() Func {
if !h.ok { if !h.ok {
panic("Get on unset feature hook, without IsSet") panic("Get on unset feature hook, without IsSet")
@ -64,6 +65,11 @@ func (h *Hook[Func]) GetOk() (f Func, ok bool) {
return h.f, h.ok return h.f, h.ok
} }
// GetOrNil returns the hook function or nil if it hasn't been set.
func (h *Hook[Func]) GetOrNil() Func {
return h.f
}
// Hooks is a slice of funcs. // Hooks is a slice of funcs.
// //
// As opposed to a single Hook, this is meant to be used when // As opposed to a single Hook, this is meant to be used when

View File

@ -121,7 +121,7 @@ var Features = map[FeatureTag]FeatureMeta{
"oauthkey": {"OAuthKey", "OAuth secret-to-authkey resolution support", nil}, "oauthkey": {"OAuthKey", "OAuth secret-to-authkey resolution support", nil},
"outboundproxy": { "outboundproxy": {
Sym: "OutboundProxy", Sym: "OutboundProxy",
Desc: "Outbound localhost HTTP/SOCK5 proxy support", Desc: "Support running an outbound localhost HTTP/SOCK5 proxy support that sends traffic over Tailscale",
Deps: []FeatureTag{"netstack"}, Deps: []FeatureTag{"netstack"},
}, },
"osrouter": { "osrouter": {
@ -172,6 +172,10 @@ var Features = map[FeatureTag]FeatureMeta{
Desc: "Tailscale SSH support", Desc: "Tailscale SSH support",
Deps: []FeatureTag{"dbus", "netstack"}, Deps: []FeatureTag{"dbus", "netstack"},
}, },
"synology": {
Sym: "Synology",
Desc: "Synology NAS integration (applies to Linux builds only)",
},
"syspolicy": {"SystemPolicy", "System policy configuration (MDM) support", nil}, "syspolicy": {"SystemPolicy", "System policy configuration (MDM) support", nil},
"systray": { "systray": {
Sym: "SysTray", Sym: "SysTray",
@ -182,6 +186,10 @@ var Features = map[FeatureTag]FeatureMeta{
"tailnetlock": {"TailnetLock", "Tailnet Lock support", nil}, "tailnetlock": {"TailnetLock", "Tailnet Lock support", nil},
"tap": {"Tap", "Experimental Layer 2 (ethernet) support", nil}, "tap": {"Tap", "Experimental Layer 2 (ethernet) support", nil},
"tpm": {"TPM", "TPM support", nil}, "tpm": {"TPM", "TPM support", nil},
"useproxy": {
Sym: "UseProxy",
Desc: "Support using system proxies as specified by env vars or the system configuration to reach Tailscale servers.",
},
"wakeonlan": {"WakeOnLAN", "Wake-on-LAN support", nil}, "wakeonlan": {"WakeOnLAN", "Wake-on-LAN support", nil},
"webclient": { "webclient": {
Sym: "WebClient", Desc: "Web client support", Sym: "WebClient", Desc: "Web client support",

View File

@ -3,6 +3,11 @@
package feature package feature
import (
"net/http"
"net/url"
)
// HookCanAutoUpdate is a hook for the clientupdate package // HookCanAutoUpdate is a hook for the clientupdate package
// to conditionally initialize. // to conditionally initialize.
var HookCanAutoUpdate Hook[func() bool] var HookCanAutoUpdate Hook[func() bool]
@ -15,3 +20,23 @@ func CanAutoUpdate() bool {
} }
return false return false
} }
// HookProxyFromEnvironment is a hook for feature/useproxy to register
// a function to use as http.ProxyFromEnvironment.
var HookProxyFromEnvironment Hook[func(*http.Request) (*url.URL, error)]
// HookProxyInvalidateCache is a hook for feature/useproxy to register
// [tshttpproxy.InvalidateCache].
var HookProxyInvalidateCache Hook[func()]
// HookProxyGetAuthHeader is a hook for feature/useproxy to register
// [tshttpproxy.GetAuthHeader].
var HookProxyGetAuthHeader Hook[func(*url.URL) (string, error)]
// HookProxySetSelfProxy is a hook for feature/useproxy to register
// [tshttpproxy.SetSelfProxy].
var HookProxySetSelfProxy Hook[func(...string)]
// HookProxySetTransportGetProxyConnectHeader is a hook for feature/useproxy to register
// [tshttpproxy.SetTransportGetProxyConnectHeader].
var HookProxySetTransportGetProxyConnectHeader Hook[func(*http.Transport)]

View File

@ -0,0 +1,18 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package useproxy registers support for using system proxies.
package useproxy
import (
"tailscale.com/feature"
"tailscale.com/net/tshttpproxy"
)
func init() {
feature.HookProxyFromEnvironment.Set(tshttpproxy.ProxyFromEnvironment)
feature.HookProxyInvalidateCache.Set(tshttpproxy.InvalidateCache)
feature.HookProxyGetAuthHeader.Set(tshttpproxy.GetAuthHeader)
feature.HookProxySetSelfProxy.Set(tshttpproxy.SetSelfProxy)
feature.HookProxySetTransportGetProxyConnectHeader.Set(tshttpproxy.SetTransportGetProxyConnectHeader)
}

View File

@ -35,6 +35,7 @@ import (
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/feature/buildfeatures"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
@ -73,7 +74,7 @@ func (b *LocalBackend) certDir() (string, error) {
// As a workaround for Synology DSM6 not having a "var" directory, use the // As a workaround for Synology DSM6 not having a "var" directory, use the
// app's "etc" directory (on a small partition) to hold certs at least. // app's "etc" directory (on a small partition) to hold certs at least.
// See https://github.com/tailscale/tailscale/issues/4060#issuecomment-1186592251 // See https://github.com/tailscale/tailscale/issues/4060#issuecomment-1186592251
if d == "" && runtime.GOOS == "linux" && distro.Get() == distro.Synology && distro.DSMVersion() == 6 { if buildfeatures.HasSynology && d == "" && runtime.GOOS == "linux" && distro.Get() == distro.Synology && distro.DSMVersion() == 6 {
d = "/var/packages/Tailscale/etc" // base; we append "certs" below d = "/var/packages/Tailscale/etc" // base; we append "certs" below
} }
if d == "" { if d == "" {

View File

@ -31,6 +31,7 @@ import (
"golang.org/x/term" "golang.org/x/term"
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"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"
@ -44,7 +45,6 @@ import (
"tailscale.com/net/netns" "tailscale.com/net/netns"
"tailscale.com/net/netx" "tailscale.com/net/netx"
"tailscale.com/net/tlsdial" "tailscale.com/net/tlsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/paths" "tailscale.com/paths"
"tailscale.com/safesocket" "tailscale.com/safesocket"
"tailscale.com/types/logger" "tailscale.com/types/logger"
@ -870,8 +870,12 @@ func (opts TransportOptions) New() http.RoundTripper {
tr.TLSClientConfig = opts.TLSClientConfig.Clone() tr.TLSClientConfig = opts.TLSClientConfig.Clone()
} }
tr.Proxy = tshttpproxy.ProxyFromEnvironment if buildfeatures.HasUseProxy {
tshttpproxy.SetTransportGetProxyConnectHeader(tr) tr.Proxy = feature.HookProxyFromEnvironment.GetOrNil()
if set, ok := feature.HookProxySetTransportGetProxyConnectHeader.GetOk(); ok {
set(tr)
}
}
// We do our own zstd compression on uploads, and responses never contain any payload, // We do our own zstd compression on uploads, and responses never contain any payload,
// so don't send "Accept-Encoding: gzip" to save a few bytes on the wire, since there // so don't send "Accept-Encoding: gzip" to save a few bytes on the wire, since there

View File

@ -26,11 +26,11 @@ import (
"time" "time"
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"tailscale.com/feature"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/net/netns" "tailscale.com/net/netns"
"tailscale.com/net/tlsdial" "tailscale.com/net/tlsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/slicesx" "tailscale.com/util/slicesx"
@ -135,7 +135,7 @@ func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr
dialer := netns.NewDialer(logf, netMon) dialer := netns.NewDialer(logf, netMon)
tr := http.DefaultTransport.(*http.Transport).Clone() tr := http.DefaultTransport.(*http.Transport).Clone()
tr.DisableKeepAlives = true // This transport is meant to be used once. tr.DisableKeepAlives = true // This transport is meant to be used once.
tr.Proxy = tshttpproxy.ProxyFromEnvironment tr.Proxy = feature.HookProxyFromEnvironment.GetOrNil()
tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) { tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(serverIP.String(), "443")) return dialer.DialContext(ctx, "tcp", net.JoinHostPort(serverIP.String(), "443"))
} }

View File

@ -13,6 +13,7 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/feature/buildfeatures"
"tailscale.com/tsconst" "tailscale.com/tsconst"
) )
@ -22,8 +23,10 @@ const (
func init() { func init() {
likelyHomeRouterIP = likelyHomeRouterIPWindows likelyHomeRouterIP = likelyHomeRouterIPWindows
if buildfeatures.HasUseProxy {
getPAC = getPACWindows getPAC = getPACWindows
} }
}
func likelyHomeRouterIPWindows() (ret netip.Addr, _ netip.Addr, ok bool) { func likelyHomeRouterIPWindows() (ret netip.Addr, _ netip.Addr, ok bool) {
rs, err := winipcfg.GetIPForwardTable2(windows.AF_INET) rs, err := winipcfg.GetIPForwardTable2(windows.AF_INET)
@ -244,6 +247,9 @@ const (
) )
func getPACWindows() string { func getPACWindows() string {
if !buildfeatures.HasUseProxy {
return ""
}
var res *uint16 var res *uint16
r, _, e := detectAutoProxyConfigURL.Call( r, _, e := detectAutoProxyConfigURL.Call(
winHTTP_AUTO_DETECT_TYPE_DHCP|winHTTP_AUTO_DETECT_TYPE_DNS_A, winHTTP_AUTO_DETECT_TYPE_DHCP|winHTTP_AUTO_DETECT_TYPE_DNS_A,

View File

@ -15,10 +15,11 @@ import (
"strings" "strings"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/feature"
"tailscale.com/feature/buildfeatures"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/net/netaddr" "tailscale.com/net/netaddr"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/net/tshttpproxy"
"tailscale.com/util/mak" "tailscale.com/util/mak"
) )
@ -501,14 +502,16 @@ func getState(optTSInterfaceName string) (*State, error) {
} }
} }
if s.AnyInterfaceUp() { if buildfeatures.HasUseProxy && s.AnyInterfaceUp() {
req, err := http.NewRequest("GET", LoginEndpointForProxyDetermination, nil) req, err := http.NewRequest("GET", LoginEndpointForProxyDetermination, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if u, err := tshttpproxy.ProxyFromEnvironment(req); err == nil && u != nil { if proxyFromEnv, ok := feature.HookProxyFromEnvironment.GetOk(); ok {
if u, err := proxyFromEnv(req); err == nil && u != nil {
s.HTTPProxy = u.String() s.HTTPProxy = u.String()
} }
}
if getPAC != nil { if getPAC != nil {
s.PAC = getPAC() s.PAC = getPAC()
} }

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"tailscale.com/feature/buildfeatures"
"tailscale.com/version/distro" "tailscale.com/version/distro"
) )
@ -17,7 +18,7 @@ func init() {
} }
func linuxSysProxyFromEnv(req *http.Request) (*url.URL, error) { func linuxSysProxyFromEnv(req *http.Request) (*url.URL, error) {
if distro.Get() == distro.Synology { if buildfeatures.HasSynology && distro.Get() == distro.Synology {
return synologyProxyFromConfigCached(req) return synologyProxyFromConfigCached(req)
} }
return nil, nil return nil, nil

View File

@ -139,9 +139,11 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+ tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet
tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet
tailscale.com/feature/condregister/useproxy from tailscale.com/tsnet
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/syspolicy from tailscale.com/logpolicy tailscale.com/feature/syspolicy from tailscale.com/logpolicy
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
tailscale.com/hostinfo from tailscale.com/client/web+ tailscale.com/hostinfo from tailscale.com/client/web+
@ -201,7 +203,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
tailscale.com/net/tsaddr from tailscale.com/client/web+ tailscale.com/net/tsaddr from tailscale.com/client/web+
tailscale.com/net/tsdial from tailscale.com/control/controlclient+ tailscale.com/net/tsdial from tailscale.com/control/controlclient+
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+ 💣 tailscale.com/net/tshttpproxy from tailscale.com/feature/useproxy
tailscale.com/net/tstun from tailscale.com/tsd+ tailscale.com/net/tstun from tailscale.com/tsd+
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
tailscale.com/net/udprelay/status from tailscale.com/client/local tailscale.com/net/udprelay/status from tailscale.com/client/local

View File

@ -31,6 +31,7 @@ import (
"tailscale.com/envknob" "tailscale.com/envknob"
_ "tailscale.com/feature/condregister/oauthkey" _ "tailscale.com/feature/condregister/oauthkey"
_ "tailscale.com/feature/condregister/portmapper" _ "tailscale.com/feature/condregister/portmapper"
_ "tailscale.com/feature/condregister/useproxy"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/internal/client/tailscale" "tailscale.com/internal/client/tailscale"

View File

@ -36,7 +36,6 @@ import (
_ "tailscale.com/net/proxymux" _ "tailscale.com/net/proxymux"
_ "tailscale.com/net/socks5" _ "tailscale.com/net/socks5"
_ "tailscale.com/net/tsdial" _ "tailscale.com/net/tsdial"
_ "tailscale.com/net/tshttpproxy"
_ "tailscale.com/net/tstun" _ "tailscale.com/net/tstun"
_ "tailscale.com/paths" _ "tailscale.com/paths"
_ "tailscale.com/safesocket" _ "tailscale.com/safesocket"

View File

@ -36,7 +36,6 @@ import (
_ "tailscale.com/net/proxymux" _ "tailscale.com/net/proxymux"
_ "tailscale.com/net/socks5" _ "tailscale.com/net/socks5"
_ "tailscale.com/net/tsdial" _ "tailscale.com/net/tsdial"
_ "tailscale.com/net/tshttpproxy"
_ "tailscale.com/net/tstun" _ "tailscale.com/net/tstun"
_ "tailscale.com/paths" _ "tailscale.com/paths"
_ "tailscale.com/safesocket" _ "tailscale.com/safesocket"

View File

@ -36,7 +36,6 @@ import (
_ "tailscale.com/net/proxymux" _ "tailscale.com/net/proxymux"
_ "tailscale.com/net/socks5" _ "tailscale.com/net/socks5"
_ "tailscale.com/net/tsdial" _ "tailscale.com/net/tsdial"
_ "tailscale.com/net/tshttpproxy"
_ "tailscale.com/net/tstun" _ "tailscale.com/net/tstun"
_ "tailscale.com/paths" _ "tailscale.com/paths"
_ "tailscale.com/safesocket" _ "tailscale.com/safesocket"

View File

@ -36,7 +36,6 @@ import (
_ "tailscale.com/net/proxymux" _ "tailscale.com/net/proxymux"
_ "tailscale.com/net/socks5" _ "tailscale.com/net/socks5"
_ "tailscale.com/net/tsdial" _ "tailscale.com/net/tsdial"
_ "tailscale.com/net/tshttpproxy"
_ "tailscale.com/net/tstun" _ "tailscale.com/net/tstun"
_ "tailscale.com/paths" _ "tailscale.com/paths"
_ "tailscale.com/safesocket" _ "tailscale.com/safesocket"

View File

@ -46,7 +46,6 @@ import (
_ "tailscale.com/net/proxymux" _ "tailscale.com/net/proxymux"
_ "tailscale.com/net/socks5" _ "tailscale.com/net/socks5"
_ "tailscale.com/net/tsdial" _ "tailscale.com/net/tsdial"
_ "tailscale.com/net/tshttpproxy"
_ "tailscale.com/net/tstun" _ "tailscale.com/net/tstun"
_ "tailscale.com/paths" _ "tailscale.com/paths"
_ "tailscale.com/safesocket" _ "tailscale.com/safesocket"

View File

@ -13,6 +13,7 @@ import (
"runtime" "runtime"
"time" "time"
"tailscale.com/feature/buildfeatures"
"tailscale.com/version/distro" "tailscale.com/version/distro"
) )
@ -20,7 +21,7 @@ import (
// CAP_NET_RAW from tailscaled's binary. // CAP_NET_RAW from tailscaled's binary.
var setAmbientCapsRaw func(*exec.Cmd) var setAmbientCapsRaw func(*exec.Cmd)
var isSynology = runtime.GOOS == "linux" && distro.Get() == distro.Synology var isSynology = runtime.GOOS == "linux" && buildfeatures.HasSynology && distro.Get() == distro.Synology
// sendOutboundUserPing sends a non-privileged ICMP (or ICMPv6) ping to dstIP with the given timeout. // sendOutboundUserPing sends a non-privileged ICMP (or ICMPv6) ping to dstIP with the given timeout.
func (ns *Impl) sendOutboundUserPing(dstIP netip.Addr, timeout time.Duration) error { func (ns *Impl) sendOutboundUserPing(dstIP netip.Addr, timeout time.Duration) error {
@ -61,7 +62,7 @@ func (ns *Impl) sendOutboundUserPing(dstIP netip.Addr, timeout time.Duration) er
ping = "/bin/ping" ping = "/bin/ping"
} }
cmd := exec.Command(ping, "-c", "1", "-W", "3", dstIP.String()) cmd := exec.Command(ping, "-c", "1", "-W", "3", dstIP.String())
if isSynology && os.Getuid() != 0 { if buildfeatures.HasSynology && isSynology && os.Getuid() != 0 {
// On DSM7 we run as non-root and need to pass // On DSM7 we run as non-root and need to pass
// CAP_NET_RAW if our binary has it. // CAP_NET_RAW if our binary has it.
setAmbientCapsRaw(cmd) setAmbientCapsRaw(cmd)

View File

@ -23,6 +23,7 @@ import (
"tailscale.com/control/controlknobs" "tailscale.com/control/controlknobs"
"tailscale.com/drive" "tailscale.com/drive"
"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/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
@ -35,7 +36,6 @@ import (
"tailscale.com/net/sockstats" "tailscale.com/net/sockstats"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/net/tsdial" "tailscale.com/net/tsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/net/tstun" "tailscale.com/net/tstun"
"tailscale.com/syncs" "tailscale.com/syncs"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -559,7 +559,9 @@ func (e *userspaceEngine) consumeEventbusTopics(cli *eventbus.Client) func(*even
case <-cli.Done(): case <-cli.Done():
return return
case changeDelta := <-changeDeltaSub.Events(): case changeDelta := <-changeDeltaSub.Events():
tshttpproxy.InvalidateCache() if f, ok := feature.HookProxyInvalidateCache.GetOk(); ok {
f()
}
e.linkChange(&changeDelta) e.linkChange(&changeDelta)
} }
} }