Skip to content

Commit

Permalink
Add root directory override detection to autoupdate lookups (#1744)
Browse files Browse the repository at this point in the history
  • Loading branch information
zackattack01 authored Jun 10, 2024
1 parent 081e66b commit e13781c
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 96 deletions.
95 changes: 0 additions & 95 deletions cmd/launcher/svc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}
18 changes: 17 additions & 1 deletion ee/tuf/library_lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions pkg/launcher/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
71 changes: 71 additions & 0 deletions pkg/launcher/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

0 comments on commit e13781c

Please sign in to comment.