mirror of
https://github.com/juanfont/headscale.git
synced 2025-12-07 16:22:25 -05:00
auth: ensure machines are allowed in when pak change (#2917)
This commit is contained in:
@@ -456,3 +456,87 @@ func TestAuthKeyLogoutAndReloginSameUserExpiredKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthKeyDeleteKey tests Issue #2830: node with deleted auth key should still reconnect.
|
||||
// Scenario from user report: "create node, delete the auth key, restart to validate it can connect"
|
||||
// Steps:
|
||||
// 1. Create node with auth key
|
||||
// 2. DELETE the auth key from database (completely remove it)
|
||||
// 3. Restart node - should successfully reconnect using MachineKey identity
|
||||
func TestAuthKeyDeleteKey(t *testing.T) {
|
||||
IntegrationSkip(t)
|
||||
|
||||
// Create scenario with NO nodes - we'll create the node manually so we can capture the auth key
|
||||
scenario, err := NewScenario(ScenarioSpec{
|
||||
NodesPerUser: 0, // No nodes created automatically
|
||||
Users: []string{"user1"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("delkey"), hsic.WithTLS(), hsic.WithDERPAsIP())
|
||||
requireNoErrHeadscaleEnv(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
requireNoErrGetHeadscale(t, err)
|
||||
|
||||
// Get the user
|
||||
userMap, err := headscale.MapUsers()
|
||||
require.NoError(t, err)
|
||||
userID := userMap["user1"].GetId()
|
||||
|
||||
// Create a pre-auth key - we keep the full key string before it gets redacted
|
||||
authKey, err := scenario.CreatePreAuthKey(userID, false, false)
|
||||
require.NoError(t, err)
|
||||
authKeyString := authKey.GetKey()
|
||||
authKeyID := authKey.GetId()
|
||||
t.Logf("Created pre-auth key ID %d: %s", authKeyID, authKeyString)
|
||||
|
||||
// Create a tailscale client and log it in with the auth key
|
||||
client, err := scenario.CreateTailscaleNode(
|
||||
"head",
|
||||
tsic.WithNetwork(scenario.networks[scenario.testDefaultNetwork]),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Login(headscale.GetEndpoint(), authKeyString)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for the node to be registered
|
||||
var user1Nodes []*v1.Node
|
||||
assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
var err error
|
||||
user1Nodes, err = headscale.ListNodes("user1")
|
||||
assert.NoError(c, err)
|
||||
assert.Len(c, user1Nodes, 1)
|
||||
}, 30*time.Second, 500*time.Millisecond, "waiting for node to be registered")
|
||||
|
||||
nodeID := user1Nodes[0].GetId()
|
||||
nodeName := user1Nodes[0].GetName()
|
||||
t.Logf("Node %d (%s) created successfully with auth_key_id=%d", nodeID, nodeName, authKeyID)
|
||||
|
||||
// Verify node is online
|
||||
requireAllClientsOnline(t, headscale, []types.NodeID{types.NodeID(nodeID)}, true, "node should be online initially", 120*time.Second)
|
||||
|
||||
// DELETE the pre-auth key using the API
|
||||
t.Logf("Deleting pre-auth key ID %d using API", authKeyID)
|
||||
err = headscale.DeleteAuthKey(userID, authKeyString)
|
||||
require.NoError(t, err)
|
||||
t.Logf("Successfully deleted auth key")
|
||||
|
||||
// Simulate node restart (down + up)
|
||||
t.Logf("Restarting node after deleting its auth key")
|
||||
err = client.Down()
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
err = client.Up()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify node comes back online
|
||||
// This will FAIL without the fix because auth key validation will reject deleted key
|
||||
// With the fix, MachineKey identity allows reconnection even with deleted key
|
||||
requireAllClientsOnline(t, headscale, []types.NodeID{types.NodeID(nodeID)}, true, "node should reconnect after restart despite deleted key", 120*time.Second)
|
||||
|
||||
t.Logf("✓ Node successfully reconnected after its auth key was deleted")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user