Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into becca/findnew-remove
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany committed May 1, 2024
2 parents 67ade2a + 29687be commit ba1595a
Show file tree
Hide file tree
Showing 39 changed files with 651 additions and 163 deletions.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ linters-settings:
forbid:
- p: ^exec\.Command.*$
msg: use ee/allowedcmd functions instead
- p: ^os\.Exit.*$
msg: do not use os.Exit so that launcher can shut down gracefully
- p: ^logutil\.Fatal.*$
msg: do not use logutil.Fatal so that launcher can shut down gracefully
sloglint:
kv-only: true
context-only: true
Expand Down
50 changes: 27 additions & 23 deletions cmd/launcher/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,36 @@ package main

import (
"context"
"flag"
"fmt"
"log/slog"
"os"
"strings"

"github.com/kolide/launcher/cmd/launcher/internal"
"github.com/kolide/launcher/ee/agent"
"github.com/kolide/launcher/ee/agent/flags"
"github.com/kolide/launcher/ee/agent/knapsack"
"github.com/kolide/launcher/ee/agent/storage/inmemory"
"github.com/kolide/launcher/ee/tuf"
"github.com/kolide/launcher/pkg/launcher"
"github.com/kolide/launcher/pkg/log/multislogger"
"github.com/kolide/launcher/pkg/osquery/interactive"
)

