tsnet: reset serve config only once

Prior to this change, we were resetting the tsnet's serve config every
time tsnet.Server.Up was run. This is important to do on startup, to
prevent messy interactions with stale configuration when the code has
changed.

However, Up is frequently run as a just-in-case step (for example, by
Server.ListenTLS/ListenFunnel and possibly by consumers of tsnet). When
the serve config is reset on each of these calls to Up, this creates
situations in which the serve config disappears unexpectedly. The
solution is to reset the serve config only on the first call to Up.

Fixes #8800
Updates tailscale/corp#27200
Signed-off-by: Harry Harpham <harry@tailscale.com>
This commit is contained in:
Harry Harpham
2026-01-08 20:49:18 -07:00
parent 5f34f14e14
commit f9762064cf
+12 -7
View File
@@ -176,6 +176,7 @@ type Server struct {
localAPIListener net.Listener // in-memory, used by localClient localAPIListener net.Listener // in-memory, used by localClient
localClient *local.Client // in-memory localClient *local.Client // in-memory
localAPIServer *http.Server localAPIServer *http.Server
resetServeConfigOnce sync.Once
logbuffer *filch.Filch logbuffer *filch.Filch
logtail *logtail.Logger logtail *logtail.Logger
logid logid.PublicID logid logid.PublicID
@@ -388,8 +389,8 @@ func (s *Server) Up(ctx context.Context) (*ipnstate.Status, error) {
if n.ErrMessage != nil { if n.ErrMessage != nil {
return nil, fmt.Errorf("tsnet.Up: backend: %s", *n.ErrMessage) return nil, fmt.Errorf("tsnet.Up: backend: %s", *n.ErrMessage)
} }
if s := n.State; s != nil { if st := n.State; st != nil {
if *s == ipn.Running { if *st == ipn.Running {
status, err := lc.Status(ctx) status, err := lc.Status(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("tsnet.Up: %w", err) return nil, fmt.Errorf("tsnet.Up: %w", err)
@@ -398,11 +399,15 @@ func (s *Server) Up(ctx context.Context) (*ipnstate.Status, error) {
return nil, errors.New("tsnet.Up: running, but no ip") return nil, errors.New("tsnet.Up: running, but no ip")
} }
// Clear the persisted serve config state to prevent stale configuration // The first time Up is run, clear the persisted serve config.
// from code changes. This is a temporary workaround until we have a better // We do this to prevent messy interactions with stale config in
// way to handle this. (2023-03-11) // the face of code changes.
if err := lc.SetServeConfig(ctx, new(ipn.ServeConfig)); err != nil { var srvResetErr error
return nil, fmt.Errorf("tsnet.Up: %w", err) s.resetServeConfigOnce.Do(func() {
srvResetErr = lc.SetServeConfig(ctx, new(ipn.ServeConfig))
})
if srvResetErr != nil {
return nil, fmt.Errorf("tsnet.Up: clearing serve config: %w", err)
} }
return status, nil return status, nil