diff options
| author | Andrew Dunham <andrew@du.nham.ca> | 2024-10-23 00:24:39 -0500 |
|---|---|---|
| committer | Andrew Dunham <andrew@du.nham.ca> | 2024-10-24 11:35:48 -0500 |
| commit | fc4048014ee26448a3b0c63e8c1a748f0e45e0b7 (patch) | |
| tree | 9aedb9cc4ab23b51358c97e5b3ca42760fd25495 /control/controlclient/direct.go | |
| parent | b2665d9b89ee8c7be10a8e0a2fa36d35d21d8440 (diff) | |
| download | tailscale-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.go | 40 |
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. // |