func runInteractive(systemMultiSlogger *multislogger.MultiSlogger, args []string) error {
flagset := flag.NewFlagSet("interactive", flag.ExitOnError)
var (
flDebug = flagset.Bool("debug", false, "enable debug logging")
flOsquerydPath = flagset.String("osqueryd_path", "", "The path to the oqueryd binary")
flOsqueryFlags launcher.ArrayFlags
)

flagset.Var(&flOsqueryFlags, "osquery_flag", "Flags to pass to osquery (possibly overriding Launcher defaults)")

flagset.Usage = commandUsage(flagset, "launcher interactive")
if err := flagset.Parse(args); err != nil {
opts, err := launcher.ParseOptions("interactive", args)
if err != nil {
return err
}

// here we are looking for the launcher "proper" root directory so that we know where
// to find the kv.sqlite where we can pull the auto table construction config from
if opts.RootDirectory == "" {
opts.RootDirectory = launcher.DefaultPath(launcher.RootDirectory)
}

slogLevel := slog.LevelInfo
if *flDebug {
if opts.Debug {
slogLevel = slog.LevelDebug
}

Expand All @@ -42,31 +41,31 @@ func runInteractive(systemMultiSlogger *multislogger.MultiSlogger, args []string
AddSource: true,
}))

osquerydPath := *flOsquerydPath
if osquerydPath == "" {
if opts.OsquerydPath == "" {
latestOsquerydBinary, err := tuf.CheckOutLatestWithoutConfig("osqueryd", systemMultiSlogger.Logger)
if err != nil {
return fmt.Errorf("finding osqueryd binary: %w", err)
} else {
osquerydPath = latestOsquerydBinary.Path
opts.OsquerydPath = latestOsquerydBinary.Path
}
}

// have to keep tempdir name short so we don't exceed socket length
rootDir, err := agent.MkdirTemp("launcher-interactive")
// this is a tmp root directory that launcher can use to store files it needs to run
// such as the osquery socket and augeas lense files
interactiveRootDir, err := agent.MkdirTemp("launcher-interactive")
if err != nil {
return fmt.Errorf("creating temp dir for interactive mode: %w", err)
}

defer func() {
if err := os.RemoveAll(rootDir); err != nil {
if err := os.RemoveAll(interactiveRootDir); err != nil {
fmt.Printf("error removing launcher interactive temp dir: %s\n", err)
}
}()

hasTlsServerCertsOsqueryFlag := false
// check to see if we were passed a tls_server_certs flag
for _, v := range flOsqueryFlags {
for _, v := range opts.OsqueryFlags {
if strings.HasPrefix(v, "tls_server_certs") {
hasTlsServerCertsOsqueryFlag = true
break
Expand All @@ -75,15 +74,20 @@ func runInteractive(systemMultiSlogger *multislogger.MultiSlogger, args []string

// if we were not passed a tls_server_certs flag, pass default to osquery
if !hasTlsServerCertsOsqueryFlag {
certs, err := internal.InstallCaCerts(rootDir)
certs, err := internal.InstallCaCerts(interactiveRootDir)
if err != nil {
return fmt.Errorf("installing CA certs: %w", err)
}

flOsqueryFlags = append(flOsqueryFlags, fmt.Sprintf("tls_server_certs=%s", certs))
opts.OsqueryFlags = append(opts.OsqueryFlags, fmt.Sprintf("tls_server_certs=%s", certs))
}

osqueryProc, extensionsServer, err := interactive.StartProcess(systemMultiSlogger.Logger, rootDir, osquerydPath, flOsqueryFlags)
fcOpts := []flags.Option{flags.WithCmdLineOpts(opts)}
flagController := flags.NewFlagController(systemMultiSlogger.Logger, inmemory.NewStore(), fcOpts...)

knapsack := knapsack.New(nil, flagController, nil, systemMultiSlogger, nil)

osqueryProc, extensionsServer, err := interactive.StartProcess(knapsack, interactiveRootDir)
if err != nil {
return fmt.Errorf("error starting osqueryd: %s", err)
}
Expand Down
7 changes: 7 additions & 0 deletions cmd/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,13 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl
}
defer s.Close()

if err := s.WriteSettings(); err != nil {
slogger.Log(ctx, slog.LevelError,
"writing startup settings",
"err", err,
)
}

// If we have successfully opened the DB, and written a pid,
// we expect we're live. Record the version for osquery to
// pickup
Expand Down
62 changes: 43 additions & 19 deletions cmd/launcher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,17 @@ import (
)

func main() {
os.Exit(runMain()) //nolint:forbidigo // Our only allowed usage of os.Exit is in this function
}

// runMain runs launcher main -- selecting the appropriate subcommand and running that (if provided),
// or running `runLauncher`. We wrap it so that all our deferred calls will execute before we call
// `os.Exit` in `main()`.
func runMain() int {
systemSlogger, logCloser, err := multislogger.SystemSlogger()
if err != nil {
fmt.Fprintf(os.Stderr, "error creating system logger: %v\n", err)
os.Exit(1)
return 1
}
defer logCloser.Close()

Expand Down Expand Up @@ -61,22 +68,39 @@ func main() {
// fork-bombing itself. This is an ENV, because there's no
// good way to pass it through the flags.
if !env.Bool("LAUNCHER_SKIP_UPDATES", false) && !inBuildDir {
runNewerLauncherIfAvailable(ctx, systemSlogger.Logger)
if err := runNewerLauncherIfAvailable(ctx, systemSlogger.Logger); err != nil {
systemSlogger.Log(ctx, slog.LevelInfo,
"could not run newer version of launcher",
"err", err,
)
return 1
}
}

// if the launcher is being ran with a positional argument,
// handle that argument. Fall-back to running launcher
// handle that argument.
if len(os.Args) > 1 && !strings.HasPrefix(os.Args[1], `-`) {
if err := runSubcommands(systemSlogger); err != nil {
logutil.Fatal(logger, "err", fmt.Errorf("run with positional args: %w", err))
systemSlogger.Log(ctx, slog.LevelError,
"running with positional args",
"err", err,
)
return 1
}
os.Exit(0)
return 0
}

// Fall back to running launcher
opts, err := launcher.ParseOptions("", os.Args[1:])
if err != nil {
level.Info(logger).Log("err", err)
os.Exit(1)
if launcher.IsInfoCmd(err) {
return 0
}
systemSlogger.Log(ctx, slog.LevelError,
"could not parse options",
"err", err,
)
return 0
}

// recreate the logger with the appropriate level.
Expand Down Expand Up @@ -120,14 +144,16 @@ func main() {
ctx = ctxlog.NewContext(ctx, logger)

if err := runLauncher(ctx, cancel, slogger, systemSlogger, opts); err != nil {
if tuf.IsLauncherReloadNeededErr(err) {
level.Debug(logger).Log("msg", "runLauncher exited to run newer version of launcher", "err", err.Error())
runNewerLauncherIfAvailable(ctx, slogger.Logger)
} else {
if !tuf.IsLauncherReloadNeededErr(err) {
level.Debug(logger).Log("msg", "run launcher", "stack", fmt.Sprintf("%+v", err))
logutil.Fatal(logger, err, "run launcher")
return 1
}
level.Debug(logger).Log("msg", "runLauncher exited to run newer version of launcher", "err", err.Error())
if err := runNewerLauncherIfAvailable(ctx, slogger.Logger); err != nil {
return 1
}
}
return 0
}

func runSubcommands(systemMultiSlogger *multislogger.MultiSlogger) error {
Expand Down Expand Up @@ -168,26 +194,25 @@ func runSubcommands(systemMultiSlogger *multislogger.MultiSlogger) error {
}

return nil

}

// runNewerLauncherIfAvailable checks the autoupdate library for a newer version
// of launcher than the currently-running one. If found, it will exec that version.
func runNewerLauncherIfAvailable(ctx context.Context, slogger *slog.Logger) {
func runNewerLauncherIfAvailable(ctx context.Context, slogger *slog.Logger) error {
newerBinary, err := latestLauncherPath(ctx, slogger)
if err != nil {
slogger.Log(ctx, slog.LevelError,
"could not check out latest launcher",
"err", err,
)
return
return nil
}

if newerBinary == "" {
slogger.Log(ctx, slog.LevelInfo,
"nothing newer",
)
return
return nil
}

slogger.Log(ctx, slog.LevelInfo,
Expand All @@ -202,14 +227,14 @@ func runNewerLauncherIfAvailable(ctx context.Context, slogger *slog.Logger) {
"new_binary", newerBinary,
"err", err,
)
os.Exit(1)
return fmt.Errorf("execing newer version of launcher: %w", err)
}

slogger.Log(ctx, slog.LevelError,
"execing newer version of launcher exited unexpectedly without error",
"new_binary", newerBinary,
)
os.Exit(1)
return errors.New("execing newer version of launcher exited unexpectedly without error")
}

// latestLauncherPath looks for the latest version of launcher in the new autoupdate library,
Expand Down Expand Up @@ -255,6 +280,5 @@ func runVersion(_ *multislogger.MultiSlogger, args []string) error {
version.PrintFull()
detachConsole()

os.Exit(0)
return nil
}
11 changes: 3 additions & 8 deletions cmd/launcher/svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,15 @@
package main

import (
"fmt"
"os"
"errors"

"github.com/kolide/launcher/pkg/log/multislogger"
)

func runWindowsSvc(_ *multislogger.MultiSlogger, _ []string) error {
fmt.Println("This isn't windows")
os.Exit(1)
return nil
return errors.New("this is not windows")
}

func runWindowsSvcForeground(_ *multislogger.MultiSlogger, _ []string) error {
fmt.Println("This isn't windows")
os.Exit(1)
return nil
return errors.New("this is not windows")
}
45 changes: 29 additions & 16 deletions cmd/launcher/svc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import (
"github.com/go-kit/kit/log/level"
"github.com/kolide/kit/logutil"
"github.com/kolide/kit/version"
"github.com/kolide/launcher/ee/gowrapper"
"github.com/kolide/launcher/pkg/contexts/ctxlog"
"github.com/kolide/launcher/pkg/launcher"
"github.com/kolide/launcher/pkg/log/locallogger"
"github.com/kolide/launcher/pkg/log/multislogger"
"github.com/pkg/errors"

"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/debug"
)
Expand All @@ -42,7 +43,7 @@ func runWindowsSvc(systemSlogger *multislogger.MultiSlogger, args []string) erro
"error parsing options",
"err", err,
)
os.Exit(1)
return fmt.Errorf("parsing options: %w", err)
}

localSlogger := multislogger.New()
Expand Down Expand Up @@ -137,7 +138,7 @@ func runWindowsSvcForeground(systemSlogger *multislogger.MultiSlogger, args []st
opts, err := launcher.ParseOptions("", os.Args[2:])
if err != nil {
level.Info(logger).Log("err", err)
os.Exit(1)
return fmt.Errorf("parsing options: %w", err)
}

// set extra debug options
Expand Down Expand Up @@ -167,30 +168,32 @@ func (w *winSvc) Execute(args []string, r <-chan svc.ChangeRequest, changes chan
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}

ctx = ctxlog.NewContext(ctx, w.logger)
runLauncherResults := make(chan struct{})

go func() {
gowrapper.Go(ctx, w.systemSlogger.Logger, func() {
err := runLauncher(ctx, cancel, w.slogger, w.systemSlogger, w.opts)
if err != nil {
w.systemSlogger.Log(ctx, slog.LevelInfo,
"runLauncher exited",
"err", err,
"stack_trace", fmt.Sprintf("%+v", errors.WithStack(err)),
)
changes <- svc.Status{State: svc.Stopped, Accepts: cmdsAccepted}
// Launcher is already shut down -- fully exit so that the service manager can restart the service
os.Exit(1)
} else {
w.systemSlogger.Log(ctx, slog.LevelInfo,
"runLauncher exited cleanly",
)
}

// If we get here, it means runLauncher returned nil. If we do
// nothing, the service is left running, but with no
// functionality. Instead, signal that as a stop to the service
// manager, and exit. We rely on the service manager to restart.
w.systemSlogger.Log(ctx, slog.LevelInfo,
"runLauncher exited cleanly",
// Since launcher shut down, we must signal to fully exit so that the service manager can restart the service.
runLauncherResults <- struct{}{}
}, func(r any) {
w.systemSlogger.Log(ctx, slog.LevelError,
"exiting after runLauncher panic",
"err", r,
)
changes <- svc.Status{State: svc.Stopped, Accepts: cmdsAccepted}
os.Exit(0)
}()
// Since launcher shut down, we must signal to fully exit so that the service manager can restart the service.
runLauncherResults <- struct{}{}
})

for {
select {
Expand All @@ -216,6 +219,16 @@ func (w *winSvc) Execute(args []string, r <-chan svc.ChangeRequest, changes chan
"change_request", fmt.Sprintf("%+v", c),
)
}
case <-runLauncherResults:
w.systemSlogger.Log(ctx, slog.LevelInfo,
"shutting down service after runLauncher exited",
)
// We don't want to tell the service manager that we've stopped on purpose,
// so that the service manager will restart launcher correctly.
// We use this error code largely because the windows/svc code also uses it
// and it seems semantically correct enough; it doesn't appear to matter to us
// what the code is.
return false, uint32(windows.ERROR_EXCEPTION_IN_SERVICE)
}
}
}
Loading

0 comments on commit ba1595a

Please sign in to comment.