From 4335748f3075e3b564f5f782d530832d5effd21a Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Thu, 31 Oct 2024 14:28:39 -0400 Subject: [PATCH 1/9] Make socket paths unique per-instance --- pkg/osquery/interactive/interactive.go | 3 +- pkg/osquery/runtime/osqueryinstance.go | 15 ++++--- pkg/osquery/runtime/runner.go | 5 ++- pkg/osquery/runtime/runtime_helpers.go | 4 +- .../runtime/runtime_helpers_windows.go | 5 +-- pkg/osquery/runtime/runtime_posix_test.go | 4 +- pkg/osquery/runtime/runtime_test.go | 44 ++++++++++++++----- 7 files changed, 52 insertions(+), 28 deletions(-) diff --git a/pkg/osquery/interactive/interactive.go b/pkg/osquery/interactive/interactive.go index dd9ba8942..cef69dd71 100644 --- a/pkg/osquery/interactive/interactive.go +++ b/pkg/osquery/interactive/interactive.go @@ -11,6 +11,7 @@ import ( "time" "github.com/kolide/kit/fsutil" + "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/startupsettings" "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/pkg/augeas" @@ -30,7 +31,7 @@ func StartProcess(knapsack types.Knapsack, interactiveRootDir string) (*os.Proce return nil, nil, fmt.Errorf("creating root dir for interactive mode: %w", err) } - socketPath := osqueryRuntime.SocketPath(interactiveRootDir) + socketPath := osqueryRuntime.SocketPath(interactiveRootDir, ulid.New()) augeasLensesPath := filepath.Join(interactiveRootDir, "augeas-lenses") // only install augeas lenses on non-windows platforms diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index 3a131f721..79eab4944 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -15,7 +15,6 @@ import ( "sync" "time" - "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/ee/gowrapper" "github.com/kolide/launcher/pkg/backoff" @@ -93,6 +92,7 @@ type OsqueryInstance struct { serviceClient service.KolideService // the following are instance artifacts that are created and held as a result // of launching an osqueryd process + id string // string identifier for this instance errgroup *errgroup.Group saasExtension *launcherosq.Extension doneCtx context.Context // nolint:containedctx @@ -174,11 +174,12 @@ type osqueryOptions struct { stdout io.Writer } -func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *OsqueryInstance { +func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, instanceId string, opts ...OsqueryInstanceOption) *OsqueryInstance { i := &OsqueryInstance{ knapsack: knapsack, - slogger: knapsack.Slogger().With("component", "osquery_instance"), + slogger: knapsack.Slogger().With("component", "osquery_instance", "instance_id", instanceId), serviceClient: serviceClient, + id: instanceId, } for _, opt := range opts { @@ -208,7 +209,7 @@ func (i *OsqueryInstance) launch() error { // Based on the root directory, calculate the file names of all of the // required osquery artifact files. - paths, err := calculateOsqueryPaths(i.knapsack.RootDirectory(), i.opts) + paths, err := calculateOsqueryPaths(i.knapsack.RootDirectory(), i.id, i.opts) if err != nil { traces.SetError(span, fmt.Errorf("could not calculate osquery file paths: %w", err)) return fmt.Errorf("could not calculate osquery file paths: %w", err) @@ -613,12 +614,12 @@ type osqueryFilePaths struct { // In return, a structure of paths is returned that can be used to launch an // osqueryd instance. An error may be returned if the supplied parameters are // unacceptable. -func calculateOsqueryPaths(rootDirectory string, opts osqueryOptions) (*osqueryFilePaths, error) { +func calculateOsqueryPaths(rootDirectory string, instanceId string, opts osqueryOptions) (*osqueryFilePaths, error) { // Determine the path to the extension socket extensionSocketPath := opts.extensionSocketPath if extensionSocketPath == "" { - extensionSocketPath = SocketPath(rootDirectory) + extensionSocketPath = SocketPath(rootDirectory, instanceId) } extensionAutoloadPath := filepath.Join(rootDirectory, "osquery.autoload") @@ -626,7 +627,7 @@ func calculateOsqueryPaths(rootDirectory string, opts osqueryOptions) (*osqueryF // We want to use a unique pidfile per launcher run to avoid file locking issues. // See: https://github.com/kolide/launcher/issues/1599 osqueryFilePaths := &osqueryFilePaths{ - pidfilePath: filepath.Join(rootDirectory, fmt.Sprintf("osquery-%s.pid", ulid.New())), + pidfilePath: filepath.Join(rootDirectory, fmt.Sprintf("osquery-%s.pid", instanceId)), databasePath: filepath.Join(rootDirectory, "osquery.db"), augeasPath: filepath.Join(rootDirectory, "augeas-lenses"), extensionSocketPath: extensionSocketPath, diff --git a/pkg/osquery/runtime/runner.go b/pkg/osquery/runtime/runner.go index a463d66ab..c403154d6 100644 --- a/pkg/osquery/runtime/runner.go +++ b/pkg/osquery/runtime/runner.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/flags/keys" "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/pkg/service" @@ -40,7 +41,7 @@ type Runner struct { func New(k types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *Runner { runner := &Runner{ - instance: newInstance(k, serviceClient, opts...), + instance: newInstance(k, serviceClient, ulid.New(), opts...), slogger: k.Slogger().With("component", "osquery_runner"), knapsack: k, serviceClient: serviceClient, @@ -107,7 +108,7 @@ func (r *Runner) Run() error { } r.instanceLock.Lock() - r.instance = newInstance(r.knapsack, r.serviceClient, r.opts...) + r.instance = newInstance(r.knapsack, r.serviceClient, ulid.New(), r.opts...) if err := r.instance.launch(); err != nil { r.slogger.Log(context.TODO(), slog.LevelWarn, "fatal error restarting instance, shutting down", diff --git a/pkg/osquery/runtime/runtime_helpers.go b/pkg/osquery/runtime/runtime_helpers.go index cd55bd254..2eb3dd9d2 100644 --- a/pkg/osquery/runtime/runtime_helpers.go +++ b/pkg/osquery/runtime/runtime_helpers.go @@ -22,8 +22,8 @@ func killProcessGroup(cmd *exec.Cmd) error { return nil } -func SocketPath(rootDir string) string { - return filepath.Join(rootDir, "osquery.sock") +func SocketPath(rootDir string, instanceId string) string { + return filepath.Join(rootDir, fmt.Sprintf("osquery-%s.sock", instanceId)) } func platformArgs() []string { diff --git a/pkg/osquery/runtime/runtime_helpers_windows.go b/pkg/osquery/runtime/runtime_helpers_windows.go index 5c5dda948..a14e7350f 100644 --- a/pkg/osquery/runtime/runtime_helpers_windows.go +++ b/pkg/osquery/runtime/runtime_helpers_windows.go @@ -10,7 +10,6 @@ import ( "syscall" "time" - "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/allowedcmd" "github.com/pkg/errors" ) @@ -47,7 +46,7 @@ func killProcessGroup(origCmd *exec.Cmd) error { return nil } -func SocketPath(rootDir string) string { +func SocketPath(rootDir string, instanceId string) string { // On windows, local names pipes paths are all rooted in \\.\pipe\ // their names are limited to 256 characters, and can include any // character other than backslash. They are case insensitive. @@ -62,7 +61,7 @@ func SocketPath(rootDir string) string { // // We could use something based on the launcher root, but given the // context this runs in a ulid seems simpler. - return fmt.Sprintf(`\\.\pipe\kolide-osquery-%s`, ulid.New()) + return fmt.Sprintf(`\\.\pipe\kolide-osquery-%s`, instanceId) } func platformArgs() []string { diff --git a/pkg/osquery/runtime/runtime_posix_test.go b/pkg/osquery/runtime/runtime_posix_test.go index 8d0dbdaa8..aa1ccd91d 100644 --- a/pkg/osquery/runtime/runtime_posix_test.go +++ b/pkg/osquery/runtime/runtime_posix_test.go @@ -36,7 +36,7 @@ func hasPermissionsToRunTest() bool { func TestOsquerySlowStart(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -84,7 +84,7 @@ func TestOsquerySlowStart(t *testing.T) { func TestExtensionSocketPath(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) diff --git a/pkg/osquery/runtime/runtime_test.go b/pkg/osquery/runtime/runtime_test.go index c6b195ad5..445bfcd75 100644 --- a/pkg/osquery/runtime/runtime_test.go +++ b/pkg/osquery/runtime/runtime_test.go @@ -17,6 +17,7 @@ import ( "github.com/apache/thrift/lib/go/thrift" "github.com/kolide/kit/fsutil" + "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent" "github.com/kolide/launcher/ee/agent/flags/keys" "github.com/kolide/launcher/ee/agent/storage" @@ -95,7 +96,7 @@ func TestCalculateOsqueryPaths(t *testing.T) { binDir, err := getBinDir() require.NoError(t, err) - paths, err := calculateOsqueryPaths(binDir, osqueryOptions{}) + paths, err := calculateOsqueryPaths(binDir, ulid.New(), osqueryOptions{}) require.NoError(t, err) @@ -136,7 +137,7 @@ func TestCreateOsqueryCommand(t *testing.T) { k.On("OsqueryFlags").Return([]string{}) k.On("Slogger").Return(multislogger.NewNopLogger()) - i := newInstance(k, mockServiceClient()) + i := newInstance(k, mockServiceClient(), ulid.New()) i.opts = *osqOpts i.knapsack = k @@ -160,7 +161,7 @@ func TestCreateOsqueryCommandWithFlags(t *testing.T) { k.On("OsqueryVerbose").Return(true) k.On("Slogger").Return(multislogger.NewNopLogger()) - i := newInstance(k, mockServiceClient()) + i := newInstance(k, mockServiceClient(), ulid.New()) i.opts = *osqOpts i.knapsack = k @@ -196,7 +197,7 @@ func TestCreateOsqueryCommand_SetsEnabledWatchdogSettingsAppropriately(t *testin k.On("OsqueryVerbose").Return(true) k.On("OsqueryFlags").Return([]string{}) - i := newInstance(k, mockServiceClient()) + i := newInstance(k, mockServiceClient(), ulid.New()) i.opts = *osqOpts i.knapsack = k @@ -248,7 +249,7 @@ func TestCreateOsqueryCommand_SetsDisabledWatchdogSettingsAppropriately(t *testi k.On("OsqueryVerbose").Return(true) k.On("OsqueryFlags").Return([]string{}) - i := newInstance(k, mockServiceClient()) + i := newInstance(k, mockServiceClient(), ulid.New()) i.opts = *osqOpts i.knapsack = k @@ -346,7 +347,7 @@ func TestBadBinaryPath(t *testing.T) { func TestWithOsqueryFlags(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -374,7 +375,7 @@ func TestWithOsqueryFlags(t *testing.T) { func TestFlagsChanged(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -509,7 +510,7 @@ func waitHealthy(t *testing.T, runner *Runner, logBytes *threadsafebuffer.Thread func TestSimplePath(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -541,7 +542,7 @@ func TestSimplePath(t *testing.T) { func TestMultipleShutdowns(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -572,7 +573,7 @@ func TestMultipleShutdowns(t *testing.T) { func TestOsqueryDies(t *testing.T) { t.Parallel() - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -667,7 +668,7 @@ func TestExtensionIsCleanedUp(t *testing.T) { // sets up an osquery instance with a running extension to be used in tests. func setupOsqueryInstanceForTests(t *testing.T) (runner *Runner, logBytes *threadsafebuffer.ThreadSafeBuffer, teardown func()) { - rootDirectory := t.TempDir() + rootDirectory := testRootDirectory(t) logBytes, slogger, opts := setUpTestSlogger(rootDirectory) @@ -772,3 +773,24 @@ func setUpTestSlogger(rootDirectory string) (*threadsafebuffer.ThreadSafeBuffer, return logBytes, slogger, opts } + +// testRootDirectory returns a temporary directory suitable for use in these tests. +// The default t.TempDir is too long of a path, creating too long of an osquery +// extension socket, on posix systems. +func testRootDirectory(t *testing.T) string { + if runtime.GOOS == "windows" { + return t.TempDir() + } + + ulid := ulid.New() + rootDir := filepath.Join(os.TempDir(), ulid[len(ulid)-4:]) + require.NoError(t, os.Mkdir(rootDir, 0700)) + + t.Cleanup(func() { + if err := os.RemoveAll(rootDir); err != nil { + t.Errorf("testRootDirectory RemoveAll cleanup: %v", err) + } + }) + + return rootDir +} From 0a262290f1ac8c50a7d8521ae1dd0812481d59df Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Thu, 31 Oct 2024 14:29:43 -0400 Subject: [PATCH 2/9] Move osquery instance consts into osqueryinstance.go --- pkg/osquery/runtime/osqueryinstance.go | 13 +++++++++++++ pkg/osquery/runtime/runner.go | 16 ---------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index 79eab4944..8a44bb3c3 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -40,6 +40,19 @@ const ( // is required for osquery startup. It is called kolide_grpc for mostly historic reasons; // communication with Kolide SaaS happens over JSONRPC. KolideSaasExtensionName = "kolide_grpc" + + // How long to wait before erroring because we cannot open the osquery + // extension socket. + socketOpenTimeout = 10 * time.Second + + // How often to try to open the osquery extension socket + socketOpenInterval = 200 * time.Millisecond + + // How frequently we should healthcheck the client/server + healthCheckInterval = 60 * time.Second + + // The maximum amount of time to wait for the osquery socket to be available -- overrides context deadline + maxSocketWaitTime = 30 * time.Second ) // OsqueryInstanceOption is a functional option pattern for defining how an diff --git a/pkg/osquery/runtime/runner.go b/pkg/osquery/runtime/runner.go index c403154d6..9d00fbecc 100644 --- a/pkg/osquery/runtime/runner.go +++ b/pkg/osquery/runtime/runner.go @@ -5,7 +5,6 @@ import ( "fmt" "log/slog" "sync" - "time" "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/flags/keys" @@ -13,21 +12,6 @@ import ( "github.com/kolide/launcher/pkg/service" ) -const ( - // How long to wait before erroring because we cannot open the osquery - // extension socket. - socketOpenTimeout = 10 * time.Second - - // How often to try to open the osquery extension socket - socketOpenInterval = 200 * time.Millisecond - - // How frequently we should healthcheck the client/server - healthCheckInterval = 60 * time.Second - - // The maximum amount of time to wait for the osquery socket to be available -- overrides context deadline - maxSocketWaitTime = 30 * time.Second -) - type Runner struct { instance *OsqueryInstance instanceLock sync.Mutex From 81c00598562c5de576e27380869787dba921fc55 Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 11:12:00 -0500 Subject: [PATCH 3/9] Osquery instance should own ID --- pkg/osquery/runtime/osqueryinstance.go | 4 +++- pkg/osquery/runtime/runner.go | 5 ++--- pkg/osquery/runtime/runtime_test.go | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index 8a44bb3c3..018c3f543 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -15,6 +15,7 @@ import ( "sync" "time" + "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/ee/gowrapper" "github.com/kolide/launcher/pkg/backoff" @@ -187,7 +188,8 @@ type osqueryOptions struct { stdout io.Writer } -func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, instanceId string, opts ...OsqueryInstanceOption) *OsqueryInstance { +func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *OsqueryInstance { + instanceId := ulid.New() i := &OsqueryInstance{ knapsack: knapsack, slogger: knapsack.Slogger().With("component", "osquery_instance", "instance_id", instanceId), diff --git a/pkg/osquery/runtime/runner.go b/pkg/osquery/runtime/runner.go index 9d00fbecc..bb0356150 100644 --- a/pkg/osquery/runtime/runner.go +++ b/pkg/osquery/runtime/runner.go @@ -6,7 +6,6 @@ import ( "log/slog" "sync" - "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/flags/keys" "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/pkg/service" @@ -25,7 +24,7 @@ type Runner struct { func New(k types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *Runner { runner := &Runner{ - instance: newInstance(k, serviceClient, ulid.New(), opts...), + instance: newInstance(k, serviceClient, opts...), slogger: k.Slogger().With("component", "osquery_runner"), knapsack: k, serviceClient: serviceClient, @@ -92,7 +91,7 @@ func (r *Runner) Run() error { } r.instanceLock.Lock() - r.instance = newInstance(r.knapsack, r.serviceClient, ulid.New(), r.opts...) + r.instance = newInstance(r.knapsack, r.serviceClient, r.opts...) if err := r.instance.launch(); err != nil { r.slogger.Log(context.TODO(), slog.LevelWarn, "fatal error restarting instance, shutting down", diff --git a/pkg/osquery/runtime/runtime_test.go b/pkg/osquery/runtime/runtime_test.go index 445bfcd75..32629349c 100644 --- a/pkg/osquery/runtime/runtime_test.go +++ b/pkg/osquery/runtime/runtime_test.go @@ -137,7 +137,7 @@ func TestCreateOsqueryCommand(t *testing.T) { k.On("OsqueryFlags").Return([]string{}) k.On("Slogger").Return(multislogger.NewNopLogger()) - i := newInstance(k, mockServiceClient(), ulid.New()) + i := newInstance(k, mockServiceClient()) i.opts = *osqOpts i.knapsack = k @@ -161,7 +161,7 @@ func TestCreateOsqueryCommandWithFlags(t *testing.T) { k.On("OsqueryVerbose").Return(true) k.On("Slogger").Return(multislogger.NewNopLogger()) - i := newInstance(k, mockServiceClient(), ulid.New()) + i := newInstance(k, mockServiceClient()) i.opts = *osqOpts i.knapsack = k @@ -197,7 +197,7 @@ func TestCreateOsqueryCommand_SetsEnabledWatchdogSettingsAppropriately(t *testin k.On("OsqueryVerbose").Return(true) k.On("OsqueryFlags").Return([]string{}) - i := newInstance(k, mockServiceClient(), ulid.New()) + i := newInstance(k, mockServiceClient()) i.opts = *osqOpts i.knapsack = k @@ -249,7 +249,7 @@ func TestCreateOsqueryCommand_SetsDisabledWatchdogSettingsAppropriately(t *testi k.On("OsqueryVerbose").Return(true) k.On("OsqueryFlags").Return([]string{}) - i := newInstance(k, mockServiceClient(), ulid.New()) + i := newInstance(k, mockServiceClient()) i.opts = *osqOpts i.knapsack = k From 0d35645b93920370f6581882c795b7996e5e5cc9 Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 11:29:39 -0500 Subject: [PATCH 4/9] Rename instance ID to internal ID to disambiguate with osquery's own instance ID, add to table --- .../osquery_instance_history/osquery_instance_history.go | 2 ++ pkg/osquery/runtime/history/history.go | 7 ++++--- pkg/osquery/runtime/history/history_test.go | 3 ++- pkg/osquery/runtime/history/instance.go | 3 ++- pkg/osquery/runtime/osqueryinstance.go | 8 ++++---- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ee/tables/osquery_instance_history/osquery_instance_history.go b/ee/tables/osquery_instance_history/osquery_instance_history.go index a7cce448d..0bdf79788 100644 --- a/ee/tables/osquery_instance_history/osquery_instance_history.go +++ b/ee/tables/osquery_instance_history/osquery_instance_history.go @@ -9,6 +9,7 @@ import ( func TablePlugin() *table.Plugin { columns := []table.ColumnDefinition{ + table.TextColumn("internal_id"), table.TextColumn("start_time"), table.TextColumn("connect_time"), table.TextColumn("exit_time"), @@ -32,6 +33,7 @@ func generate() table.GenerateFunc { for _, instance := range history { results = append(results, map[string]string{ + "internal_id": instance.InternalId, "start_time": instance.StartTime, "connect_time": instance.ConnectTime, "exit_time": instance.ExitTime, diff --git a/pkg/osquery/runtime/history/history.go b/pkg/osquery/runtime/history/history.go index 7decfc0df..84a894b5f 100644 --- a/pkg/osquery/runtime/history/history.go +++ b/pkg/osquery/runtime/history/history.go @@ -88,7 +88,7 @@ func LatestInstanceUptimeMinutes() (int64, error) { } // NewInstance adds a new instance to the osquery instance history and returns it -func NewInstance() (*Instance, error) { +func NewInstance(internalId string) (*Instance, error) { currentHistory.Lock() defer currentHistory.Unlock() @@ -98,8 +98,9 @@ func NewInstance() (*Instance, error) { } newInstance := &Instance{ - StartTime: timeNow(), - Hostname: hostname, + InternalId: internalId, + StartTime: timeNow(), + Hostname: hostname, } currentHistory.addInstanceToHistory(newInstance) diff --git a/pkg/osquery/runtime/history/history_test.go b/pkg/osquery/runtime/history/history_test.go index 5cda4757c..ac91b3c61 100644 --- a/pkg/osquery/runtime/history/history_test.go +++ b/pkg/osquery/runtime/history/history_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/kolide/kit/ulid" "github.com/kolide/launcher/ee/agent/storage" storageci "github.com/kolide/launcher/ee/agent/storage/ci" "github.com/kolide/launcher/ee/agent/types" @@ -58,7 +59,7 @@ func TestNewInstance(t *testing.T) { // nolint:paralleltest require.NoError(t, InitHistory(setupStorage(t, tt.initialInstances...))) - _, err := NewInstance() + _, err := NewInstance(ulid.New()) assert.Equal(t, tt.wantNumInstances, len(currentHistory.instances), "expect history length to reflect new instance") diff --git a/pkg/osquery/runtime/history/instance.go b/pkg/osquery/runtime/history/instance.go index 251773b9a..a9c187803 100644 --- a/pkg/osquery/runtime/history/instance.go +++ b/pkg/osquery/runtime/history/instance.go @@ -6,11 +6,12 @@ import ( ) type Instance struct { + InternalId string // ID assigned by launcher StartTime string ConnectTime string ExitTime string Hostname string - InstanceId string + InstanceId string // ID from osquery Version string Error string } diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index cc574f290..5da93c78e 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -189,12 +189,12 @@ type osqueryOptions struct { } func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *OsqueryInstance { - instanceId := ulid.New() + id := ulid.New() i := &OsqueryInstance{ knapsack: knapsack, - slogger: knapsack.Slogger().With("component", "osquery_instance", "instance_id", instanceId), + slogger: knapsack.Slogger().With("component", "osquery_instance", "internal_id", id), serviceClient: serviceClient, - id: instanceId, + id: id, } for _, opt := range opts { @@ -360,7 +360,7 @@ func (i *OsqueryInstance) Launch() error { "osquery socket created", ) - stats, err := history.NewInstance() + stats, err := history.NewInstance(i.id) if err != nil { i.slogger.Log(ctx, slog.LevelWarn, "could not create new osquery instance history", From 13b8e02ff40516c4d3fdc79e6f1e8596b0372e66 Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 11:32:50 -0500 Subject: [PATCH 5/9] Finish renaming instance id to id --- pkg/osquery/runtime/osqueryinstance.go | 6 +++--- pkg/osquery/runtime/runtime_helpers.go | 4 ++-- pkg/osquery/runtime/runtime_helpers_windows.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index 5da93c78e..11aa8e001 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -665,12 +665,12 @@ type osqueryFilePaths struct { // In return, a structure of paths is returned that can be used to launch an // osqueryd instance. An error may be returned if the supplied parameters are // unacceptable. -func calculateOsqueryPaths(rootDirectory string, instanceId string, opts osqueryOptions) (*osqueryFilePaths, error) { +func calculateOsqueryPaths(rootDirectory string, id string, opts osqueryOptions) (*osqueryFilePaths, error) { // Determine the path to the extension socket extensionSocketPath := opts.extensionSocketPath if extensionSocketPath == "" { - extensionSocketPath = SocketPath(rootDirectory, instanceId) + extensionSocketPath = SocketPath(rootDirectory, id) } extensionAutoloadPath := filepath.Join(rootDirectory, "osquery.autoload") @@ -678,7 +678,7 @@ func calculateOsqueryPaths(rootDirectory string, instanceId string, opts osquery // We want to use a unique pidfile per launcher run to avoid file locking issues. // See: https://github.com/kolide/launcher/issues/1599 osqueryFilePaths := &osqueryFilePaths{ - pidfilePath: filepath.Join(rootDirectory, fmt.Sprintf("osquery-%s.pid", instanceId)), + pidfilePath: filepath.Join(rootDirectory, fmt.Sprintf("osquery-%s.pid", id)), databasePath: filepath.Join(rootDirectory, "osquery.db"), augeasPath: filepath.Join(rootDirectory, "augeas-lenses"), extensionSocketPath: extensionSocketPath, diff --git a/pkg/osquery/runtime/runtime_helpers.go b/pkg/osquery/runtime/runtime_helpers.go index 2eb3dd9d2..67eec82a4 100644 --- a/pkg/osquery/runtime/runtime_helpers.go +++ b/pkg/osquery/runtime/runtime_helpers.go @@ -22,8 +22,8 @@ func killProcessGroup(cmd *exec.Cmd) error { return nil } -func SocketPath(rootDir string, instanceId string) string { - return filepath.Join(rootDir, fmt.Sprintf("osquery-%s.sock", instanceId)) +func SocketPath(rootDir string, id string) string { + return filepath.Join(rootDir, fmt.Sprintf("osquery-%s.sock", id)) } func platformArgs() []string { diff --git a/pkg/osquery/runtime/runtime_helpers_windows.go b/pkg/osquery/runtime/runtime_helpers_windows.go index a14e7350f..7c5ed26e3 100644 --- a/pkg/osquery/runtime/runtime_helpers_windows.go +++ b/pkg/osquery/runtime/runtime_helpers_windows.go @@ -46,7 +46,7 @@ func killProcessGroup(origCmd *exec.Cmd) error { return nil } -func SocketPath(rootDir string, instanceId string) string { +func SocketPath(rootDir string, id string) string { // On windows, local names pipes paths are all rooted in \\.\pipe\ // their names are limited to 256 characters, and can include any // character other than backslash. They are case insensitive. @@ -61,7 +61,7 @@ func SocketPath(rootDir string, instanceId string) string { // // We could use something based on the launcher root, but given the // context this runs in a ulid seems simpler. - return fmt.Sprintf(`\\.\pipe\kolide-osquery-%s`, instanceId) + return fmt.Sprintf(`\\.\pipe\kolide-osquery-%s`, id) } func platformArgs() []string { From 34cd1aed38ae69423a6f14f24d72f2c8a4577ce0 Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 11:36:53 -0500 Subject: [PATCH 6/9] Rename to run id --- .../osquery_instance_history.go | 18 +++++++++--------- pkg/osquery/runtime/history/history.go | 8 ++++---- pkg/osquery/runtime/history/instance.go | 2 +- pkg/osquery/runtime/osqueryinstance.go | 18 +++++++++--------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ee/tables/osquery_instance_history/osquery_instance_history.go b/ee/tables/osquery_instance_history/osquery_instance_history.go index 0bdf79788..5cce87e2f 100644 --- a/ee/tables/osquery_instance_history/osquery_instance_history.go +++ b/ee/tables/osquery_instance_history/osquery_instance_history.go @@ -9,7 +9,7 @@ import ( func TablePlugin() *table.Plugin { columns := []table.ColumnDefinition{ - table.TextColumn("internal_id"), + table.TextColumn("instance_run_id"), table.TextColumn("start_time"), table.TextColumn("connect_time"), table.TextColumn("exit_time"), @@ -33,14 +33,14 @@ func generate() table.GenerateFunc { for _, instance := range history { results = append(results, map[string]string{ - "internal_id": instance.InternalId, - "start_time": instance.StartTime, - "connect_time": instance.ConnectTime, - "exit_time": instance.ExitTime, - "instance_id": instance.InstanceId, - "version": instance.Version, - "hostname": instance.Hostname, - "errors": instance.Error, + "instance_run_id": instance.RunId, + "start_time": instance.StartTime, + "connect_time": instance.ConnectTime, + "exit_time": instance.ExitTime, + "instance_id": instance.InstanceId, + "version": instance.Version, + "hostname": instance.Hostname, + "errors": instance.Error, }) } diff --git a/pkg/osquery/runtime/history/history.go b/pkg/osquery/runtime/history/history.go index 84a894b5f..57ae0a6c0 100644 --- a/pkg/osquery/runtime/history/history.go +++ b/pkg/osquery/runtime/history/history.go @@ -88,7 +88,7 @@ func LatestInstanceUptimeMinutes() (int64, error) { } // NewInstance adds a new instance to the osquery instance history and returns it -func NewInstance(internalId string) (*Instance, error) { +func NewInstance(runId string) (*Instance, error) { currentHistory.Lock() defer currentHistory.Unlock() @@ -98,9 +98,9 @@ func NewInstance(internalId string) (*Instance, error) { } newInstance := &Instance{ - InternalId: internalId, - StartTime: timeNow(), - Hostname: hostname, + RunId: runId, + StartTime: timeNow(), + Hostname: hostname, } currentHistory.addInstanceToHistory(newInstance) diff --git a/pkg/osquery/runtime/history/instance.go b/pkg/osquery/runtime/history/instance.go index a9c187803..aaf65f22a 100644 --- a/pkg/osquery/runtime/history/instance.go +++ b/pkg/osquery/runtime/history/instance.go @@ -6,7 +6,7 @@ import ( ) type Instance struct { - InternalId string // ID assigned by launcher + RunId string // ID for instance, assigned by launcher StartTime string ConnectTime string ExitTime string diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index 11aa8e001..6e9173dd6 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -106,7 +106,7 @@ type OsqueryInstance struct { serviceClient service.KolideService // the following are instance artifacts that are created and held as a result // of launching an osqueryd process - id string // string identifier for this instance + runId string // string identifier for this instance errgroup *errgroup.Group saasExtension *launcherosq.Extension doneCtx context.Context // nolint:containedctx @@ -189,12 +189,12 @@ type osqueryOptions struct { } func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *OsqueryInstance { - id := ulid.New() + runId := ulid.New() i := &OsqueryInstance{ knapsack: knapsack, - slogger: knapsack.Slogger().With("component", "osquery_instance", "internal_id", id), + slogger: knapsack.Slogger().With("component", "osquery_instance", "instance_run_id", runId), serviceClient: serviceClient, - id: id, + runId: runId, } for _, opt := range opts { @@ -260,7 +260,7 @@ func (i *OsqueryInstance) Launch() error { // Based on the root directory, calculate the file names of all of the // required osquery artifact files. - paths, err := calculateOsqueryPaths(i.knapsack.RootDirectory(), i.id, i.opts) + paths, err := calculateOsqueryPaths(i.knapsack.RootDirectory(), i.runId, i.opts) if err != nil { traces.SetError(span, fmt.Errorf("could not calculate osquery file paths: %w", err)) return fmt.Errorf("could not calculate osquery file paths: %w", err) @@ -360,7 +360,7 @@ func (i *OsqueryInstance) Launch() error { "osquery socket created", ) - stats, err := history.NewInstance(i.id) + stats, err := history.NewInstance(i.runId) if err != nil { i.slogger.Log(ctx, slog.LevelWarn, "could not create new osquery instance history", @@ -665,12 +665,12 @@ type osqueryFilePaths struct { // In return, a structure of paths is returned that can be used to launch an // osqueryd instance. An error may be returned if the supplied parameters are // unacceptable. -func calculateOsqueryPaths(rootDirectory string, id string, opts osqueryOptions) (*osqueryFilePaths, error) { +func calculateOsqueryPaths(rootDirectory string, runId string, opts osqueryOptions) (*osqueryFilePaths, error) { // Determine the path to the extension socket extensionSocketPath := opts.extensionSocketPath if extensionSocketPath == "" { - extensionSocketPath = SocketPath(rootDirectory, id) + extensionSocketPath = SocketPath(rootDirectory, runId) } extensionAutoloadPath := filepath.Join(rootDirectory, "osquery.autoload") @@ -678,7 +678,7 @@ func calculateOsqueryPaths(rootDirectory string, id string, opts osqueryOptions) // We want to use a unique pidfile per launcher run to avoid file locking issues. // See: https://github.com/kolide/launcher/issues/1599 osqueryFilePaths := &osqueryFilePaths{ - pidfilePath: filepath.Join(rootDirectory, fmt.Sprintf("osquery-%s.pid", id)), + pidfilePath: filepath.Join(rootDirectory, fmt.Sprintf("osquery-%s.pid", runId)), databasePath: filepath.Join(rootDirectory, "osquery.db"), augeasPath: filepath.Join(rootDirectory, "augeas-lenses"), extensionSocketPath: extensionSocketPath, From c7cfcb879c36c418106dea2a5a65e22225dc5ff4 Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 12:08:37 -0500 Subject: [PATCH 7/9] Update test directory for interactive too --- pkg/osquery/interactive/interactive_test.go | 23 ++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pkg/osquery/interactive/interactive_test.go b/pkg/osquery/interactive/interactive_test.go index d44c9de03..c78875422 100644 --- a/pkg/osquery/interactive/interactive_test.go +++ b/pkg/osquery/interactive/interactive_test.go @@ -97,7 +97,7 @@ func TestProc(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - rootDir := t.TempDir() + rootDir := testRootDirectory(t) require.NoError(t, downloadOsquery(rootDir)) var logBytes threadsafebuffer.ThreadSafeBuffer @@ -176,3 +176,24 @@ func downloadOsquery(dir string) error { return nil } + +// testRootDirectory returns a temporary directory suitable for use in these tests. +// The default t.TempDir is too long of a path, creating too long of an osquery +// extension socket, on posix systems. +func testRootDirectory(t *testing.T) string { + if runtime.GOOS == "windows" { + return t.TempDir() + } + + ulid := ulid.New() + rootDir := filepath.Join(os.TempDir(), ulid[len(ulid)-4:]) + require.NoError(t, os.Mkdir(rootDir, 0700)) + + t.Cleanup(func() { + if err := os.RemoveAll(rootDir); err != nil { + t.Errorf("testRootDirectory RemoveAll cleanup: %v", err) + } + }) + + return rootDir +} From 61ea62ab635975daaf445f6fb15fa7f4081b17ae Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 12:32:48 -0500 Subject: [PATCH 8/9] Fix lint error --- pkg/osquery/interactive/interactive_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/osquery/interactive/interactive_test.go b/pkg/osquery/interactive/interactive_test.go index c78875422..84f1caaed 100644 --- a/pkg/osquery/interactive/interactive_test.go +++ b/pkg/osquery/interactive/interactive_test.go @@ -181,10 +181,6 @@ func downloadOsquery(dir string) error { // The default t.TempDir is too long of a path, creating too long of an osquery // extension socket, on posix systems. func testRootDirectory(t *testing.T) string { - if runtime.GOOS == "windows" { - return t.TempDir() - } - ulid := ulid.New() rootDir := filepath.Join(os.TempDir(), ulid[len(ulid)-4:]) require.NoError(t, os.Mkdir(rootDir, 0700)) From 08fc483566b8377d6878d8eee03a036617c2c2f4 Mon Sep 17 00:00:00 2001 From: RebeccaMahany Date: Mon, 4 Nov 2024 12:38:23 -0500 Subject: [PATCH 9/9] Fix test --- pkg/osquery/interactive/interactive_test.go | 37 +++++++++++++-------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/pkg/osquery/interactive/interactive_test.go b/pkg/osquery/interactive/interactive_test.go index 84f1caaed..ff9298425 100644 --- a/pkg/osquery/interactive/interactive_test.go +++ b/pkg/osquery/interactive/interactive_test.go @@ -62,17 +62,20 @@ func TestProc(t *testing.T) { t.Parallel() tests := []struct { - name string - osqueryFlags []string - wantProc bool - errContainsStr string + name string + useShortRootDir bool + osqueryFlags []string + wantProc bool + errContainsStr string }{ { - name: "no flags", - wantProc: true, + name: "no flags", + useShortRootDir: true, + wantProc: true, }, { - name: "flags", + name: "flags", + useShortRootDir: true, osqueryFlags: []string{ "verbose", "force=false", @@ -80,16 +83,18 @@ func TestProc(t *testing.T) { wantProc: true, }, { - name: "config path", + name: "config path", + useShortRootDir: true, osqueryFlags: []string{ fmt.Sprintf("config_path=%s", ulid.New()), }, wantProc: true, }, { - name: "socket path too long, the name of the test causes the socket path to be to long to be created, resulting in timeout waiting for the socket", - wantProc: false, - errContainsStr: "error waiting for osquery to create socket", + name: "socket path too long, the name of the test causes the socket path to be to long to be created, resulting in timeout waiting for the socket", + useShortRootDir: false, + wantProc: false, + errContainsStr: "error waiting for osquery to create socket", }, } for _, tt := range tests { @@ -97,7 +102,13 @@ func TestProc(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - rootDir := testRootDirectory(t) + var rootDir string + if tt.useShortRootDir { + rootDir = testRootDirectory(t) + } else { + rootDir = t.TempDir() + } + require.NoError(t, downloadOsquery(rootDir)) var logBytes threadsafebuffer.ThreadSafeBuffer @@ -110,7 +121,7 @@ func TestProc(t *testing.T) { mockSack.On("OsquerydPath").Return(filepath.Join(rootDir, "osqueryd")) mockSack.On("OsqueryFlags").Return(tt.osqueryFlags) mockSack.On("Slogger").Return(slogger) - mockSack.On("RootDirectory").Maybe().Return("whatever_the_root_launcher_dir_is") + mockSack.On("RootDirectory").Maybe().Return(rootDir) store, err := storageci.NewStore(t, slogger, storage.KatcConfigStore.String()) require.NoError(t, err) mockSack.On("KatcConfigStore").Return(store)