tsd, all: add Sys.ExtraRootCAs, plumb through TLS dial paths
Add ExtraRootCAs *x509.CertPool to tsd.System and plumb it through the control client, noise transport, DERP, and wgengine layers so that platforms like Android can inject user-installed CA certificates into Go's TLS verification. tlsdial.Config now honors base.RootCAs as additional trusted roots, tried after system roots and before the baked-in LetsEncrypt fallback. SetConfigExpectedCert gets the same treatment for domain-fronted DERP. The Android client will set sys.ExtraRootCAs with a pool built from x509.SystemCertPool + user-installed certs obtained via the Android KeyStore API, replacing the current SSL_CERT_DIR environment variable approach. Updates #8085 Change-Id: Iecce0fd140cd5aa0331b124e55a7045e24d8e0c2 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
c4cb5eb809
commit
a182b864ac
@@ -9,6 +9,8 @@ import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -74,6 +76,7 @@ type Direct struct {
|
||||
logf logger.Logf
|
||||
netMon *netmon.Monitor // non-nil
|
||||
health *health.Tracker
|
||||
extraRootCAs *x509.CertPool // additional trusted root CAs; or nil
|
||||
busClient *eventbus.Client
|
||||
clientVersionPub *eventbus.Publisher[tailcfg.ClientVersion]
|
||||
autoUpdatePub *eventbus.Publisher[AutoUpdate]
|
||||
@@ -141,6 +144,7 @@ type Options struct {
|
||||
NoiseTestClient *http.Client // optional HTTP client to use for noise RPCs (tests only)
|
||||
DebugFlags []string // debug settings to send to control
|
||||
HealthTracker *health.Tracker
|
||||
ExtraRootCAs *x509.CertPool // additional trusted root CAs; or nil
|
||||
PopBrowserURL func(url string) // optional func to open browser
|
||||
Dialer *tsdial.Dialer // non-nil
|
||||
C2NHandler http.Handler // or nil
|
||||
@@ -297,6 +301,12 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
f(tr)
|
||||
}
|
||||
}
|
||||
if opts.ExtraRootCAs != nil {
|
||||
if tr.TLSClientConfig == nil {
|
||||
tr.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
tr.TLSClientConfig.RootCAs = opts.ExtraRootCAs
|
||||
}
|
||||
tr.TLSClientConfig = tlsdial.Config(opts.HealthTracker, tr.TLSClientConfig)
|
||||
var dialFunc netx.DialFunc
|
||||
dialFunc, interceptedDial = makeScreenTimeDetectingDialFunc(opts.Dialer.SystemDial)
|
||||
@@ -324,6 +334,7 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
debugFlags: opts.DebugFlags,
|
||||
netMon: netMon,
|
||||
health: opts.HealthTracker,
|
||||
extraRootCAs: opts.ExtraRootCAs,
|
||||
pinger: opts.Pinger,
|
||||
polc: cmp.Or(opts.PolicyClient, policyclient.Client(policyclient.NoPolicyClient{})),
|
||||
popBrowser: opts.PopBrowserURL,
|
||||
@@ -1631,6 +1642,7 @@ func (c *Direct) getNoiseClient() (*ts2021.Client, error) {
|
||||
Logf: c.logf,
|
||||
NetMon: c.netMon,
|
||||
HealthTracker: c.health,
|
||||
ExtraRootCAs: c.extraRootCAs,
|
||||
DialPlan: dp,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -479,6 +479,9 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, optAddr netip.Ad
|
||||
// Disable HTTP2, since h2 can't do protocol switching.
|
||||
tr.TLSClientConfig.NextProtos = []string{}
|
||||
tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
|
||||
if a.ExtraRootCAs != nil {
|
||||
tr.TLSClientConfig.RootCAs = a.ExtraRootCAs
|
||||
}
|
||||
tr.TLSClientConfig = tlsdial.Config(a.HealthTracker, tr.TLSClientConfig)
|
||||
if !tr.TLSClientConfig.InsecureSkipVerify {
|
||||
panic("unexpected") // should be set by tlsdial.Config
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package controlhttp
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
@@ -85,6 +86,9 @@ type Dialer struct {
|
||||
// HealthTracker, if non-nil, is the health tracker to use.
|
||||
HealthTracker *health.Tracker
|
||||
|
||||
// ExtraRootCAs, if non-nil, specifies additional trusted root CAs for TLS.
|
||||
ExtraRootCAs *x509.CertPool
|
||||
|
||||
// DialPlan, if set, contains instructions from the control server on
|
||||
// how to connect to it. If present, we will try the methods in this
|
||||
// plan before falling back to DNS.
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -86,6 +87,9 @@ type ClientOpts struct {
|
||||
// HealthTracker, if non-nil, is the health tracker to use.
|
||||
HealthTracker *health.Tracker
|
||||
|
||||
// ExtraRootCAs, if non-nil, specifies additional trusted root CAs for TLS.
|
||||
ExtraRootCAs *x509.CertPool
|
||||
|
||||
// DialPlan, if set, is a function that should return an explicit plan
|
||||
// on how to connect to the server.
|
||||
DialPlan func() *tailcfg.ControlDialPlan
|
||||
@@ -252,6 +256,7 @@ func (nc *Client) dial(ctx context.Context) (*Conn, error) {
|
||||
Logf: nc.logf,
|
||||
NetMon: nc.opts.NetMon,
|
||||
HealthTracker: nc.opts.HealthTracker,
|
||||
ExtraRootCAs: nc.opts.ExtraRootCAs,
|
||||
Clock: tstime.StdClock{},
|
||||
}
|
||||
clientConn, err := chd.Dial(ctx)
|
||||
|
||||
Reference in New Issue
Block a user