diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index 4e51bbbe5..847f137c5 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -298,7 +298,7 @@ func run() error { Port: 41112, StatePath: args.statepath, AutostartStateKey: globalStateKey, - SurviveDisconnects: true, + SurviveDisconnects: runtime.GOOS != "windows", DebugMux: debugMux, } err = ipnserver.Run(ctx, logf, pol.PublicID.String(), ipnserver.FixedEngine(e), opts) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 645674cad..c1c508dc8 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -2082,6 +2082,30 @@ func (b *LocalBackend) requestEngineStatusAndWait() { b.statusLock.Unlock() } +// ResetForClientDisconnect resets the backend for GUI clients running +// in interactive (non-headless) mode. This is currently used only by +// Windows. This causes all state to be cleared, lest an unrelated user +// connect to tailscaled next. But it does not trigger a logout; we +// don't want to the user to have to reauthenticate in the future +// when they restart the GUI. +func (b *LocalBackend) ResetForClientDisconnect() { + defer b.enterState(ipn.Stopped) + b.mu.Lock() + defer b.mu.Unlock() + b.logf("LocalBackend.ResetForClientDisconnect") + + if b.cc != nil { + go b.cc.Shutdown() + b.cc = nil + } + b.stateKey = "" + b.userID = "" + b.setNetMapLocked(nil) + b.prefs = new(ipn.Prefs) + b.authURL = "" + b.activeLogin = "" +} + // Logout tells the controlclient that we want to log out, and // transitions the local engine to the logged-out state without // waiting for controlclient to be in that state. @@ -2105,7 +2129,7 @@ func (b *LocalBackend) logout(ctx context.Context, sync bool) error { b.EditPrefs(&ipn.MaskedPrefs{ WantRunningSet: true, - Prefs: ipn.Prefs{WantRunning: true}, + Prefs: ipn.Prefs{WantRunning: false}, }) if cc == nil { diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index c7126b72e..900552c81 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -476,9 +476,7 @@ func (s *server) addConn(c net.Conn, isHTTP bool) (ci connIdentity, err error) { defer func() { if doReset { s.logf("identity changed; resetting server") - s.bsMu.Lock() - s.bs.Reset(context.TODO()) - s.bsMu.Unlock() + s.b.ResetForClientDisconnect() } }() @@ -528,9 +526,7 @@ func (s *server) removeAndCloseConn(c net.Conn) { s.logf("client disconnected; staying alive in server mode") } else { s.logf("client disconnected; stopping server") - s.bsMu.Lock() - s.bs.Reset(context.TODO()) - s.bsMu.Unlock() + s.b.ResetForClientDisconnect() } } c.Close() diff --git a/ipn/message.go b/ipn/message.go index fe3df7d67..8eeac8b57 100644 --- a/ipn/message.go +++ b/ipn/message.go @@ -143,11 +143,6 @@ func (bs *BackendServer) GotCommandMsg(ctx context.Context, b []byte) error { return bs.GotCommand(ctx, cmd) } -func (bs *BackendServer) GotFakeCommand(ctx context.Context, cmd *Command) error { - cmd.Version = version.Long - return bs.GotCommand(ctx, cmd) -} - // ErrMsgPermissionDenied is the Notify.ErrMessage value used an // operation was done from a user/context that didn't have permission. const ErrMsgPermissionDenied = "permission denied" @@ -211,12 +206,6 @@ func (bs *BackendServer) GotCommand(ctx context.Context, cmd *Command) error { return fmt.Errorf("BackendServer.Do: no command specified") } -func (bs *BackendServer) Reset(ctx context.Context) error { - // Tell the backend we got a Logout command, which will cause it - // to forget all its authentication information. - return bs.GotFakeCommand(ctx, &Command{Logout: &NoArgs{}}) -} - type BackendClient struct { logf logger.Logf sendCommandMsg func(jsonb []byte)