Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add root directory override detection to autoupdate lookups #1744

Merged
merged 4 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading