summaryrefslogtreecommitdiffhomepage
path: root/control/controlclient/direct.go
diff options
context:
space:
mode:
authorAndrew Dunham <andrew@du.nham.ca>2024-10-23 00:24:39 -0500
committerAndrew Dunham <andrew@du.nham.ca>2024-10-24 11:35:48 -0500
commitfc4048014ee26448a3b0c63e8c1a748f0e45e0b7 (patch)
tree9aedb9cc4ab23b51358c97e5b3ca42760fd25495 /control/controlclient/direct.go
parentb2665d9b89ee8c7be10a8e0a2fa36d35d21d8440 (diff)
downloadtailscale-andrew/keyfallback.tar.xz
tailscale-andrew/keyfallback.zip
control/keyfallback: add baked-in fallback for control keyandrew/keyfallback
Similar to how we bake in the DERPMap to ensure that we can reach the DERP servers if DNS isn't working, also bake in the control key for the default control server that we use if the control server is down. Updates #13890 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: I18ef0381e266bd3db10063685993bc3cb76b2f42
Diffstat (limited to 'control/controlclient/direct.go')
-rw-r--r--control/controlclient/direct.go40
1 files changed, 34 insertions, 6 deletions
diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go
index 9cbd0e14e..599e7fba5 100644
--- a/control/controlclient/direct.go
+++ b/control/controlclient/direct.go
@@ -29,9 +29,11 @@ import (
"go4.org/mem"
"tailscale.com/control/controlknobs"
+ "tailscale.com/control/keyfallback"
"tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/hostinfo"
+ "tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/logtail"
"tailscale.com/net/dnscache"
@@ -87,9 +89,10 @@ type Direct struct {
dialPlan ControlDialPlanner // can be nil
- mu sync.Mutex // mutex guards the following fields
- serverLegacyKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key; only used for signRegisterRequest on Windows now
- serverNoiseKey key.MachinePublic
+ mu sync.Mutex // mutex guards the following fields
+ serverLegacyKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key; only used for signRegisterRequest on Windows now
+ serverNoiseKey key.MachinePublic
+ usedFallbackNoiseKey bool // true if we used the baked-in fallback key
sfGroup singleflight.Group[struct{}, *NoiseClient] // protects noiseClient creation.
noiseClient *NoiseClient
@@ -498,6 +501,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
tryingNewKey := c.tryingNewKey
serverKey := c.serverLegacyKey
serverNoiseKey := c.serverNoiseKey
+ usedFallback := c.usedFallbackNoiseKey
authKey, isWrapped, wrappedSig, wrappedKey := tka.DecodeWrappedAuthkey(c.authKey, c.logf)
hi := c.hostInfoLocked()
backendLogID := hi.BackendLogID
@@ -528,7 +532,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
}
c.logf("doLogin(regen=%v, hasUrl=%v)", regen, opt.URL != "")
- if serverKey.IsZero() {
+ if serverKey.IsZero() || usedFallback {
keys, err := loadServerPubKeys(ctx, c.httpc, c.serverURL)
if err != nil && c.interceptedDial != nil && c.interceptedDial.Load() {
c.health.SetUnhealthy(macOSScreenTime, nil)
@@ -536,13 +540,21 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
c.health.SetHealthy(macOSScreenTime)
}
if err != nil {
- return regen, opt.URL, nil, err
+ if k2, err := c.getFallbackServerPubKeys(); err == nil {
+ keys = k2
+ usedFallback = true
+ } else {
+ return regen, opt.URL, nil, err
+ }
+ } else {
+ usedFallback = false
+ c.logf("control server key from %s: ts2021=%s", c.serverURL, keys.PublicKey.ShortString())
}
- c.logf("control server key from %s: ts2021=%s, legacy=%v", c.serverURL, keys.PublicKey.ShortString(), keys.LegacyPublicKey.ShortString())
c.mu.Lock()
c.serverLegacyKey = keys.LegacyPublicKey
c.serverNoiseKey = keys.PublicKey
+ c.usedFallbackNoiseKey = usedFallback
c.mu.Unlock()
serverKey = keys.LegacyPublicKey
serverNoiseKey = keys.PublicKey
@@ -751,6 +763,22 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
return false, resp.AuthURL, nil, nil
}
+func (c *Direct) getFallbackServerPubKeys() (*tailcfg.OverTLSPublicKeyResponse, error) {
+ // If we saw an error, try to use the fallback key if
+ // we're dialing the default control server.
+ if ipn.IsLoginServerSynonym(c.serverURL) {
+ return nil, errors.New("not using default control server")
+ }
+
+ kf, err := keyfallback.Get()
+ if err != nil {
+ return nil, err
+ }
+
+ c.logf("using fallback server key: ts2021=%s", kf.PublicKey.ShortString())
+ return kf, nil
+}
+
// newEndpoints acquires c.mu and sets the local port and endpoints and reports
// whether they've changed.
//