mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
90fd04cbde
I realized that a lot of the problems that we're seeing around migration and LocalBackend state can be avoided if we drive Windows pref migration entirely from within tailscaled. By doing it this way, tailscaled can automatically perform the migration as soon as the connection with the client frontend is established. Since tailscaled is already running as LocalSystem, it already has access to the user's local AppData directory. The profile manager already knows which user is connected, so we simply need to resolve the user's prefs file and read it from there. Of course, to properly migrate this information we need to also check system policies. I moved a bunch of policy resolution code out of the GUI and into a new package in util/winutil/policy. Updates #7626 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
85 lines
2.5 KiB
Go
85 lines
2.5 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ipnlocal
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
|
|
"tailscale.com/atomicfile"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/util/winutil/policy"
|
|
)
|
|
|
|
const (
|
|
legacyPrefsFile = "prefs"
|
|
legacyPrefsMigrationSentinelFile = "_migrated-to-profiles"
|
|
legacyPrefsExt = ".conf"
|
|
)
|
|
|
|
var errAlreadyMigrated = errors.New("profile migration already completed")
|
|
|
|
func legacyPrefsDir(uid ipn.WindowsUserID) (string, error) {
|
|
// TODO(aaron): Ideally we'd have the impersonation token for the pipe's
|
|
// client and use it to call SHGetKnownFolderPath, thus yielding the correct
|
|
// path without having to make gross assumptions about directory names.
|
|
usr, err := user.LookupId(string(uid))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if usr.HomeDir == "" {
|
|
return "", fmt.Errorf("user %q does not have a home directory", uid)
|
|
}
|
|
userLegacyPrefsDir := filepath.Join(usr.HomeDir, "AppData", "Local", "Tailscale")
|
|
return userLegacyPrefsDir, nil
|
|
}
|
|
|
|
func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
|
|
userLegacyPrefsDir, err := legacyPrefsDir(pm.currentUserID)
|
|
if err != nil {
|
|
return "", ipn.PrefsView{}, err
|
|
}
|
|
|
|
migrationSentinel := filepath.Join(userLegacyPrefsDir, legacyPrefsMigrationSentinelFile+legacyPrefsExt)
|
|
// verify that migration sentinel is not present
|
|
_, err = os.Stat(migrationSentinel)
|
|
if err == nil {
|
|
return "", ipn.PrefsView{}, errAlreadyMigrated
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
return "", ipn.PrefsView{}, err
|
|
}
|
|
|
|
prefsPath := filepath.Join(userLegacyPrefsDir, legacyPrefsFile+legacyPrefsExt)
|
|
prefs, err := ipn.LoadPrefs(prefsPath)
|
|
if err != nil {
|
|
return "", ipn.PrefsView{}, err
|
|
}
|
|
|
|
prefs.ControlURL = policy.SelectControlURL(defaultPrefs.ControlURL(), prefs.ControlURL)
|
|
prefs.ExitNodeIP = resolveExitNodeIP(prefs.ExitNodeIP)
|
|
prefs.ShieldsUp = resolveShieldsUp(prefs.ShieldsUp)
|
|
prefs.ForceDaemon = resolveForceDaemon(prefs.ForceDaemon)
|
|
|
|
pm.logf("migrating Windows profile to new format")
|
|
return migrationSentinel, prefs.View(), nil
|
|
}
|
|
|
|
func (pm *profileManager) completeMigration(migrationSentinel string) {
|
|
atomicfile.WriteFile(migrationSentinel, []byte{}, 0600)
|
|
}
|
|
|
|
func resolveShieldsUp(defval bool) bool {
|
|
pol := policy.GetPreferenceOptionPolicy("AllowIncomingConnections")
|
|
return !pol.ShouldEnable(!defval)
|
|
}
|
|
|
|
func resolveForceDaemon(defval bool) bool {
|
|
pol := policy.GetPreferenceOptionPolicy("UnattendedMode")
|
|
return pol.ShouldEnable(defval)
|
|
}
|