diff --git a/cmd/launcher/svc_windows.go b/cmd/launcher/svc_windows.go index 712439256..2ef32fd35 100644 --- a/cmd/launcher/svc_windows.go +++ b/cmd/launcher/svc_windows.go @@ -29,11 +29,6 @@ import ( // TODO This should be inherited from some setting const serviceName = "launcher" -var likelyRootDirPaths = []string{ - "C:\\ProgramData\\Kolide\\Launcher-kolide-k2\\data", - "C:\\Program Files\\Kolide\\Launcher-kolide-k2\\data", -} - // runWindowsSvc starts launcher as a windows service. This will // probably not behave correctly if you start it from the command line. func runWindowsSvc(systemSlogger *multislogger.MultiSlogger, args []string) error { @@ -55,16 +50,6 @@ func runWindowsSvc(systemSlogger *multislogger.MultiSlogger, args []string) erro logger := log.NewNopLogger() if opts.RootDirectory != "" { - rootDirectoryChanged := false - optsRootDirectory := opts.RootDirectory - // check for old root directories before creating DB in case we've stomped over with windows MSI install - updatedRootDirectory := determineRootDirectory(systemSlogger.Logger, opts) - if updatedRootDirectory != opts.RootDirectory { - opts.RootDirectory = updatedRootDirectory - // cache that we did this so we can log to debug.json when set up below - rootDirectoryChanged = true - } - // Create a local logger. This logs to a known path, and aims to help diagnostics ll := locallogger.NewKitLogger(filepath.Join(opts.RootDirectory, "debug.json")) logger = ll @@ -78,14 +63,6 @@ func runWindowsSvc(systemSlogger *multislogger.MultiSlogger, args []string) erro // also write system logs to localSloggerHandler systemSlogger.AddHandler(localSloggerHandler) - - if rootDirectoryChanged { - localSlogger.Log(context.TODO(), slog.LevelInfo, - "old root directory contents detected, overriding opts.RootDirectory", - "opts_root_directory", optsRootDirectory, - "updated_root_directory", updatedRootDirectory, - ) - } } // Confirm that service configuration is up-to-date @@ -255,75 +232,3 @@ func (w *winSvc) Execute(args []string, r <-chan svc.ChangeRequest, changes chan } } } - -// determineRootDirectory is used specifically for windows deployments to override the -// configured root directory if another one containing a launcher DB already exists -func determineRootDirectory(slogger *slog.Logger, opts *launcher.Options) string { - optsRootDirectory := opts.RootDirectory - // don't mess with the path if this installation isn't pointing to a kolide server URL - if opts.KolideServerURL != "k2device.kolide.com" && opts.KolideServerURL != "k2device-preprod.kolide.com" { - return optsRootDirectory - } - - optsDBLocation := filepath.Join(optsRootDirectory, "launcher.db") - dbExists, err := nonEmptyFileExists(optsDBLocation) - // If we get an unknown error, back out from making any options changes. This is an - // unlikely path but doesn't feel right updating the rootDirectory without knowing what's going - // on here - if err != nil { - slogger.Log(context.TODO(), slog.LevelError, - "encountered error checking for pre-existing launcher.db", - "location", optsDBLocation, - "err", err, - ) - - return optsRootDirectory - } - - // database already exists in configured root directory, keep that - if dbExists { - return optsRootDirectory - } - - // we know this is a fresh install with no launcher.db in the configured root directory, - // check likely locations and return updated rootDirectory if found - for _, path := range likelyRootDirPaths { - if path == optsRootDirectory { // we already know this does not contain an enrolled DB - continue - } - - testingLocation := filepath.Join(path, "launcher.db") - dbExists, err := nonEmptyFileExists(testingLocation) - if err == nil && dbExists { - return path - } - - if err != nil { - slogger.Log(context.TODO(), slog.LevelWarn, - "encountered error checking non-configured locations for launcher.db", - "opts_location", optsDBLocation, - "tested_location", testingLocation, - "err", err, - ) - - continue - } - } - - // if all else fails, return the originally configured rootDirectory - - // this is expected for devices that are truly installing from MSI for the first time - return optsRootDirectory -} - -func nonEmptyFileExists(path string) (bool, error) { - fileInfo, err := os.Stat(path) - if os.IsNotExist(err) { - return false, nil - } - - if err != nil { - return false, err - } - - return fileInfo.Size() > 0, nil -} diff --git a/ee/tuf/library_lookup.go b/ee/tuf/library_lookup.go index 098d8ac2a..2f4713dcc 100644 --- a/ee/tuf/library_lookup.go +++ b/ee/tuf/library_lookup.go @@ -27,6 +27,7 @@ type autoupdateConfig struct { updateDirectory string channel string localDevelopmentPath string + hostname string } // CheckOutLatestWithoutConfig returns information about the latest downloaded executable for our binary, @@ -46,6 +47,18 @@ func CheckOutLatestWithoutConfig(binary autoupdatableBinary, slogger *slog.Logge return &BinaryUpdateInfo{Path: cfg.localDevelopmentPath}, nil } + // check for old root directories before returning final config in case we've stomped over with windows MSI install + updatedRootDirectory := launcher.DetermineRootDirectoryOverride(cfg.rootDirectory, cfg.hostname) + if updatedRootDirectory != cfg.rootDirectory { + slogger.Log(context.TODO(), slog.LevelInfo, + "old root directory contents detected, overriding for autoupdate config", + "opts_root_directory", cfg.rootDirectory, + "updated_root_directory", updatedRootDirectory, + ) + + cfg.rootDirectory = updatedRootDirectory + } + // Get update channel from startup settings pinnedVersion, updateChannel, err := getUpdateSettingsFromStartupSettings(ctx, binary, cfg.rootDirectory) if err != nil { @@ -112,12 +125,13 @@ func getAutoupdateConfig(args []string) (*autoupdateConfig, error) { pflagSet.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{UnknownFlags: true} // Extract the config flag plus the autoupdate flags - var flConfigFilePath, flRootDirectory, flUpdateDirectory, flUpdateChannel, flLocalDevelopmentPath string + var flConfigFilePath, flRootDirectory, flUpdateDirectory, flUpdateChannel, flLocalDevelopmentPath, flHostname string pflagSet.StringVar(&flConfigFilePath, "config", "", "") pflagSet.StringVar(&flRootDirectory, "root_directory", "", "") pflagSet.StringVar(&flUpdateDirectory, "update_directory", "", "") pflagSet.StringVar(&flUpdateChannel, "update_channel", "", "") pflagSet.StringVar(&flLocalDevelopmentPath, "localdev_path", "", "") + pflagSet.StringVar(&flHostname, "hostname", "", "") if err := pflagSet.Parse(argsToParse); err != nil { return nil, fmt.Errorf("parsing command-line flags: %w", err) @@ -171,6 +185,8 @@ func getAutoupdateConfigFromFile(configFilePath string) (*autoupdateConfig, erro cfg.channel = value case "localdev_path": cfg.localDevelopmentPath = value + case "hostname": + cfg.hostname = value } return nil }); err != nil { diff --git a/pkg/launcher/options.go b/pkg/launcher/options.go index f97d74be8..ce2c2bdca 100644 --- a/pkg/launcher/options.go +++ b/pkg/launcher/options.go @@ -367,6 +367,14 @@ func ParseOptions(subcommandName string, args []string) (*Options, error) { *flKolideHosted = true } + if runtime.GOOS == "windows" { + // check for old root directories before returning the configured option in case we've stomped over with windows MSI install + updatedRootDirectory := DetermineRootDirectoryOverride(*flRootDirectory, *flKolideServerURL) + if updatedRootDirectory != *flRootDirectory { + *flRootDirectory = updatedRootDirectory + } + } + opts := &Options{ Autoupdate: *flAutoupdate, AutoupdateInterval: *flAutoupdateInterval, diff --git a/pkg/launcher/paths.go b/pkg/launcher/paths.go index 6398b3d01..0447cb660 100644 --- a/pkg/launcher/paths.go +++ b/pkg/launcher/paths.go @@ -36,6 +36,11 @@ const ( SecretFile ) +var likelyWindowsRootDirPaths = []string{ + "C:\\ProgramData\\Kolide\\Launcher-kolide-k2\\data", + "C:\\Program Files\\Kolide\\Launcher-kolide-k2\\data", +} + func DefaultPath(path defaultPath) string { if runtime.GOOS == "windows" { switch path { @@ -77,3 +82,69 @@ func DefaultPath(path defaultPath) string { return "" } } + +// DetermineRootDirectoryOverride is used specifically for windows deployments to override the +// configured root directory if another well known location containing a launcher DB already exists +// This is used by ParseOptions which doesn't have access to a logger, we should add more logging here +// when we have that available +func DetermineRootDirectoryOverride(optsRootDirectory, kolideServerURL string) string { + if runtime.GOOS != "windows" { + return optsRootDirectory + } + + // don't mess with the path if this installation isn't pointing to a kolide server URL + if kolideServerURL != "k2device.kolide.com" && kolideServerURL != "k2device-preprod.kolide.com" { + return optsRootDirectory + } + + optsDBLocation := filepath.Join(optsRootDirectory, "launcher.db") + dbExists, err := nonEmptyFileExists(optsDBLocation) + // If we get an unknown error, back out from making any options changes. This is an + // unlikely path but doesn't feel right updating the rootDirectory without knowing what's going + // on here + if err != nil { + // we should add logs here when available - revisit with https://github.com/kolide/launcher/issues/1698 + return optsRootDirectory + } + + // database already exists in configured root directory, keep that + if dbExists { + return optsRootDirectory + } + + // we know this is a fresh install with no launcher.db in the configured root directory, + // check likely locations and return updated rootDirectory if found + for _, path := range likelyWindowsRootDirPaths { + if path == optsRootDirectory { // we already know this does not contain an enrolled DB + continue + } + + testingLocation := filepath.Join(path, "launcher.db") + dbExists, err := nonEmptyFileExists(testingLocation) + if err == nil && dbExists { + return path + } + + if err != nil { + // we should add logs here when available - revisit with https://github.com/kolide/launcher/issues/1698 + continue + } + } + + // if all else fails, return the originally configured rootDirectory - + // this is expected for devices that are truly installing from MSI for the first time + return optsRootDirectory +} + +func nonEmptyFileExists(path string) (bool, error) { + fileInfo, err := os.Stat(path) + if os.IsNotExist(err) { + return false, nil + } + + if err != nil { + return false, err + } + + return fileInfo.Size() > 0, nil +}