From d937a3afab551b3c0c36871921b87692385f6fed Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Wed, 23 Jul 2025 04:27:40 -0700 Subject: [PATCH] CTMU no longer uses a static path, any re-execution from the tools directory must disable further re-execution --- lib/autoupdate/tools/helper.go | 2 +- lib/autoupdate/tools/updater.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/autoupdate/tools/helper.go b/lib/autoupdate/tools/helper.go index d93a83cec76a9..f2e45b286d052 100644 --- a/lib/autoupdate/tools/helper.go +++ b/lib/autoupdate/tools/helper.go @@ -187,7 +187,7 @@ func updateAndReExec(ctx context.Context, updater *Updater, toolsVersion string, } // Re-execute client tools with the correct version of client tools. - code, err := updater.Exec(toolsVersion, args) + code, err := updater.Exec(ctx, toolsVersion, args) if err != nil && !errors.Is(err, os.ErrNotExist) { slog.DebugContext(ctx, "Failed to re-exec client tool", "error", err, "code", code) os.Exit(code) diff --git a/lib/autoupdate/tools/updater.go b/lib/autoupdate/tools/updater.go index 08615e69740b2..4f439ba03bcb4 100644 --- a/lib/autoupdate/tools/updater.go +++ b/lib/autoupdate/tools/updater.go @@ -34,6 +34,7 @@ import ( "path/filepath" "regexp" "runtime" + "strings" "syscall" "time" @@ -207,11 +208,7 @@ func (u *Updater) CheckLocal(ctx context.Context, profileName string) (resp *Upd // operate with this cluster. It returns the semantic version that needs updating and whether // re-execution is necessary, by re-execution flag we understand that update and re-execute is required. func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bool) (response *UpdateResponse, err error) { - proxyHost, err := utils.Host(proxyAddr) - if err != nil { - return nil, trace.Wrap(err) - } - + proxyHost := utils.TryHost(proxyAddr) // Check if the user has requested a specific version of client tools. requestedVersion := os.Getenv(teleportToolsVersionEnv) switch requestedVersion { @@ -402,7 +399,7 @@ func (u *Updater) ToolPath(toolName, toolVersion string) (path string, err error } // Exec re-executes tool command with same arguments and environ variables. -func (u *Updater) Exec(toolsVersion string, args []string) (int, error) { +func (u *Updater) Exec(ctx context.Context, toolsVersion string, args []string) (int, error) { executablePath, err := os.Executable() if err != nil { return 0, trace.Wrap(err) @@ -423,14 +420,19 @@ func (u *Updater) Exec(toolsVersion string, args []string) (int, error) { env := append(os.Environ(), fmt.Sprintf("%s=%s", teleportToolsDirsEnv, u.toolsDir)) // To prevent re-execution loop we have to disable update logic for re-execution, // by unsetting current tools version env variable and setting it to "off". - if path == executablePath { + // The re-execution path and tools directory are absolute. Since the v2 logic + // no longer uses a static path, any re-execution from the tools directory + // must disable further re-execution. + if path == executablePath || strings.HasPrefix(path, u.toolsDir) { if err := os.Unsetenv(teleportToolsVersionEnv); err != nil { return 0, trace.Wrap(err) } env = append(env, teleportToolsVersionEnv+"=off") + slog.DebugContext(ctx, "Disable next re-execution") } env = append(env, fmt.Sprintf("%s=%s", teleportToolsVersionReExecEnv, u.localVersion)) + slog.DebugContext(ctx, "Re-execute updated version", "execute", path, "from", executablePath) if runtime.GOOS == constants.WindowsOS { cmd := exec.Command(path, args...) cmd.Env = env