mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +00:00
control/controlclient, localapi: shorten expiry time via localapi (#4112)
Signed-off-by: Nick O'Neill <nick@tailscale.com>
This commit is contained in:
parent
2bcc047d4f
commit
1625e87526
@ -657,6 +657,10 @@ func (c *Auto) Logout(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Auto) SetExpirySooner(ctx context.Context, expiry time.Time) error {
|
||||||
|
return c.direct.SetExpirySooner(ctx, expiry)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateEndpoints sets the client's discovered endpoints and sends
|
// UpdateEndpoints sets the client's discovered endpoints and sends
|
||||||
// them to the control server if they've changed.
|
// them to the control server if they've changed.
|
||||||
//
|
//
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
)
|
)
|
||||||
@ -45,6 +46,9 @@ type Client interface {
|
|||||||
// Logout starts a synchronous logout process. It doesn't return
|
// Logout starts a synchronous logout process. It doesn't return
|
||||||
// until the logout operation has been completed.
|
// until the logout operation has been completed.
|
||||||
Logout(context.Context) error
|
Logout(context.Context) error
|
||||||
|
// SetExpirySooner sets the node's expiry time via the controlclient,
|
||||||
|
// as long as it's shorter than the current expiry time.
|
||||||
|
SetExpirySooner(context.Context, time.Time) error
|
||||||
// SetPaused pauses or unpauses the controlclient activity as much
|
// SetPaused pauses or unpauses the controlclient activity as much
|
||||||
// as possible, without losing its internal state, to minimize
|
// as possible, without losing its internal state, to minimize
|
||||||
// unnecessary network activity.
|
// unnecessary network activity.
|
||||||
|
@ -302,12 +302,27 @@ func (c *Direct) doLoginOrRegen(ctx context.Context, opt loginOpt) (newURL strin
|
|||||||
return url, err
|
return url, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetExpirySooner attempts to shorten the expiry to the specified time.
|
||||||
|
func (c *Direct) SetExpirySooner(ctx context.Context, expiry time.Time) error {
|
||||||
|
c.logf("[v1] direct.SetExpirySooner()")
|
||||||
|
|
||||||
|
newURL, err := c.doLoginOrRegen(ctx, loginOpt{Expiry: &expiry})
|
||||||
|
c.logf("[v1] SetExpirySooner control response: newURL=%v, err=%v", newURL, err)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
type loginOpt struct {
|
type loginOpt struct {
|
||||||
Token *tailcfg.Oauth2Token
|
Token *tailcfg.Oauth2Token
|
||||||
Flags LoginFlags
|
Flags LoginFlags
|
||||||
Regen bool
|
Regen bool // generate a new nodekey, can be overridden in doLogin
|
||||||
URL string
|
URL string
|
||||||
Logout bool
|
Logout bool // set the expiry to the far past, expiring the node
|
||||||
|
// Expiry, if non-nil, attempts to set the node expiry to the
|
||||||
|
// specified time and cannot be used to extend the expiry.
|
||||||
|
// It is ignored if Logout is set since Logout works by setting a
|
||||||
|
// expiry time in the far past.
|
||||||
|
Expiry *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpClient provides a common interface for the noiseClient and
|
// httpClient provides a common interface for the noiseClient and
|
||||||
@ -406,6 +421,8 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
}
|
}
|
||||||
if opt.Logout {
|
if opt.Logout {
|
||||||
request.Expiry = time.Unix(123, 0) // far in the past
|
request.Expiry = time.Unix(123, 0) // far in the past
|
||||||
|
} else if opt.Expiry != nil {
|
||||||
|
request.Expiry = *opt.Expiry
|
||||||
}
|
}
|
||||||
c.logf("RegisterReq: onode=%v node=%v fup=%v",
|
c.logf("RegisterReq: onode=%v node=%v fup=%v",
|
||||||
request.OldNodeKey.ShortString(),
|
request.OldNodeKey.ShortString(),
|
||||||
|
@ -3187,6 +3187,15 @@ func (b *LocalBackend) allowExitNodeDNSProxyToServeName(name string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetExpiry updates the expiry of the current node key to t, as long as it's
|
||||||
|
// only sooner than the old expiry.
|
||||||
|
//
|
||||||
|
// If t is in the past, the key is expired immediately.
|
||||||
|
// If t is after the current expiry, an error is returned.
|
||||||
|
func (b *LocalBackend) SetExpirySooner(ctx context.Context, expiry time.Time) error {
|
||||||
|
return b.cc.SetExpirySooner(ctx, expiry)
|
||||||
|
}
|
||||||
|
|
||||||
// exitNodeCanProxyDNS reports the DoH base URL ("http://foo/dns-query") without query parameters
|
// exitNodeCanProxyDNS reports the DoH base URL ("http://foo/dns-query") without query parameters
|
||||||
// to exitNodeID's DoH service, if available.
|
// to exitNodeID's DoH service, if available.
|
||||||
//
|
//
|
||||||
|
@ -223,6 +223,12 @@ func (cc *mockControl) Logout(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *mockControl) SetExpirySooner(context.Context, time.Time) error {
|
||||||
|
cc.logf("SetExpirySooner")
|
||||||
|
cc.called("SetExpirySooner")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (cc *mockControl) SetPaused(paused bool) {
|
func (cc *mockControl) SetPaused(paused bool) {
|
||||||
cc.logf("SetPaused=%v", paused)
|
cc.logf("SetPaused=%v", paused)
|
||||||
if paused {
|
if paused {
|
||||||
|
@ -122,6 +122,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.serveMetrics(w, r)
|
h.serveMetrics(w, r)
|
||||||
case "/localapi/v0/debug":
|
case "/localapi/v0/debug":
|
||||||
h.serveDebug(w, r)
|
h.serveDebug(w, r)
|
||||||
|
case "/localapi/v0/set-expiry-sooner":
|
||||||
|
h.serveSetExpirySooner(w, r)
|
||||||
case "/":
|
case "/":
|
||||||
io.WriteString(w, "tailscaled\n")
|
io.WriteString(w, "tailscaled\n")
|
||||||
default:
|
default:
|
||||||
@ -511,6 +513,35 @@ func (h *Handler) serveDERPMap(w http.ResponseWriter, r *http.Request) {
|
|||||||
e.Encode(h.b.DERPMap())
|
e.Encode(h.b.DERPMap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serveSetExpirySooner sets the expiry date on the current machine, specified
|
||||||
|
// by an `expiry` unix timestamp as POST or query param.
|
||||||
|
func (h *Handler) serveSetExpirySooner(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "POST required", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var expiryTime time.Time
|
||||||
|
if v := r.FormValue("expiry"); v != "" {
|
||||||
|
expiryInt, err := strconv.ParseInt(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "can't parse expiry time, expects a unix timestamp", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expiryTime = time.Unix(expiryInt, 0)
|
||||||
|
} else {
|
||||||
|
http.Error(w, "missing 'expiry' parameter, a unix timestamp", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := h.b.SetExpirySooner(r.Context(), expiryTime)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
io.WriteString(w, "done\n")
|
||||||
|
}
|
||||||
|
|
||||||
func defBool(a string, def bool) bool {
|
func defBool(a string, def bool) bool {
|
||||||
if a == "" {
|
if a == "" {
|
||||||
return def
|
return def
|
||||||
|
Loading…
x
Reference in New Issue
Block a user