Logout: revoke the node even when the server is already stopped#53
Closed
danReynolds wants to merge 1 commit into
Closed
Logout: revoke the node even when the server is already stopped#53danReynolds wants to merge 1 commit into
danReynolds wants to merge 1 commit into
Conversation
Logout only revoked the node key at the control plane when a server was running — revokeNodeKey needs a live LocalClient. A caller that stops/downs the server before logging out (e.g. disconnect-then-logout) therefore got a local-only wipe: the node stayed registered in the tailnet until key expiry, and any surviving copy of the wiped state remained a valid credential. Cache the last Start's hostname + control URL; when Logout finds no live server but persisted state exists, briefly bring the node back up from that state (reusing its node key, no auth key) to issue the control-plane logout, then wipe. Best-effort and timeout-bounded — the local wipe always proceeds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Owner
Author
|
Closing this after re-checking the original concern. The machine-key/node-ID rotation case is already covered on main: Logout(stateDir) always removes the persisted state directory, even when the embedded server was already stopped, so the next up() cannot reuse the old local machine key and should register as a fresh node. This PR only adds a best-effort online remote-revocation path for the narrower down()/Stop() then logout() case. It still cannot revoke when the control plane/network is unavailable, and that stopped-node revocation behavior is not proven by the current tests. Given the motivating question was offline/local rotation, this is extra complexity rather than a necessary fix. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Logout(stateDir)only revokes the node key at the control plane when a server is currently running:revokeNodeKeyissues the control-plane logout viasrv.LocalClient().Logout(ctx), which requires a live backend. So a caller that stops/down()s the server before callingLogout(e.g. adisconnect()-then-logout()sequence) gets a local-only wipe: the node is never told to log out, and stays registered in the tailnet until node-key expiry. Worse, any surviving copy of the (now-wiped) state remains a valid credential for that window.A
Logoutthat silently leaves the node registered just because the engine was already stopped is the wrong contract — logging out should clear the node regardless of connection state.Fix
Start'shostname+controlURLin package state.Logoutfinds no live server but persisted state exists, briefly bring the node back up from that state (revokeStoppedNode) — reusing the persisted node key, no auth key — so the LocalAPI logout can reach the control plane, then tear the transient server down and wipe.Net: a logout after a
down()/Stop()now still revokes the node at the control plane, instead of only clearing local credentials.Verification
go test ./...passes, including a newTestLogout_NoCachedControlURLWipesWithoutHangguard (persisted state + no cached control URL → wipes without attempting a hanging transient revoke). ExistingTestLogout_RemovesDir/HasStatetests unchanged.go build -buildmode=c-shared ./cmd/dylibbuilds cleanly;DuneLogoutexport intact.test/e2e/run_e2e.sh) before merge — happy to add adown() → logout() → node revokedcase there.🤖 Generated with Claude Code