diff --git a/derp/derphttp/derphttp_client.go b/derp/derphttp/derphttp_client.go index 07317fcbf..c08c293cc 100644 --- a/derp/derphttp/derphttp_client.go +++ b/derp/derphttp/derphttp_client.go @@ -206,6 +206,9 @@ func (c *Client) tlsServerName(node *tailcfg.DERPNode) string { if c.url != nil { return c.url.Host } + if node == nil { + return "" + } return node.HostName } @@ -247,7 +250,7 @@ func (c *Client) preferIPv6() bool { } // dialWebsocketFunc is non-nil (set by websocket.go's init) when compiled in. -var dialWebsocketFunc func(ctx context.Context, urlStr string) (net.Conn, error) +var dialWebsocketFunc func(ctx context.Context, urlStr string, tlsConfig *tls.Config) (net.Conn, error) func useWebsockets() bool { if runtime.GOOS == "js" { @@ -314,13 +317,16 @@ func (c *Client) connect(ctx context.Context, caller string) (client *derp.Clien switch { case useWebsockets(): var urlStr string + var tlsConfig *tls.Config if c.url != nil { urlStr = c.url.String() + tlsConfig = c.tlsConfig(nil) } else { urlStr = c.urlString(reg.Nodes[0]) + tlsConfig = c.tlsConfig(reg.Nodes[0]) } c.logf("%s: connecting websocket to %v", caller, urlStr) - conn, err := dialWebsocketFunc(ctx, urlStr) + conn, err := dialWebsocketFunc(ctx, urlStr, tlsConfig) if err != nil { c.logf("%s: websocket to %v error: %v", caller, urlStr, err) return nil, 0, err @@ -385,7 +391,7 @@ func (c *Client) connect(ctx context.Context, caller string) (client *derp.Clien var serverProtoVersion int var tlsState *tls.ConnectionState if c.useHTTPS() { - tlsConn := c.tlsClient(tcpConn, node) + tlsConn := tls.Client(tcpConn, c.tlsConfig(node)) httpConn = tlsConn // Force a handshake now (instead of waiting for it to @@ -544,7 +550,7 @@ func (c *Client) dialRegion(ctx context.Context, reg *tailcfg.DERPRegion) (net.C return nil, nil, firstErr } -func (c *Client) tlsClient(nc net.Conn, node *tailcfg.DERPNode) *tls.Conn { +func (c *Client) tlsConfig(node *tailcfg.DERPNode) *tls.Config { tlsConf := tlsdial.Config(c.tlsServerName(node), c.TLSConfig) if node != nil { if node.InsecureForTests { @@ -555,7 +561,7 @@ func (c *Client) tlsClient(nc net.Conn, node *tailcfg.DERPNode) *tls.Conn { tlsdial.SetConfigExpectedCert(tlsConf, node.CertName) } } - return tls.Client(nc, tlsConf) + return tlsConf } // DialRegionTLS returns a TLS connection to a DERP node in the given region. @@ -571,7 +577,7 @@ func (c *Client) DialRegionTLS(ctx context.Context, reg *tailcfg.DERPRegion) (tl done := make(chan bool) // unbuffered defer close(done) - tlsConn = c.tlsClient(tcpConn, node) + tlsConn = tls.Client(tcpConn, c.tlsConfig(node)) go func() { select { case <-done: diff --git a/derp/derphttp/websocket.go b/derp/derphttp/websocket.go index 730f975ff..a40ee7505 100644 --- a/derp/derphttp/websocket.go +++ b/derp/derphttp/websocket.go @@ -1,14 +1,16 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause -//go:build linux || js +//go:build linux || windows || darwin || js package derphttp import ( "context" + "crypto/tls" "log" "net" + "net/http" "nhooyr.io/websocket" "tailscale.com/net/wsconn" @@ -18,8 +20,13 @@ func init() { dialWebsocketFunc = dialWebsocket } -func dialWebsocket(ctx context.Context, urlStr string) (net.Conn, error) { +func dialWebsocket(ctx context.Context, urlStr string, tlsConfig *tls.Config) (net.Conn, error) { c, res, err := websocket.Dial(ctx, urlStr, &websocket.DialOptions{ + HTTPClient: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsConfig, + }, + }, Subprotocols: []string{"derp"}, }) if err != nil {