mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-05 23:21:00 +00:00
tsd, ipnlocal, etc: add tsd.System.HealthTracker, start some plumbing
This adds a health.Tracker to tsd.System, accessible via a new tsd.System.HealthTracker method. In the future, that new method will return a tsd.System-specific HealthTracker, so multiple tsnet.Servers in the same process are isolated. For now, though, it just always returns the temporary health.Global value. That permits incremental plumbing over a number of changes. When the second to last health.Global reference is gone, then the tsd.System.HealthTracker implementation can return a private Tracker. The primary plumbing this does is adding it to LocalBackend and its dozen and change health calls. A few misc other callers are also plumbed. Subsequent changes will flesh out other parts of the tree (magicsock, controlclient, etc). Updates #11874 Updates #4136 Change-Id: Id51e73cfc8a39110425b6dc19d18b3975eac75ce Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
cb66952a0d
commit
723c775dbb
@ -89,7 +89,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
|||||||
tailscale.com/disco from tailscale.com/derp
|
tailscale.com/disco from tailscale.com/derp
|
||||||
tailscale.com/drive from tailscale.com/client/tailscale+
|
tailscale.com/drive from tailscale.com/client/tailscale+
|
||||||
tailscale.com/envknob from tailscale.com/client/tailscale+
|
tailscale.com/envknob from tailscale.com/client/tailscale+
|
||||||
tailscale.com/health from tailscale.com/net/tlsdial
|
tailscale.com/health from tailscale.com/net/tlsdial+
|
||||||
tailscale.com/hostinfo from tailscale.com/net/interfaces+
|
tailscale.com/hostinfo from tailscale.com/net/interfaces+
|
||||||
tailscale.com/ipn from tailscale.com/client/tailscale
|
tailscale.com/ipn from tailscale.com/client/tailscale
|
||||||
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
|
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
|
||||||
|
@ -88,7 +88,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/disco from tailscale.com/derp
|
tailscale.com/disco from tailscale.com/derp
|
||||||
tailscale.com/drive from tailscale.com/client/tailscale+
|
tailscale.com/drive from tailscale.com/client/tailscale+
|
||||||
tailscale.com/envknob from tailscale.com/client/tailscale+
|
tailscale.com/envknob from tailscale.com/client/tailscale+
|
||||||
tailscale.com/health from tailscale.com/net/tlsdial
|
tailscale.com/health from tailscale.com/net/tlsdial+
|
||||||
tailscale.com/health/healthmsg from tailscale.com/cmd/tailscale/cli
|
tailscale.com/health/healthmsg from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/hostinfo from tailscale.com/client/web+
|
tailscale.com/hostinfo from tailscale.com/client/web+
|
||||||
tailscale.com/ipn from tailscale.com/client/tailscale+
|
tailscale.com/ipn from tailscale.com/client/tailscale+
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tailscale.com/derp/derphttp"
|
"tailscale.com/derp/derphttp"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
@ -157,6 +158,7 @@ func getURL(ctx context.Context, urlStr string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkDerp(ctx context.Context, derpRegion string) (err error) {
|
func checkDerp(ctx context.Context, derpRegion string) (err error) {
|
||||||
|
ht := new(health.Tracker)
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", ipn.DefaultControlURL+"/derpmap/default", nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", ipn.DefaultControlURL+"/derpmap/default", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create derp map request: %w", err)
|
return fmt.Errorf("create derp map request: %w", err)
|
||||||
@ -195,6 +197,8 @@ func checkDerp(ctx context.Context, derpRegion string) (err error) {
|
|||||||
|
|
||||||
c1 := derphttp.NewRegionClient(priv1, log.Printf, nil, getRegion)
|
c1 := derphttp.NewRegionClient(priv1, log.Printf, nil, getRegion)
|
||||||
c2 := derphttp.NewRegionClient(priv2, log.Printf, nil, getRegion)
|
c2 := derphttp.NewRegionClient(priv2, log.Printf, nil, getRegion)
|
||||||
|
c1.HealthTracker = ht
|
||||||
|
c2.HealthTracker = ht
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c1.Close()
|
c1.Close()
|
||||||
|
@ -248,7 +248,7 @@ func NewDirect(opts Options) (*Direct, error) {
|
|||||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
tr.Proxy = tshttpproxy.ProxyFromEnvironment
|
tr.Proxy = tshttpproxy.ProxyFromEnvironment
|
||||||
tshttpproxy.SetTransportGetProxyConnectHeader(tr)
|
tshttpproxy.SetTransportGetProxyConnectHeader(tr)
|
||||||
tr.TLSClientConfig = tlsdial.Config(serverURL.Hostname(), tr.TLSClientConfig)
|
tr.TLSClientConfig = tlsdial.Config(serverURL.Hostname(), health.Global, tr.TLSClientConfig)
|
||||||
tr.DialContext = dnscache.Dialer(opts.Dialer.SystemDial, dnsCache)
|
tr.DialContext = dnscache.Dialer(opts.Dialer.SystemDial, dnsCache)
|
||||||
tr.DialTLSContext = dnscache.TLSDialer(opts.Dialer.SystemDial, dnsCache, tr.TLSClientConfig)
|
tr.DialTLSContext = dnscache.TLSDialer(opts.Dialer.SystemDial, dnsCache, tr.TLSClientConfig)
|
||||||
tr.ForceAttemptHTTP2 = true
|
tr.ForceAttemptHTTP2 = true
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/control/controlbase"
|
"tailscale.com/control/controlbase"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/net/dnscache"
|
"tailscale.com/net/dnscache"
|
||||||
"tailscale.com/net/dnsfallback"
|
"tailscale.com/net/dnsfallback"
|
||||||
"tailscale.com/net/netutil"
|
"tailscale.com/net/netutil"
|
||||||
@ -433,7 +434,7 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr,
|
|||||||
// Disable HTTP2, since h2 can't do protocol switching.
|
// Disable HTTP2, since h2 can't do protocol switching.
|
||||||
tr.TLSClientConfig.NextProtos = []string{}
|
tr.TLSClientConfig.NextProtos = []string{}
|
||||||
tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
|
tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
|
||||||
tr.TLSClientConfig = tlsdial.Config(a.Hostname, tr.TLSClientConfig)
|
tr.TLSClientConfig = tlsdial.Config(a.Hostname, health.Global, tr.TLSClientConfig)
|
||||||
if !tr.TLSClientConfig.InsecureSkipVerify {
|
if !tr.TLSClientConfig.InsecureSkipVerify {
|
||||||
panic("unexpected") // should be set by tlsdial.Config
|
panic("unexpected") // should be set by tlsdial.Config
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"tailscale.com/derp"
|
"tailscale.com/derp"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/net/dnscache"
|
"tailscale.com/net/dnscache"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
"tailscale.com/net/netns"
|
"tailscale.com/net/netns"
|
||||||
@ -51,10 +52,11 @@ import (
|
|||||||
// Send/Recv will completely re-establish the connection (unless Close
|
// Send/Recv will completely re-establish the connection (unless Close
|
||||||
// has been called).
|
// has been called).
|
||||||
type Client struct {
|
type Client struct {
|
||||||
TLSConfig *tls.Config // optional; nil means default
|
TLSConfig *tls.Config // optional; nil means default
|
||||||
DNSCache *dnscache.Resolver // optional; nil means no caching
|
HealthTracker *health.Tracker // optional; used if non-nil only
|
||||||
MeshKey string // optional; for trusted clients
|
DNSCache *dnscache.Resolver // optional; nil means no caching
|
||||||
IsProber bool // optional; for probers to optional declare themselves as such
|
MeshKey string // optional; for trusted clients
|
||||||
|
IsProber bool // optional; for probers to optional declare themselves as such
|
||||||
|
|
||||||
// WatchConnectionChanges is whether the client wishes to subscribe to
|
// WatchConnectionChanges is whether the client wishes to subscribe to
|
||||||
// notifications about clients connecting & disconnecting.
|
// notifications about clients connecting & disconnecting.
|
||||||
@ -115,6 +117,7 @@ func (c *Client) String() string {
|
|||||||
// NewRegionClient returns a new DERP-over-HTTP client. It connects lazily.
|
// NewRegionClient returns a new DERP-over-HTTP client. It connects lazily.
|
||||||
// To trigger a connection, use Connect.
|
// To trigger a connection, use Connect.
|
||||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||||
|
// The healthTracker parameter is also optional.
|
||||||
func NewRegionClient(privateKey key.NodePrivate, logf logger.Logf, netMon *netmon.Monitor, getRegion func() *tailcfg.DERPRegion) *Client {
|
func NewRegionClient(privateKey key.NodePrivate, logf logger.Logf, netMon *netmon.Monitor, getRegion func() *tailcfg.DERPRegion) *Client {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
c := &Client{
|
c := &Client{
|
||||||
@ -612,7 +615,7 @@ func (c *Client) dialRegion(ctx context.Context, reg *tailcfg.DERPRegion) (net.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) tlsClient(nc net.Conn, node *tailcfg.DERPNode) *tls.Conn {
|
func (c *Client) tlsClient(nc net.Conn, node *tailcfg.DERPNode) *tls.Conn {
|
||||||
tlsConf := tlsdial.Config(c.tlsServerName(node), c.TLSConfig)
|
tlsConf := tlsdial.Config(c.tlsServerName(node), c.HealthTracker, c.TLSConfig)
|
||||||
if node != nil {
|
if node != nil {
|
||||||
if node.InsecureForTests {
|
if node.InsecureForTests {
|
||||||
tlsConf.InsecureSkipVerify = true
|
tlsConf.InsecureSkipVerify = true
|
||||||
|
@ -30,8 +30,11 @@ var (
|
|||||||
|
|
||||||
// Global is a global health tracker for the process.
|
// Global is a global health tracker for the process.
|
||||||
//
|
//
|
||||||
// TODO(bradfitz): move this to tsd.System so a process can have multiple
|
// TODO(bradfitz): finish moving all reference to this plumb it (ultimately out
|
||||||
// tsnet/etc instances with their own health trackers.
|
// from tsd.System) so a process can have multiple tsnet/etc instances with
|
||||||
|
// their own health trackers. But for now (2024-04-25), the tsd.System value
|
||||||
|
// given out is just this one, until that's the only remaining Global reference
|
||||||
|
// remaining.
|
||||||
var Global = new(Tracker)
|
var Global = new(Tracker)
|
||||||
|
|
||||||
type Tracker struct {
|
type Tracker struct {
|
||||||
|
@ -170,6 +170,7 @@ type LocalBackend struct {
|
|||||||
keyLogf logger.Logf // for printing list of peers on change
|
keyLogf logger.Logf // for printing list of peers on change
|
||||||
statsLogf logger.Logf // for printing peers stats on change
|
statsLogf logger.Logf // for printing peers stats on change
|
||||||
sys *tsd.System
|
sys *tsd.System
|
||||||
|
health *health.Tracker // always non-nil
|
||||||
e wgengine.Engine // non-nil; TODO(bradfitz): remove; use sys
|
e wgengine.Engine // non-nil; TODO(bradfitz): remove; use sys
|
||||||
store ipn.StateStore // non-nil; TODO(bradfitz): remove; use sys
|
store ipn.StateStore // non-nil; TODO(bradfitz): remove; use sys
|
||||||
dialer *tsdial.Dialer // non-nil; TODO(bradfitz): remove; use sys
|
dialer *tsdial.Dialer // non-nil; TODO(bradfitz): remove; use sys
|
||||||
@ -386,6 +387,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||||||
keyLogf: logger.LogOnChange(logf, 5*time.Minute, clock.Now),
|
keyLogf: logger.LogOnChange(logf, 5*time.Minute, clock.Now),
|
||||||
statsLogf: logger.LogOnChange(logf, 5*time.Minute, clock.Now),
|
statsLogf: logger.LogOnChange(logf, 5*time.Minute, clock.Now),
|
||||||
sys: sys,
|
sys: sys,
|
||||||
|
health: sys.HealthTracker(),
|
||||||
conf: sys.InitialConfig,
|
conf: sys.InitialConfig,
|
||||||
e: e,
|
e: e,
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
@ -426,7 +428,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||||||
b.linkChange(&netmon.ChangeDelta{New: netMon.InterfaceState()})
|
b.linkChange(&netmon.ChangeDelta{New: netMon.InterfaceState()})
|
||||||
b.unregisterNetMon = netMon.RegisterChangeCallback(b.linkChange)
|
b.unregisterNetMon = netMon.RegisterChangeCallback(b.linkChange)
|
||||||
|
|
||||||
b.unregisterHealthWatch = health.Global.RegisterWatcher(b.onHealthChange)
|
b.unregisterHealthWatch = b.health.RegisterWatcher(b.onHealthChange)
|
||||||
|
|
||||||
if tunWrap, ok := b.sys.Tun.GetOK(); ok {
|
if tunWrap, ok := b.sys.Tun.GetOK(); ok {
|
||||||
tunWrap.PeerAPIPort = b.GetPeerAPIPort
|
tunWrap.PeerAPIPort = b.GetPeerAPIPort
|
||||||
@ -625,7 +627,7 @@ func (b *LocalBackend) linkChange(delta *netmon.ChangeDelta) {
|
|||||||
// If the local network configuration has changed, our filter may
|
// If the local network configuration has changed, our filter may
|
||||||
// need updating to tweak default routes.
|
// need updating to tweak default routes.
|
||||||
b.updateFilterLocked(b.netMap, b.pm.CurrentPrefs())
|
b.updateFilterLocked(b.netMap, b.pm.CurrentPrefs())
|
||||||
updateExitNodeUsageWarning(b.pm.CurrentPrefs(), delta.New)
|
updateExitNodeUsageWarning(b.pm.CurrentPrefs(), delta.New, b.health)
|
||||||
|
|
||||||
if peerAPIListenAsync && b.netMap != nil && b.state == ipn.Running {
|
if peerAPIListenAsync && b.netMap != nil && b.state == ipn.Running {
|
||||||
want := b.netMap.GetAddresses().Len()
|
want := b.netMap.GetAddresses().Len()
|
||||||
@ -761,7 +763,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := health.Global.OverallError(); err != nil {
|
if err := b.health.OverallError(); err != nil {
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case multierr.Error:
|
case multierr.Error:
|
||||||
for _, err := range e.Errors() {
|
for _, err := range e.Errors() {
|
||||||
@ -820,7 +822,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
|
|
||||||
sb.MutateSelfStatus(func(ss *ipnstate.PeerStatus) {
|
sb.MutateSelfStatus(func(ss *ipnstate.PeerStatus) {
|
||||||
ss.OS = version.OS()
|
ss.OS = version.OS()
|
||||||
ss.Online = health.Global.GetInPollNetMap()
|
ss.Online = b.health.GetInPollNetMap()
|
||||||
if b.netMap != nil {
|
if b.netMap != nil {
|
||||||
ss.InNetworkMap = true
|
ss.InNetworkMap = true
|
||||||
if hi := b.netMap.SelfNode.Hostinfo(); hi.Valid() {
|
if hi := b.netMap.SelfNode.Hostinfo(); hi.Valid() {
|
||||||
@ -1221,7 +1223,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
|
|||||||
if st.NetMap != nil {
|
if st.NetMap != nil {
|
||||||
if envknob.NoLogsNoSupport() && st.NetMap.HasCap(tailcfg.CapabilityDataPlaneAuditLogs) {
|
if envknob.NoLogsNoSupport() && st.NetMap.HasCap(tailcfg.CapabilityDataPlaneAuditLogs) {
|
||||||
msg := "tailnet requires logging to be enabled. Remove --no-logs-no-support from tailscaled command line."
|
msg := "tailnet requires logging to be enabled. Remove --no-logs-no-support from tailscaled command line."
|
||||||
health.Global.SetLocalLogConfigHealth(errors.New(msg))
|
b.health.SetLocalLogConfigHealth(errors.New(msg))
|
||||||
// Connecting to this tailnet without logging is forbidden; boot us outta here.
|
// Connecting to this tailnet without logging is forbidden; boot us outta here.
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
prefs.WantRunning = false
|
prefs.WantRunning = false
|
||||||
@ -1851,10 +1853,10 @@ func (b *LocalBackend) updateFilterLocked(netMap *netmap.NetworkMap, prefs ipn.P
|
|||||||
|
|
||||||
if packetFilterPermitsUnlockedNodes(b.peers, packetFilter) {
|
if packetFilterPermitsUnlockedNodes(b.peers, packetFilter) {
|
||||||
err := errors.New("server sent invalid packet filter permitting traffic to unlocked nodes; rejecting all packets for safety")
|
err := errors.New("server sent invalid packet filter permitting traffic to unlocked nodes; rejecting all packets for safety")
|
||||||
health.Global.SetWarnable(warnInvalidUnsignedNodes, err)
|
b.health.SetWarnable(warnInvalidUnsignedNodes, err)
|
||||||
packetFilter = nil
|
packetFilter = nil
|
||||||
} else {
|
} else {
|
||||||
health.Global.SetWarnable(warnInvalidUnsignedNodes, nil)
|
b.health.SetWarnable(warnInvalidUnsignedNodes, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if prefs.Valid() {
|
if prefs.Valid() {
|
||||||
@ -3048,7 +3050,7 @@ var warnExitNodeUsage = health.NewWarnable(health.WithConnectivityImpact())
|
|||||||
|
|
||||||
// updateExitNodeUsageWarning updates a warnable meant to notify users of
|
// updateExitNodeUsageWarning updates a warnable meant to notify users of
|
||||||
// configuration issues that could break exit node usage.
|
// configuration issues that could break exit node usage.
|
||||||
func updateExitNodeUsageWarning(p ipn.PrefsView, state *interfaces.State) {
|
func updateExitNodeUsageWarning(p ipn.PrefsView, state *interfaces.State, health *health.Tracker) {
|
||||||
var result error
|
var result error
|
||||||
if p.ExitNodeIP().IsValid() || p.ExitNodeID() != "" {
|
if p.ExitNodeIP().IsValid() || p.ExitNodeID() != "" {
|
||||||
warn, _ := netutil.CheckReversePathFiltering(state)
|
warn, _ := netutil.CheckReversePathFiltering(state)
|
||||||
@ -3057,7 +3059,7 @@ func updateExitNodeUsageWarning(p ipn.PrefsView, state *interfaces.State) {
|
|||||||
result = fmt.Errorf("%s: %v, %s", healthmsg.WarnExitNodeUsage, warn, comment)
|
result = fmt.Errorf("%s: %v, %s", healthmsg.WarnExitNodeUsage, warn, comment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
health.Global.SetWarnable(warnExitNodeUsage, result)
|
health.SetWarnable(warnExitNodeUsage, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) checkExitNodePrefsLocked(p *ipn.Prefs) error {
|
func (b *LocalBackend) checkExitNodePrefsLocked(p *ipn.Prefs) error {
|
||||||
@ -4254,7 +4256,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
|
|||||||
|
|
||||||
// prefs may change irrespective of state; WantRunning should be explicitly
|
// prefs may change irrespective of state; WantRunning should be explicitly
|
||||||
// set before potential early return even if the state is unchanged.
|
// set before potential early return even if the state is unchanged.
|
||||||
health.Global.SetIPNState(newState.String(), prefs.Valid() && prefs.WantRunning())
|
b.health.SetIPNState(newState.String(), prefs.Valid() && prefs.WantRunning())
|
||||||
if oldState == newState {
|
if oldState == newState {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -4692,9 +4694,9 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
|
|||||||
b.pauseOrResumeControlClientLocked()
|
b.pauseOrResumeControlClientLocked()
|
||||||
|
|
||||||
if nm != nil {
|
if nm != nil {
|
||||||
health.Global.SetControlHealth(nm.ControlHealth)
|
b.health.SetControlHealth(nm.ControlHealth)
|
||||||
} else {
|
} else {
|
||||||
health.Global.SetControlHealth(nil)
|
b.health.SetControlHealth(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if file sharing is enabled
|
// Determine if file sharing is enabled
|
||||||
@ -5679,9 +5681,9 @@ var warnSSHSELinux = health.NewWarnable()
|
|||||||
|
|
||||||
func (b *LocalBackend) updateSELinuxHealthWarning() {
|
func (b *LocalBackend) updateSELinuxHealthWarning() {
|
||||||
if hostinfo.IsSELinuxEnforcing() {
|
if hostinfo.IsSELinuxEnforcing() {
|
||||||
health.Global.SetWarnable(warnSSHSELinux, errors.New("SELinux is enabled; Tailscale SSH may not work. See https://tailscale.com/s/ssh-selinux"))
|
b.health.SetWarnable(warnSSHSELinux, errors.New("SELinux is enabled; Tailscale SSH may not work. See https://tailscale.com/s/ssh-selinux"))
|
||||||
} else {
|
} else {
|
||||||
health.Global.SetWarnable(warnSSHSELinux, nil)
|
b.health.SetWarnable(warnSSHSELinux, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5908,7 +5910,7 @@ func (b *LocalBackend) resetForProfileChangeLockedOnEntry(unlock unlockOnce) err
|
|||||||
b.lastServeConfJSON = mem.B(nil)
|
b.lastServeConfJSON = mem.B(nil)
|
||||||
b.serveConfig = ipn.ServeConfigView{}
|
b.serveConfig = ipn.ServeConfigView{}
|
||||||
b.enterStateLockedOnEntry(ipn.NoState, unlock) // Reset state; releases b.mu
|
b.enterStateLockedOnEntry(ipn.NoState, unlock) // Reset state; releases b.mu
|
||||||
health.Global.SetLocalLogConfigHealth(nil)
|
b.health.SetLocalLogConfigHealth(nil)
|
||||||
return b.Start(ipn.Options{})
|
return b.Start(ipn.Options{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
"tailscale.com/atomicfile"
|
"tailscale.com/atomicfile"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/log/filelogger"
|
"tailscale.com/log/filelogger"
|
||||||
"tailscale.com/logtail"
|
"tailscale.com/logtail"
|
||||||
"tailscale.com/logtail/filch"
|
"tailscale.com/logtail/filch"
|
||||||
@ -782,7 +783,7 @@ func NewLogtailTransport(host string, netMon *netmon.Monitor, logf logger.Logf)
|
|||||||
tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{}
|
tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{}
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.TLSClientConfig = tlsdial.Config(host, tr.TLSClientConfig)
|
tr.TLSClientConfig = tlsdial.Config(host, health.Global, tr.TLSClientConfig)
|
||||||
|
|
||||||
return tr
|
return tr
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/atomicfile"
|
"tailscale.com/atomicfile"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/net/dns/recursive"
|
"tailscale.com/net/dns/recursive"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
"tailscale.com/net/netns"
|
"tailscale.com/net/netns"
|
||||||
@ -64,9 +65,10 @@ func MakeLookupFunc(logf logger.Logf, netMon *netmon.Monitor) func(ctx context.C
|
|||||||
// fallbackResolver contains the state and configuration for a DNS resolution
|
// fallbackResolver contains the state and configuration for a DNS resolution
|
||||||
// function.
|
// function.
|
||||||
type fallbackResolver struct {
|
type fallbackResolver struct {
|
||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
netMon *netmon.Monitor // or nil
|
netMon *netmon.Monitor // or nil
|
||||||
sf singleflight.Group[string, resolveResult]
|
healthTracker *health.Tracker // or nil
|
||||||
|
sf singleflight.Group[string, resolveResult]
|
||||||
|
|
||||||
// for tests
|
// for tests
|
||||||
waitForCompare bool
|
waitForCompare bool
|
||||||
@ -79,7 +81,7 @@ func (fr *fallbackResolver) Lookup(ctx context.Context, host string) ([]netip.Ad
|
|||||||
// recursive resolver. (tailscale/corp#15261) In the future, we might
|
// recursive resolver. (tailscale/corp#15261) In the future, we might
|
||||||
// change the default (the opt.Bool being unset) to mean enabled.
|
// change the default (the opt.Bool being unset) to mean enabled.
|
||||||
if disableRecursiveResolver() || !optRecursiveResolver().EqualBool(true) {
|
if disableRecursiveResolver() || !optRecursiveResolver().EqualBool(true) {
|
||||||
return lookup(ctx, host, fr.logf, fr.netMon)
|
return lookup(ctx, host, fr.logf, fr.healthTracker, fr.netMon)
|
||||||
}
|
}
|
||||||
|
|
||||||
addrsCh := make(chan []netip.Addr, 1)
|
addrsCh := make(chan []netip.Addr, 1)
|
||||||
@ -99,7 +101,7 @@ func (fr *fallbackResolver) Lookup(ctx context.Context, host string) ([]netip.Ad
|
|||||||
go fr.compareWithRecursive(ctx, addrsCh, host)
|
go fr.compareWithRecursive(ctx, addrsCh, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs, err := lookup(ctx, host, fr.logf, fr.netMon)
|
addrs, err := lookup(ctx, host, fr.logf, fr.healthTracker, fr.netMon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
addrsCh <- nil
|
addrsCh <- nil
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -207,7 +209,7 @@ func (fr *fallbackResolver) compareWithRecursive(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookup(ctx context.Context, host string, logf logger.Logf, netMon *netmon.Monitor) ([]netip.Addr, error) {
|
func lookup(ctx context.Context, host string, logf logger.Logf, ht *health.Tracker, netMon *netmon.Monitor) ([]netip.Addr, error) {
|
||||||
if ip, err := netip.ParseAddr(host); err == nil && ip.IsValid() {
|
if ip, err := netip.ParseAddr(host); err == nil && ip.IsValid() {
|
||||||
return []netip.Addr{ip}, nil
|
return []netip.Addr{ip}, nil
|
||||||
}
|
}
|
||||||
@ -255,7 +257,7 @@ func lookup(ctx context.Context, host string, logf logger.Logf, netMon *netmon.M
|
|||||||
logf("trying bootstrapDNS(%q, %q) for %q ...", cand.dnsName, cand.ip, host)
|
logf("trying bootstrapDNS(%q, %q) for %q ...", cand.dnsName, cand.ip, host)
|
||||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
dm, err := bootstrapDNSMap(ctx, cand.dnsName, cand.ip, host, logf, netMon)
|
dm, err := bootstrapDNSMap(ctx, cand.dnsName, cand.ip, host, logf, ht, netMon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("bootstrapDNS(%q, %q) for %q error: %v", cand.dnsName, cand.ip, host, err)
|
logf("bootstrapDNS(%q, %q) for %q error: %v", cand.dnsName, cand.ip, host, err)
|
||||||
continue
|
continue
|
||||||
@ -274,14 +276,16 @@ func lookup(ctx context.Context, host string, logf logger.Logf, netMon *netmon.M
|
|||||||
|
|
||||||
// serverName and serverIP of are, say, "derpN.tailscale.com".
|
// serverName and serverIP of are, say, "derpN.tailscale.com".
|
||||||
// queryName is the name being sought (e.g. "controlplane.tailscale.com"), passed as hint.
|
// queryName is the name being sought (e.g. "controlplane.tailscale.com"), passed as hint.
|
||||||
func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr, queryName string, logf logger.Logf, netMon *netmon.Monitor) (dnsMap, error) {
|
//
|
||||||
|
// ht may be nil.
|
||||||
|
func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr, queryName string, logf logger.Logf, ht *health.Tracker, netMon *netmon.Monitor) (dnsMap, error) {
|
||||||
dialer := netns.NewDialer(logf, netMon)
|
dialer := netns.NewDialer(logf, netMon)
|
||||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
tr.Proxy = tshttpproxy.ProxyFromEnvironment
|
tr.Proxy = tshttpproxy.ProxyFromEnvironment
|
||||||
tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
|
tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
|
||||||
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(serverIP.String(), "443"))
|
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(serverIP.String(), "443"))
|
||||||
}
|
}
|
||||||
tr.TLSClientConfig = tlsdial.Config(serverName, tr.TLSClientConfig)
|
tr.TLSClientConfig = tlsdial.Config(serverName, ht, tr.TLSClientConfig)
|
||||||
c := &http.Client{Transport: tr}
|
c := &http.Client{Transport: tr}
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://"+serverName+"/bootstrap-dns?q="+url.QueryEscape(queryName), nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", "https://"+serverName+"/bootstrap-dns?q="+url.QueryEscape(queryName), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -46,7 +46,8 @@ var tlsdialWarningPrinted sync.Map // map[string]bool
|
|||||||
// Config returns a tls.Config for connecting to a server.
|
// Config returns a tls.Config for connecting to a server.
|
||||||
// If base is non-nil, it's cloned as the base config before
|
// If base is non-nil, it's cloned as the base config before
|
||||||
// being configured and returned.
|
// being configured and returned.
|
||||||
func Config(host string, base *tls.Config) *tls.Config {
|
// If ht is non-nil, it's used to report health errors.
|
||||||
|
func Config(host string, ht *health.Tracker, base *tls.Config) *tls.Config {
|
||||||
var conf *tls.Config
|
var conf *tls.Config
|
||||||
if base == nil {
|
if base == nil {
|
||||||
conf = new(tls.Config)
|
conf = new(tls.Config)
|
||||||
@ -78,12 +79,14 @@ func Config(host string, base *tls.Config) *tls.Config {
|
|||||||
conf.VerifyConnection = func(cs tls.ConnectionState) error {
|
conf.VerifyConnection = func(cs tls.ConnectionState) error {
|
||||||
// Perform some health checks on this certificate before we do
|
// Perform some health checks on this certificate before we do
|
||||||
// any verification.
|
// any verification.
|
||||||
if certIsSelfSigned(cs.PeerCertificates[0]) {
|
if ht != nil {
|
||||||
// Self-signed certs are never valid.
|
if certIsSelfSigned(cs.PeerCertificates[0]) {
|
||||||
health.Global.SetTLSConnectionError(cs.ServerName, fmt.Errorf("certificate is self-signed"))
|
// Self-signed certs are never valid.
|
||||||
} else {
|
ht.SetTLSConnectionError(cs.ServerName, fmt.Errorf("certificate is self-signed"))
|
||||||
// Ensure we clear any error state for this ServerName.
|
} else {
|
||||||
health.Global.SetTLSConnectionError(cs.ServerName, nil)
|
// Ensure we clear any error state for this ServerName.
|
||||||
|
ht.SetTLSConnectionError(cs.ServerName, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// First try doing x509 verification with the system's
|
// First try doing x509 verification with the system's
|
||||||
@ -204,7 +207,7 @@ func NewTransport() *http.Transport {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var d tls.Dialer
|
var d tls.Dialer
|
||||||
d.Config = Config(host, nil)
|
d.Config = Config(host, nil, nil)
|
||||||
return d.DialContext(ctx, network, addr)
|
return d.DialContext(ctx, network, addr)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/health"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resetOnce() {
|
func resetOnce() {
|
||||||
@ -105,7 +107,8 @@ func TestFallbackRootWorks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
DisableKeepAlives: true, // for test cleanup ease
|
DisableKeepAlives: true, // for test cleanup ease
|
||||||
}
|
}
|
||||||
tr.TLSClientConfig = Config("tlsdial.test", tr.TLSClientConfig)
|
ht := new(health.Tracker)
|
||||||
|
tr.TLSClientConfig = Config("tlsdial.test", ht, tr.TLSClientConfig)
|
||||||
c := &http.Client{Transport: tr}
|
c := &http.Client{Transport: tr}
|
||||||
|
|
||||||
ctr0 := atomic.LoadInt32(&counterFallbackOK)
|
ctr0 := atomic.LoadInt32(&counterFallbackOK)
|
||||||
|
16
tsd/tsd.go
16
tsd/tsd.go
@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/control/controlknobs"
|
"tailscale.com/control/controlknobs"
|
||||||
"tailscale.com/drive"
|
"tailscale.com/drive"
|
||||||
|
"tailscale.com/health"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/conffile"
|
"tailscale.com/ipn/conffile"
|
||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
@ -63,6 +64,8 @@ type System struct {
|
|||||||
|
|
||||||
controlKnobs controlknobs.Knobs
|
controlKnobs controlknobs.Knobs
|
||||||
proxyMap proxymap.Mapper
|
proxyMap proxymap.Mapper
|
||||||
|
|
||||||
|
healthTracker health.Tracker
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetstackImpl is the interface that *netstack.Impl implements.
|
// NetstackImpl is the interface that *netstack.Impl implements.
|
||||||
@ -134,6 +137,19 @@ func (s *System) ProxyMapper() *proxymap.Mapper {
|
|||||||
return &s.proxyMap
|
return &s.proxyMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HealthTracker returns the system health tracker.
|
||||||
|
func (s *System) HealthTracker() *health.Tracker {
|
||||||
|
// TODO(bradfitz): plumb the tsd.System.HealthTracker() value
|
||||||
|
// everywhere and then then remove this use of the global
|
||||||
|
// and remove health.Global entirely. But for now we keep
|
||||||
|
// the two in sync during plumbing.
|
||||||
|
const stillPlumbing = true
|
||||||
|
if stillPlumbing {
|
||||||
|
return health.Global
|
||||||
|
}
|
||||||
|
return &s.healthTracker
|
||||||
|
}
|
||||||
|
|
||||||
// SubSystem represents some subsystem of the Tailscale node daemon.
|
// SubSystem represents some subsystem of the Tailscale node daemon.
|
||||||
//
|
//
|
||||||
// A subsystem can be set to a value, and then later retrieved. A subsystem
|
// A subsystem can be set to a value, and then later retrieved. A subsystem
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/drive/driveimpl"
|
_ "tailscale.com/drive/driveimpl"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
|
_ "tailscale.com/health"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
_ "tailscale.com/ipn/conffile"
|
_ "tailscale.com/ipn/conffile"
|
||||||
_ "tailscale.com/ipn/ipnlocal"
|
_ "tailscale.com/ipn/ipnlocal"
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/drive/driveimpl"
|
_ "tailscale.com/drive/driveimpl"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
|
_ "tailscale.com/health"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
_ "tailscale.com/ipn/conffile"
|
_ "tailscale.com/ipn/conffile"
|
||||||
_ "tailscale.com/ipn/ipnlocal"
|
_ "tailscale.com/ipn/ipnlocal"
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/drive/driveimpl"
|
_ "tailscale.com/drive/driveimpl"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
|
_ "tailscale.com/health"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
_ "tailscale.com/ipn/conffile"
|
_ "tailscale.com/ipn/conffile"
|
||||||
_ "tailscale.com/ipn/ipnlocal"
|
_ "tailscale.com/ipn/ipnlocal"
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/drive/driveimpl"
|
_ "tailscale.com/drive/driveimpl"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
|
_ "tailscale.com/health"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
_ "tailscale.com/ipn/conffile"
|
_ "tailscale.com/ipn/conffile"
|
||||||
_ "tailscale.com/ipn/ipnlocal"
|
_ "tailscale.com/ipn/ipnlocal"
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/drive/driveimpl"
|
_ "tailscale.com/drive/driveimpl"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
|
_ "tailscale.com/health"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
_ "tailscale.com/ipn/conffile"
|
_ "tailscale.com/ipn/conffile"
|
||||||
_ "tailscale.com/ipn/ipnlocal"
|
_ "tailscale.com/ipn/ipnlocal"
|
||||||
|
@ -400,6 +400,7 @@ func (c *Conn) derpWriteChanOfAddr(addr netip.AddrPort, peer key.NodePublic) cha
|
|||||||
}
|
}
|
||||||
return derpMap.Regions[regionID]
|
return derpMap.Regions[regionID]
|
||||||
})
|
})
|
||||||
|
dc.HealthTracker = health.Global
|
||||||
|
|
||||||
dc.SetCanAckPings(true)
|
dc.SetCanAckPings(true)
|
||||||
dc.NotePreferred(c.myDerp == regionID)
|
dc.NotePreferred(c.myDerp == regionID)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user