From 28296e6b842fd658d8f1d67a3fd294bce0d3715d Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 21 Oct 2026 19:44:33 +0000 Subject: [PATCH] auth: answer a logout that deletes an ephemeral node as authorised --- hscontrol/auth.go | 3 ++- hscontrol/auth_test.go | 2 +- hscontrol/servertest/lifecycle_compat_test.go | 4 ---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hscontrol/auth.go b/hscontrol/auth.go index 0f2f8ca49..823360b87 100644 --- a/hscontrol/auth.go +++ b/hscontrol/auth.go @@ -246,9 +246,10 @@ func (h *Headscale) handleLogout( return nil, fmt.Errorf("deleting ephemeral node: %w", err) } + // The node was authorised to log out; Tailscale answers so too. return &tailcfg.RegisterResponse{ NodeKeyExpired: true, - MachineAuthorized: false, + MachineAuthorized: true, }, nil } diff --git a/hscontrol/auth_test.go b/hscontrol/auth_test.go index 4d2d5dc8e..e96c641cd 100644 --- a/hscontrol/auth_test.go +++ b/hscontrol/auth_test.go @@ -657,10 +657,10 @@ func TestAuthenticationFlows(t *testing.T) { } }, machineKey: machineKey1.Public, + wantAuth: true, // the logout is answered as authorised wantExpired: true, validate: func(t *testing.T, resp *tailcfg.RegisterResponse, app *Headscale) { //nolint:thelper //nolint:thelper assert.True(t, resp.NodeKeyExpired) - assert.False(t, resp.MachineAuthorized) // Ephemeral node should be deleted, not just marked expired _, found := app.state.GetNodeByNodeKey(nodeKey1.Public()) diff --git a/hscontrol/servertest/lifecycle_compat_test.go b/hscontrol/servertest/lifecycle_compat_test.go index b4c01dfa1..a54d26015 100644 --- a/hscontrol/servertest/lifecycle_compat_test.go +++ b/hscontrol/servertest/lifecycle_compat_test.go @@ -36,16 +36,12 @@ import ( // the step of what killed nodes leave behind. A listed check that matches // fails, so a fix must delete its entry. var lifecycleSkipReasons = map[string]string{ - "ephemeral-lifecycle/E1-logout/eph-logout/outcome": logoutAnswerUnauthorized, - "ephemeral-lifecycle/E1-logout/eph-logout-user/outcome": logoutAnswerUnauthorized, - "ephemeral-lifecycle/E3-key-delete/2/keys": deletedKeyListed, "ephemeral-lifecycle/E3-key-delete/3/keys": deletedKeyListed, "ephemeral-lifecycle/E3-key-delete/4/status": deletedKeyDeleteNotFound, } const ( - logoutAnswerUnauthorized = "LOGOUT_ANSWER_UNAUTHORIZED: headscale answers the logout that deletes an ephemeral node with MachineAuthorized false; SaaS answers true" deletedKeyListed = "DELETED_KEY_LISTED: headscale lists a deleted auth key; SaaS leaves it out of both key lists, while a read by ID still answers" deletedKeyDeleteNotFound = "DELETED_KEY_DELETE_NOT_FOUND: headscale answers 404 to a second delete of an auth key; SaaS answers 200" )