From adcd23dbc35a4df42a389df5f46d20efc6f48c14 Mon Sep 17 00:00:00 2001 From: Raj Singh Date: Thu, 18 Jun 2026 09:38:43 -0500 Subject: [PATCH] cmd/containerboot: reload tailscaled config on any Secret mount event watchTailscaledConfigChanges only handled fsnotify events whose name matched /..data, dropping all other events from the kubelet atomic-writer rotation. Across kernels/fsnotify versions the ..data symlink swap can arrive as CREATE/REMOVE on .. or the leaf symlinks, or be coalesced entirely, so the filter silently discarded the rotation and lc.ReloadConfig was never called. Running tailscaled kept stale prefs (notably AdvertiseServices) until the pod restarted, which is the only path AdvertiseServices reaches a ProxyGroup pod since TS_EXPERIMENTAL_SERVICE_AUTO_ADVERTISEMENT=false disables the runtime push. Mirror the serve-config watcher (serve.go) and re-read the file on any event, letting the existing byte comparison decide whether to reload. Fixes #19782 Signed-off-by: Raj Singh --- cmd/containerboot/tailscaled.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/containerboot/tailscaled.go b/cmd/containerboot/tailscaled.go index 6f4ed77e7..3bdf71a1e 100644 --- a/cmd/containerboot/tailscaled.go +++ b/cmd/containerboot/tailscaled.go @@ -215,12 +215,15 @@ func watchTailscaledConfigChanges(ctx context.Context, path string, lc *local.Cl return } prevTailscaledCfg = b - // kubelet mounts Secrets to Pods using a series of symlinks, one of - // which is /..data that Kubernetes recommends consumers to - // use if they need to monitor changes - // https://github.com/kubernetes/kubernetes/blob/v1.28.1/pkg/volume/util/atomic_writer.go#L39-L61 - const kubeletMountedCfg = "..data" - toWatch := filepath.Join(tailscaledCfgDir, kubeletMountedCfg) + // kubelet mounts Secrets to Pods using a series of symlinks. The atomic + // writer rotates the mount by creating a new .. directory, + // swapping the ..data symlink into it, and removing the old directory. + // Across kernels/fsnotify versions the resulting fsnotify events are not + // reliable to filter on (they may arrive as CREATE/REMOVE on .., + // on the leaf symlinks, or be coalesced/missed for the ..data swap itself). + // So, like the serve-config watcher, we re-read the file on any event and + // let the byte comparison below decide whether anything actually changed. + // See https://github.com/kubernetes/kubernetes/blob/v1.28.1/pkg/volume/util/atomic_writer.go#L39-L61 for { select { case <-ctx.Done(): @@ -229,10 +232,7 @@ func watchTailscaledConfigChanges(ctx context.Context, path string, lc *local.Cl errCh <- fmt.Errorf("watcher error: %w", err) return case <-tickChan: - case event := <-eventChan: - if event.Name != toWatch { - continue - } + case <-eventChan: } b, err := os.ReadFile(path) if err != nil {