ipn/ipnlocal: close foreground sessions on SetServeConfig

This PR ensures zombie foregrounds are shutdown if a new
ServeConfig is created that wipes the ongoing foreground ones.
For example, "tailscale serve|funnel reset|off" should close
all open sessions.

Updates #8489

Signed-off-by: Marwan Sulaiman <marwan@tailscale.com>
This commit is contained in:
Marwan Sulaiman
2023-09-18 10:30:58 -04:00
committed by Marwan Sulaiman
parent 530aaa52f1
commit 651620623b
5 changed files with 140 additions and 11 deletions

View File

@@ -247,16 +247,17 @@ func (b *LocalBackend) setServeConfigLocked(config *ipn.ServeConfig, etag string
// If etag is present, check that it has
// not changed from the last config.
prevConfig := b.serveConfig
if etag != "" {
// Note that we marshal b.serveConfig
// and not use b.lastServeConfJSON as that might
// be a Go nil value, which produces a different
// checksum from a JSON "null" value.
previousCfg, err := json.Marshal(b.serveConfig)
prevBytes, err := json.Marshal(prevConfig)
if err != nil {
return fmt.Errorf("error encoding previous config: %w", err)
}
sum := sha256.Sum256(previousCfg)
sum := sha256.Sum256(prevBytes)
previousEtag := hex.EncodeToString(sum[:])
if etag != previousEtag {
return ErrETagMismatch
@@ -279,6 +280,26 @@ func (b *LocalBackend) setServeConfigLocked(config *ipn.ServeConfig, etag string
}
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked(b.pm.CurrentPrefs())
// clean up and close all previously open foreground sessions
// if the current ServeConfig has overwritten them.
if prevConfig.Valid() {
has := func(string) bool { return false }
if b.serveConfig.Valid() {
has = b.serveConfig.Foreground().Has
}
prevConfig.Foreground().Range(func(k string, v ipn.ServeConfigView) (cont bool) {
if !has(k) {
for _, sess := range b.notifyWatchers {
if sess.sessionID == k {
close(sess.ch)
}
}
}
return true
})
}
return nil
}