diff options
| author | Raj Singh <raj@tailscale.com> | 2026-03-31 12:50:16 -0500 |
|---|---|---|
| committer | Raj Singh <raj@tailscale.com> | 2026-03-31 12:51:43 -0500 |
| commit | d88ee20a98ca42675604225bb42d40cb9cb3551a (patch) | |
| tree | f6737d8c0db8e10daa5ea690aa36f7b1fd2a1c3c /control/controlclient | |
| parent | 70afbac0534e64bac192ce677d4359328ee9890f (diff) | |
| download | tailscale-rajsingh/persist-nodekey-before-register.tar.xz tailscale-rajsingh/persist-nodekey-before-register.zip | |
controlclient: persist node key after login, don't wait for netmaprajsingh/persist-nodekey-before-register
sendStatus only included Persist in the Status when a netmap was
available (nm != nil && loggedIn && inMapPoll). This meant the node
key was not written to the state store until the first map response
arrived. If the process was killed after a successful registration
but before the first netmap, the key was lost and a fresh one
generated on restart.
Decouple persist from the netmap gate so it flows to the state store
as soon as login succeeds.
Fixes #19149
Signed-off-by: Raj Singh <raj@tailscale.com>
Diffstat (limited to 'control/controlclient')
| -rw-r--r-- | control/controlclient/auto.go | 5 | ||||
| -rw-r--r-- | control/controlclient/direct.go | 16 |
2 files changed, 3 insertions, 18 deletions
diff --git a/control/controlclient/auto.go b/control/controlclient/auto.go index b087e1444..d7da21932 100644 --- a/control/controlclient/auto.go +++ b/control/controlclient/auto.go @@ -625,9 +625,10 @@ func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkM c.logf("[v1] sendStatus: %s: loggedIn=%v inMapPoll=%v", who, loggedIn, inMapPoll) var p persist.PersistView - if nm != nil && loggedIn && inMapPoll { + if loggedIn { p = c.direct.GetPersist() - } else { + } + if !(nm != nil && loggedIn && inMapPoll) { // don't send netmap status, as it's misleading when we're // not logged in. nm = nil diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index cbbf2bdfe..3b4e6ba9b 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -79,7 +79,6 @@ type Direct struct { autoUpdatePub *eventbus.Publisher[AutoUpdate] controlTimePub *eventbus.Publisher[ControlTime] getMachinePrivKey func() (key.MachinePrivate, error) - persistState func(persist.PersistView) // or nil; called before RegisterRequest to persist new node key debugFlags []string pinger Pinger popBrowser func(url string) // or nil @@ -177,12 +176,6 @@ type Options struct { // attempted. It is used to allow the client to clean up any resources or complete any // tasks that are dependent on a live client. Shutdown func() - - // PersistState, if non-nil, is called with an updated Persist after - // generating a new node key but before sending the RegisterRequest. - // This allows the caller to write the key to durable storage so that - // a crash during registration doesn't lose the key. - PersistState func(persist.PersistView) } // ControlDialPlanner is the interface optionally supplied when creating a @@ -338,7 +331,6 @@ func NewDirect(opts Options) (*Direct, error) { dialer: opts.Dialer, dnsCache: dnsCache, dialPlan: opts.DialPlan, - persistState: opts.PersistState, } c.discoPubKey = opts.DiscoPublicKey c.closedCtx, c.closeCtx = context.WithCancel(context.Background()) @@ -665,14 +657,6 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new persist.NetworkLockKey = key.NewNLPrivate() } - // Persist the new key before the RegisterRequest so a crash - // mid-HTTP doesn't lose it. - if c.persistState != nil && !tryingNewKey.IsZero() && (persist.PrivateNodeKey.IsZero() || tryingNewKey.Public() != persist.PrivateNodeKey.Public()) { - preHTTPPersist := persist - preHTTPPersist.PrivateNodeKey = tryingNewKey - c.persistState(preHTTPPersist.View()) - } - nlPub := persist.NetworkLockKey.Public() if tryingNewKey.IsZero() { |
