From afb3020ef0b95c9f3b3d017284c2652fadf8e8cf Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 4 Sep 2026 10:31:21 +0000 Subject: [PATCH] noise: make deleted-node expiry clock independent Updates #3410 --- hscontrol/noise.go | 11 +++++++---- hscontrol/noise_test.go | 5 +++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hscontrol/noise.go b/hscontrol/noise.go index 8064caa06..7ce89e8c4 100644 --- a/hscontrol/noise.go +++ b/hscontrol/noise.go @@ -718,8 +718,11 @@ func (ns *noiseServer) PollNetMapHandler( if errors.Is(err, errNodeNotInStore) && mapRequest.Stream { expired := &tailcfg.MapResponse{ Node: &tailcfg.Node{ - Key: mapRequest.NodeKey, - KeyExpiry: time.Now().Add(-time.Hour).UTC(), + Key: mapRequest.NodeKey, + // Zero means that the key does not expire. Use a fixed, + // ancient non-zero value so this remains expired even when + // the client's clock is substantially behind the server. + KeyExpiry: time.Unix(1, 0).UTC(), Expired: true, }, } @@ -813,8 +816,8 @@ func (ns *noiseServer) RegistrationHandler( // means the node is gone and its client should re-authenticate. var errNodeNotInStore = errors.New("node not found") -// getAndValidateNode retrieves the node from the database using the NodeKey -// and validates that it matches the MachineKey from the Noise session. +// getAndValidateNode retrieves the node from the in-memory NodeStore using the +// NodeKey and validates that it matches the MachineKey from the Noise session. func (ns *noiseServer) getAndValidateNode(mapRequest tailcfg.MapRequest) (types.NodeView, error) { nv, ok := ns.headscale.state.GetNodeByNodeKey(mapRequest.NodeKey) if !ok { diff --git a/hscontrol/noise_test.go b/hscontrol/noise_test.go index a6f7b50e9..942b1bd74 100644 --- a/hscontrol/noise_test.go +++ b/hscontrol/noise_test.go @@ -618,8 +618,9 @@ func TestPollNetMapHandler_DeletedNodeGetsExpiredSelf(t *testing.T) { resp := decodeMapResponse(t, compress, rec.Body.Bytes()) require.NotNil(t, resp.Node, "clients reject an initial map response without a node") assert.Equal(t, node.NodeKey, resp.Node.Key) - assert.True(t, resp.Node.KeyExpiry.Before(time.Now()), - "a past KeyExpiry is what drives the client to NeedsLogin, got %v", resp.Node.KeyExpiry) + assert.Equal(t, time.Unix(1, 0).UTC(), resp.Node.KeyExpiry, + "a fixed ancient KeyExpiry must remain expired despite client clock skew") + assert.True(t, resp.Node.Expired) }) } }