ipnlocal: force-regen new authURL when it is too old (#10971)

Fixes tailscale/support-escalations#23.

authURLs returned by control expire after 1 hour from creation. Customer reported that the Tailscale client on macOS would sending users to a stale authentication page when clicking on the `Login...` menu item. This can happen when clicking on Login after leaving the device unattended for several days. The device key expires, leading to the creation of a new authURL, however the client doesn't keep track of when the authURL was created. Meaning that `login-interactive` would send the user to an authURL that had expired server-side a long time before.

This PR ensures that whenever `login-interactive` is called via LocalAPI, an authURL that is too old won't be used. We force control to give us a new authURL whenever it's been more than 30 minutes since the last authURL was sent down from control.



Apply suggestions from code review




Set interval to 6 days and 23 hours

Signed-off-by: Andrea Gottardo <andrea@tailscale.com>
Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
pull/11077/head
Andrea Gottardo 4 months ago committed by GitHub
parent 1217f655c0
commit 6c79f55d48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -241,8 +241,9 @@ type LocalBackend struct {
endpoints []tailcfg.Endpoint
blocked bool
keyExpired bool
authURL string // cleared on Notify
authURLSticky string // not cleared on Notify
authURL string // cleared on Notify
authURLSticky string // not cleared on Notify
authURLTime time.Time // when the authURL was received from the control server
interact bool
egg bool
prevIfState *interfaces.State
@ -1096,6 +1097,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
if st.URL != "" {
b.authURL = st.URL
b.authURLSticky = st.URL
b.authURLTime = b.clock.Now()
}
if (wasBlocked || b.seamlessRenewalEnabled()) && st.LoginFinished() {
// Interactive login finished successfully (URL visited).
@ -2784,11 +2786,15 @@ func (b *LocalBackend) StartLoginInteractive() {
b.assertClientLocked()
b.interact = true
url := b.authURL
timeSinceAuthURLCreated := b.clock.Since(b.authURLTime)
cc := b.cc
b.mu.Unlock()
b.logf("StartLoginInteractive: url=%v", url != "")
if url != "" {
// Only use an authURL if it was sent down from control in the last
// 6 days and 23 hours. Avoids using a stale URL that is no longer valid
// server-side. Server-side URLs expire after 7 days.
if url != "" && timeSinceAuthURLCreated < ((7*24*time.Hour)-(1*time.Hour)) {
b.popBrowserAuthNow()
} else {
cc.Login(nil, b.loginFlags|controlclient.LoginInteractive)
@ -4166,6 +4172,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State) {
if newState == ipn.Running {
b.authURL = ""
b.authURLSticky = ""
b.authURLTime = time.Time{}
} else if oldState == ipn.Running {
// Transitioning away from running.
b.closePeerAPIListenersLocked()
@ -4408,6 +4415,7 @@ func (b *LocalBackend) ResetForClientDisconnect() {
b.keyExpired = false
b.authURL = ""
b.authURLSticky = ""
b.authURLTime = time.Time{}
b.activeLogin = ""
b.setAtomicValuesFromPrefsLocked(ipn.PrefsView{})
b.enterStateLockedOnEntry(ipn.Stopped)

Loading…
Cancel
Save