diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index d6f3f3697..36e9dd293 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -26,6 +26,7 @@ import ( "github.com/tailscale/wireguard-go/wgcfg" "golang.org/x/crypto/nacl/box" "golang.org/x/oauth2" + "tailscale.com/net/tlsdial" "tailscale.com/tailcfg" "tailscale.com/types/logger" "tailscale.com/version" @@ -93,7 +94,6 @@ type Direct struct { type Options struct { Persist Persist // initial persistent data - HTTPC *http.Client // HTTP client used to talk to tailcontrol ServerURL string // URL of the tailcontrol server AuthKey string // optional node auth key for auto registration TimeNow func() time.Time // time.Now implementation used by Client @@ -114,9 +114,6 @@ func NewDirect(opts Options) (*Direct, error) { return nil, errors.New("controlclient.New: no server URL specified") } opts.ServerURL = strings.TrimRight(opts.ServerURL, "/") - if opts.HTTPC == nil { - opts.HTTPC = http.DefaultClient - } if opts.TimeNow == nil { opts.TimeNow = time.Now } @@ -125,8 +122,14 @@ func NewDirect(opts Options) (*Direct, error) { // TODO(bradfitz): ... but then it shouldn't be in Options. opts.Logf = log.Printf } + + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.ForceAttemptHTTP2 = true + tr.TLSClientConfig = tlsdial.Config("", tr.TLSClientConfig) + httpc := &http.Client{Transport: tr} + c := &Direct{ - httpc: opts.HTTPC, + httpc: httpc, serverURL: opts.ServerURL, timeNow: opts.TimeNow, logf: opts.Logf, diff --git a/derp/derphttp/derphttp_client.go b/derp/derphttp/derphttp_client.go index fbb018664..e3076ed9a 100644 --- a/derp/derphttp/derphttp_client.go +++ b/derp/derphttp/derphttp_client.go @@ -26,6 +26,7 @@ import ( "tailscale.com/derp" "tailscale.com/net/dnscache" + "tailscale.com/net/tlsdial" "tailscale.com/types/key" "tailscale.com/types/logger" ) @@ -37,8 +38,8 @@ import ( // Send/Recv will completely re-establish the connection (unless Close // has been called). type Client struct { - TLSConfig *tls.Config // for sever connection, optional, nil means default - DNSCache *dnscache.Resolver // optional; if nil, no caching + TLSConfig *tls.Config // optional; nil means default + DNSCache *dnscache.Resolver // optional; nil means no caching privateKey key.Private logf logger.Logf @@ -182,12 +183,7 @@ func (c *Client) connect(ctx context.Context, caller string) (client *derp.Clien var httpConn net.Conn // a TCP conn or a TLS conn; what we speak HTTP to if c.url.Scheme == "https" { - tlsConfig := &tls.Config{} - if c.TLSConfig != nil { - tlsConfig = c.TLSConfig.Clone() - } - tlsConfig.ServerName = c.url.Host - httpConn = tls.Client(tcpConn, tlsConfig) + httpConn = tls.Client(tcpConn, tlsdial.Config(c.url.Host, c.TLSConfig)) } else { httpConn = tcpConn } diff --git a/net/tlsdial/tlsdial.go b/net/tlsdial/tlsdial.go new file mode 100644 index 000000000..c7d6e26fb --- /dev/null +++ b/net/tlsdial/tlsdial.go @@ -0,0 +1,24 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tlsdial sets up a tls.Config for x509 validation, using +// a memory-optimized path for iOS. +package tlsdial + +import "crypto/tls" + +// Config returns a tls.Config for dialing the given host. +// If base is non-nil, it's cloned as the base config before +// being configured and returned. +func Config(host string, base *tls.Config) *tls.Config { + var conf *tls.Config + if base == nil { + conf = new(tls.Config) + } else { + conf = base.Clone() + } + conf.ServerName = host + + return conf +}