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

Unique filename for osquery.sock #1935

Merged
16 changes: 9 additions & 7 deletions ee/tables/osquery_instance_history/osquery_instance_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

func TablePlugin() *table.Plugin {
columns := []table.ColumnDefinition{
table.TextColumn("instance_run_id"),
table.TextColumn("start_time"),
table.TextColumn("connect_time"),
table.TextColumn("exit_time"),
Expand All @@ -32,13 +33,14 @@ func generate() table.GenerateFunc {
for _, instance := range history {

results = append(results, map[string]string{
"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,
})
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/osquery/interactive/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down
54 changes: 41 additions & 13 deletions pkg/osquery/interactive/interactive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,42 +62,53 @@ 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",
},
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 {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

rootDir := t.TempDir()
var rootDir string
if tt.useShortRootDir {
rootDir = testRootDirectory(t)
} else {
rootDir = t.TempDir()
}

require.NoError(t, downloadOsquery(rootDir))

var logBytes threadsafebuffer.ThreadSafeBuffer
Expand All @@ -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)
Expand Down Expand Up @@ -176,3 +187,20 @@ 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 {
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
}
3 changes: 2 additions & 1 deletion pkg/osquery/runtime/history/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(runId string) (*Instance, error) {
currentHistory.Lock()
defer currentHistory.Unlock()

Expand All @@ -98,6 +98,7 @@ func NewInstance() (*Instance, error) {
}

newInstance := &Instance{
RunId: runId,
StartTime: timeNow(),
Hostname: hostname,
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/osquery/runtime/history/history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")

Expand Down
3 changes: 2 additions & 1 deletion pkg/osquery/runtime/history/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (
)

type Instance struct {
RunId string // ID for instance, assigned by launcher
StartTime string
ConnectTime string
ExitTime string
Hostname string
InstanceId string
InstanceId string // ID from osquery
Version string
Error string
}
Expand Down
28 changes: 22 additions & 6 deletions pkg/osquery/runtime/osqueryinstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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
Expand Down Expand Up @@ -93,6 +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
runId string // string identifier for this instance
errgroup *errgroup.Group
saasExtension *launcherosq.Extension
doneCtx context.Context // nolint:containedctx
Expand Down Expand Up @@ -175,10 +189,12 @@ type osqueryOptions struct {
}

func newInstance(knapsack types.Knapsack, serviceClient service.KolideService, opts ...OsqueryInstanceOption) *OsqueryInstance {
runId := ulid.New()
i := &OsqueryInstance{
knapsack: knapsack,
slogger: knapsack.Slogger().With("component", "osquery_instance"),
slogger: knapsack.Slogger().With("component", "osquery_instance", "instance_run_id", runId),
serviceClient: serviceClient,
runId: runId,
}

for _, opt := range opts {
Expand Down Expand Up @@ -244,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.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)
Expand Down Expand Up @@ -344,7 +360,7 @@ func (i *OsqueryInstance) Launch() error {
"osquery socket created",
)

stats, err := history.NewInstance()
stats, err := history.NewInstance(i.runId)
if err != nil {
i.slogger.Log(ctx, slog.LevelWarn,
"could not create new osquery instance history",
Expand Down Expand Up @@ -649,20 +665,20 @@ 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, runId string, opts osqueryOptions) (*osqueryFilePaths, error) {

// Determine the path to the extension socket
extensionSocketPath := opts.extensionSocketPath
if extensionSocketPath == "" {
extensionSocketPath = SocketPath(rootDirectory)
extensionSocketPath = SocketPath(rootDirectory, runId)
}

extensionAutoloadPath := filepath.Join(rootDirectory, "osquery.autoload")

// 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", runId)),
databasePath: filepath.Join(rootDirectory, "osquery.db"),
augeasPath: filepath.Join(rootDirectory, "augeas-lenses"),
extensionSocketPath: extensionSocketPath,
Expand Down
16 changes: 0 additions & 16 deletions pkg/osquery/runtime/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,12 @@ import (
"fmt"
"log/slog"
"sync"
"time"

"github.com/kolide/launcher/ee/agent/flags/keys"
"github.com/kolide/launcher/ee/agent/types"
"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
Expand Down
4 changes: 2 additions & 2 deletions pkg/osquery/runtime/runtime_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, id string) string {
return filepath.Join(rootDir, fmt.Sprintf("osquery-%s.sock", id))
}

func platformArgs() []string {
Expand Down
5 changes: 2 additions & 3 deletions pkg/osquery/runtime/runtime_helpers_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"syscall"
"time"

"github.com/kolide/kit/ulid"
"github.com/kolide/launcher/ee/allowedcmd"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -47,7 +46,7 @@ func killProcessGroup(origCmd *exec.Cmd) error {
return nil
}

func SocketPath(rootDir 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.
Expand All @@ -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`, id)
}

func platformArgs() []string {
Expand Down
4 changes: 2 additions & 2 deletions pkg/osquery/runtime/runtime_posix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
Loading
Loading