summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/tailscale/localclient.go31
-rw-r--r--client/tailscale/localclient_h2c.go31
-rw-r--r--cmd/derper/depaware.txt5
-rw-r--r--cmd/tailscale/cli/cli.go3
-rw-r--r--cmd/tailscaled/depaware.txt2
-rw-r--r--ipn/ipnserver/server.go5
-rw-r--r--ipn/ipnserver/server_h2c.go29
7 files changed, 95 insertions, 11 deletions
diff --git a/client/tailscale/localclient.go b/client/tailscale/localclient.go
index 98371393d..e2caaa47c 100644
--- a/client/tailscale/localclient.go
+++ b/client/tailscale/localclient.go
@@ -41,6 +41,13 @@ import (
"tailscale.com/types/tkatype"
)
+// DialFunc is any function that dials the given address.
+type DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error)
+
+// h2cTransport returns nil on platforms where H2C ("cleartext" HTTP/2)
+// support for the LocalAPI is not implemented.
+var h2cTransport = func(DialFunc) http.RoundTripper { return nil }
+
// defaultLocalClient is the default LocalClient when using the legacy
// package-level functions.
var defaultLocalClient LocalClient
@@ -58,7 +65,7 @@ var defaultLocalClient LocalClient
type LocalClient struct {
// Dial optionally specifies an alternate func that connects to the local
// machine's tailscaled or equivalent. If nil, a default is used.
- Dial func(ctx context.Context, network, addr string) (net.Conn, error)
+ Dial DialFunc
// Socket specifies an alternate path to the local Tailscale socket.
// If empty, a platform-specific default is used.
@@ -77,6 +84,9 @@ type LocalClient struct {
// different operating system, such as in integration tests.
OmitAuth bool
+ // AllowH2C enables H2C ("cleartext" HTTP/2) if supported on the current platform.
+ AllowH2C bool
+
// tsClient does HTTP requests to the local Tailscale daemon.
// It's lazily initialized on first use.
tsClient *http.Client
@@ -90,7 +100,7 @@ func (lc *LocalClient) socket() string {
return paths.DefaultTailscaledSocket()
}
-func (lc *LocalClient) dialer() func(ctx context.Context, network, addr string) (net.Conn, error) {
+func (lc *LocalClient) dialer() DialFunc {
if lc.Dial != nil {
return lc.Dial
}
@@ -114,6 +124,17 @@ func (lc *LocalClient) defaultDialer(ctx context.Context, network, addr string)
return safesocket.ConnectContext(ctx, lc.socket())
}
+// transport returns the HTTP transport to be used when making requests
+// to the local machine's Tailscale daemon.
+func (lc *LocalClient) transport() http.RoundTripper {
+ if lc.AllowH2C {
+ if t := h2cTransport(lc.dialer()); t != nil {
+ return t
+ }
+ }
+ return &http.Transport{DialContext: lc.dialer()}
+}
+
// DoLocalRequest makes an HTTP request to the local machine's Tailscale daemon.
//
// URLs are of the form http://local-tailscaled.sock/localapi/v0/whois?ip=1.2.3.4.
@@ -126,11 +147,7 @@ func (lc *LocalClient) defaultDialer(ctx context.Context, network, addr string)
func (lc *LocalClient) DoLocalRequest(req *http.Request) (*http.Response, error) {
req.Header.Set("Tailscale-Cap", strconv.Itoa(int(tailcfg.CurrentCapabilityVersion)))
lc.tsClientOnce.Do(func() {
- lc.tsClient = &http.Client{
- Transport: &http.Transport{
- DialContext: lc.dialer(),
- },
- }
+ lc.tsClient = &http.Client{Transport: lc.transport()}
})
if !lc.OmitAuth {
if _, token, err := safesocket.LocalTCPPortAndToken(); err == nil {
diff --git a/client/tailscale/localclient_h2c.go b/client/tailscale/localclient_h2c.go
new file mode 100644
index 000000000..1b20bbe0d
--- /dev/null
+++ b/client/tailscale/localclient_h2c.go
@@ -0,0 +1,31 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+// Enabling H2C for LocalAPI is not Windows-specific.
+// However, Windows is expected to benefit the most
+// due to additional, potentially slow authentication steps
+// performed for each new named pipe connection.
+// As an experiment, we are enabling it on Windows first.
+//go:build windows
+
+package tailscale
+
+import (
+ "context"
+ "crypto/tls"
+ "net"
+ "net/http"
+
+ "golang.org/x/net/http2"
+)
+
+func init() {
+ h2cTransport = func(dialer DialFunc) http.RoundTripper {
+ return &http2.Transport{
+ AllowHTTP: true,
+ DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
+ return dialer(ctx, network, addr)
+ },
+ }
+ }
+}
diff --git a/cmd/derper/depaware.txt b/cmd/derper/depaware.txt
index 53b263d03..b9aad9e47 100644
--- a/cmd/derper/depaware.txt
+++ b/cmd/derper/depaware.txt
@@ -189,9 +189,10 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
golang.org/x/exp/maps from tailscale.com/util/syspolicy/setting
L golang.org/x/net/bpf from github.com/mdlayher/netlink+
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/http2/hpack from net/http
+ W golang.org/x/net/http2 from tailscale.com/client/tailscale
+ 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/proxy from tailscale.com/net/netns
D golang.org/x/net/route from net+
diff --git a/cmd/tailscale/cli/cli.go b/cmd/tailscale/cli/cli.go
index efbdd3e40..cde0f70e5 100644
--- a/cmd/tailscale/cli/cli.go
+++ b/cmd/tailscale/cli/cli.go
@@ -79,7 +79,8 @@ func CleanUpArgs(args []string) []string {
}
var localClient = tailscale.LocalClient{
- Socket: paths.DefaultTailscaledSocket(),
+ AllowH2C: true,
+ Socket: paths.DefaultTailscaledSocket(),
}
// Run runs the CLI. The args do not include the binary name.
diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt
index eed37c7d4..7489cdc29 100644
--- a/cmd/tailscaled/depaware.txt
+++ b/cmd/tailscaled/depaware.txt
@@ -451,7 +451,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
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/http2 from golang.org/x/net/http2/h2c+
- golang.org/x/net/http2/h2c from tailscale.com/ipn/ipnlocal
+ golang.org/x/net/http2/h2c from tailscale.com/ipn/ipnlocal+
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/idna from golang.org/x/net/http/httpguts+
diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go
index 25c672e2e..e52540463 100644
--- a/ipn/ipnserver/server.go
+++ b/ipn/ipnserver/server.go
@@ -33,6 +33,10 @@ import (
"tailscale.com/util/systemd"
)
+// addH2C is a no-op on platforms where the LocalAPI
+// does not support H2C ("cleartext" HTTP/2).
+var addH2C = func(*http.Server) {}
+
// Server is an IPN backend and its set of 0 or more active localhost
// TCP or unix socket connections talking to that backend.
type Server struct {
@@ -515,6 +519,7 @@ func (s *Server) Run(ctx context.Context, ln net.Listener) error {
IdleTimeout: 5 * time.Second,
ErrorLog: logger.StdLogger(logger.WithPrefix(s.logf, "ipnserver: ")),
}
+ addH2C(hs)
if err := hs.Serve(ln); err != nil {
if err := ctx.Err(); err != nil {
return err
diff --git a/ipn/ipnserver/server_h2c.go b/ipn/ipnserver/server_h2c.go
new file mode 100644
index 000000000..31270ea90
--- /dev/null
+++ b/ipn/ipnserver/server_h2c.go
@@ -0,0 +1,29 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+// Enabling H2C for LocalAPI is not Windows-specific.
+// However, Windows is expected to benefit the most
+// due to additional, potentially slow authentication steps
+// performed for each new named pipe connection.
+// As an experiment, we are enabling it on Windows first.
+//go:build windows
+
+package ipnserver
+
+import (
+ "net/http"
+
+ "golang.org/x/net/http2"
+ "golang.org/x/net/http2/h2c"
+)
+
+func init() {
+ addH2C = func(s *http.Server) {
+ h2s := &http2.Server{}
+ s.Handler = h2c.NewHandler(s.Handler, h2s)
+ // [http2.ConfigureServer] sets up a server shutdown handler that gracefully
+ // closes connections when [http.Server.Shutdown] is called.
+ // Otherwise, it leaks goroutines.
+ http2.ConfigureServer(s, h2s)
+ }
+}