From d58ba59fd50577d08e8b3165bf39387e697df92b Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 23 Aug 2023 20:21:37 -0700 Subject: [PATCH] cmd/tailscale/cli: make netcheck run even if machine lacks TLS certs We have a fancy package for doing TLS cert validation even if the machine doesn't have TLS certs (for LetsEncrypt only) but the CLI's netcheck command wasn't using it. Also, update the tlsdial's outdated package docs while here. Updates #cleanup Change-Id: I74b3cb645d07af4d8ae230fb39a60c809ec129ad Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/cli/netcheck.go | 4 +++- net/tlsdial/tlsdial.go | 32 +++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cmd/tailscale/cli/netcheck.go b/cmd/tailscale/cli/netcheck.go index f4932d23e..a61505087 100644 --- a/cmd/tailscale/cli/netcheck.go +++ b/cmd/tailscale/cli/netcheck.go @@ -21,6 +21,7 @@ import ( "tailscale.com/net/netcheck" "tailscale.com/net/netmon" "tailscale.com/net/portmapper" + "tailscale.com/net/tlsdial" "tailscale.com/tailcfg" "tailscale.com/types/logger" ) @@ -76,7 +77,8 @@ func runNetcheck(ctx context.Context, args []string) error { log.Printf("No DERP map from tailscaled; using default.") } if err != nil || noRegions { - dm, err = prodDERPMap(ctx, http.DefaultClient) + hc := &http.Client{Transport: tlsdial.NewTransport()} + dm, err = prodDERPMap(ctx, hc) if err != nil { return err } diff --git a/net/tlsdial/tlsdial.go b/net/tlsdial/tlsdial.go index d571d38a6..2c9be4e1c 100644 --- a/net/tlsdial/tlsdial.go +++ b/net/tlsdial/tlsdial.go @@ -1,22 +1,24 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause -// Package tlsdial originally existed to set up a tls.Config for x509 -// validation, using a memory-optimized path for iOS, but then we -// moved that to the tailscale/go tree instead, so now this package -// does very little. But for now we keep it as a unified point where -// we might want to add shared policy on outgoing TLS connections from -// the 3 places in the client that connect to Tailscale (logs, -// control, DERP). +// Package tlsdial generates tls.Config values and does x509 validation of +// certs. It bakes in the LetsEncrypt roots so even if the user's machine +// doesn't have TLS roots, we can at least connect to Tailscale's LetsEncrypt +// services. It's the unified point where we can add shared policy on outgoing +// TLS connections from the three places in the client that connect to Tailscale +// (logs, control, DERP). package tlsdial import ( "bytes" + "context" "crypto/tls" "crypto/x509" "errors" "fmt" "log" + "net" + "net/http" "os" "sync" "sync/atomic" @@ -192,6 +194,22 @@ func SetConfigExpectedCert(c *tls.Config, certDNSName string) { } } +// NewTransport returns a new HTTP transport that verifies TLS certs using this +// package, including its baked-in LetsEncrypt fallback roots. +func NewTransport() *http.Transport { + return &http.Transport{ + DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + var d tls.Dialer + d.Config = Config(host, nil) + return d.DialContext(ctx, network, addr) + }, + } +} + /* letsEncryptX1 is the LetsEncrypt X1 root: