From 0121083b538d4ec4bd215c4ec750ed0d323fcd5d Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 7 Jun 2026 13:36:01 +0000 Subject: [PATCH] test: assert NodeKey hijack is rejected on re-auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TestReAuthWithDifferentMachineKey asserted that a second machine claiming an existing NodeKey succeeds, but that hijack (now rejected by f8f08cf7) poisoned the NodeKey index and DoS'd the original node — the test only checked the hijacker's node, never the original's survival. Assert the registration is rejected and the original node survives intact. --- hscontrol/auth_tags_test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/hscontrol/auth_tags_test.go b/hscontrol/auth_tags_test.go index 048b6311..3c55c667 100644 --- a/hscontrol/auth_tags_test.go +++ b/hscontrol/auth_tags_test.go @@ -1008,16 +1008,24 @@ func TestReAuthWithDifferentMachineKey(t *testing.T) { Expiry: time.Now().Add(24 * time.Hour), } - resp2, err := app.handleRegisterWithAuthKey(regReq2, machineKey2.Public()) - require.NoError(t, err) - require.True(t, resp2.MachineAuthorized) + // A NodeKey is bound 1:1 to a MachineKey (getAndValidateNode enforces + // this at poll time). A different machine claiming an existing NodeKey is + // a hijack: it would poison the NodeStore NodeKey index so the original + // node fails the poll-time MachineKey check and is denied service. + // Registration now rejects it (see f8f08cf7). Real Tailscale clients + // never reuse a NodeKey across machine keys, so no legitimate flow is + // affected. + _, err = app.handleRegisterWithAuthKey(regReq2, machineKey2.Public()) + require.Error(t, err, + "a different machine claiming an existing NodeKey must be rejected") - // Verify the node still exists and has tags - // Note: Depending on implementation, this might be the same node or a new node + // The original node is unaffected: still present, tagged, same identity. node2, found := app.state.GetNodeByNodeKey(nodeKey.Public()) require.True(t, found) assert.True(t, node2.IsTagged()) assert.ElementsMatch(t, tags, node2.Tags().AsSlice()) + assert.Equal(t, node1.ID(), node2.ID(), + "original node must survive; the hijacking registration was rejected") } // TestUntaggedAuthKeyZeroExpiryGetsDefault tests that when node.expiry is configured