Skip to content

Commit

Permalink
Store configuration for Kolide custom ATC tables (#1761)
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany authored Jun 26, 2024
1 parent 25f1513 commit 05ea065
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 12 deletions.
11 changes: 8 additions & 3 deletions cmd/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const (
serverDataSubsystemName = "kolide_server_data"
desktopMenuSubsystemName = "kolide_desktop_menu"
authTokensSubsystemName = "auth_tokens"
katcSubsystemName = "katc_config" // Kolide ATC
)

// runLauncher is the entry point into running launcher. It creates a
Expand Down Expand Up @@ -242,13 +243,13 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl
startupSpan.AddEvent("log_shipper_init_completed")
}

s, err := startupsettings.OpenWriter(ctx, k)
startupSettingsWriter, err := startupsettings.OpenWriter(ctx, k)
if err != nil {
return fmt.Errorf("creating startup db: %w", err)
}
defer s.Close()
defer startupSettingsWriter.Close()

if err := s.WriteSettings(); err != nil {
if err := startupSettingsWriter.WriteSettings(); err != nil {
slogger.Log(ctx, slog.LevelError,
"writing startup settings",
"err", err,
Expand Down Expand Up @@ -400,6 +401,10 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl
controlService.RegisterConsumer(serverDataSubsystemName, keyvalueconsumer.New(k.ServerProvidedDataStore()))
// agentFlagConsumer handles agent flags pushed from the control server
controlService.RegisterConsumer(agentFlagsSubsystemName, keyvalueconsumer.New(flagController))
// katcConfigConsumer handles updates to Kolide's custom ATC tables
controlService.RegisterConsumer(katcSubsystemName, keyvalueconsumer.New(k.KatcConfigStore()))
controlService.RegisterSubscriber(katcSubsystemName, osqueryRunner)
controlService.RegisterSubscriber(katcSubsystemName, startupSettingsWriter)

runner, err = desktopRunner.New(
k,
Expand Down
4 changes: 4 additions & 0 deletions ee/agent/knapsack/knapsack.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func (k *knapsack) AgentFlagsStore() types.KVStore {
return k.getKVStore(storage.AgentFlagsStore)
}

func (k *knapsack) KatcConfigStore() types.KVStore {
return k.getKVStore(storage.KatcConfigStore)
}

func (k *knapsack) AutoupdateErrorsStore() types.KVStore {
return k.getKVStore(storage.AutoupdateErrorsStore)
}
Expand Down
37 changes: 37 additions & 0 deletions ee/agent/startupsettings/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ func OpenWriter(ctx context.Context, knapsack types.Knapsack) (*startupSettingsW
return s, nil
}

// Ping satisfies the control.subscriber interface -- the runner subscribes to changes to
// the katc_config subsystem.
func (s *startupSettingsWriter) Ping() {
if err := s.WriteSettings(); err != nil {
s.knapsack.Slogger().Log(context.TODO(), slog.LevelWarn,
"could not write updated settings",
"err", err,
)
}
}

// WriteSettings updates the flags with their values from the agent flag data store.
func (s *startupSettingsWriter) WriteSettings() error {
updatedFlags := make(map[string]string)
Expand All @@ -69,6 +80,15 @@ func (s *startupSettingsWriter) WriteSettings() error {
updatedFlags["auto_table_construction"] = atcConfig
}

if katcConfig, err := s.extractKATCConstructionConfig(); err != nil {
s.knapsack.Slogger().Log(context.TODO(), slog.LevelDebug,
"extracting katc_config",
"err", err,
)
} else {
updatedFlags["katc_config"] = katcConfig
}

if _, err := s.kvStore.Update(updatedFlags); err != nil {
return fmt.Errorf("writing settings: %w", err)
}
Expand Down Expand Up @@ -119,3 +139,20 @@ func (s *startupSettingsWriter) extractAutoTableConstructionConfig() (string, er

return string(atcJson), nil
}

func (s *startupSettingsWriter) extractKATCConstructionConfig() (string, error) {
kolideCfg := make(map[string]string)
if err := s.knapsack.KatcConfigStore().ForEach(func(k []byte, v []byte) error {
kolideCfg[string(k)] = string(v)
return nil
}); err != nil {
return "", fmt.Errorf("could not get Kolide ATC config from store: %w", err)
}

atcJson, err := json.Marshal(kolideCfg)
if err != nil {
return "", fmt.Errorf("could not marshal katc_config: %w", err)
}

return string(atcJson), nil
}
3 changes: 3 additions & 0 deletions ee/agent/startupsettings/writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestOpenWriter_NewDatabase(t *testing.T) {
k.On("PinnedOsquerydVersion").Return("5.11.0")
k.On("ConfigStore").Return(inmemory.NewStore())
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("KatcConfigStore").Return(inmemory.NewStore())

// Set up storage db, which should create the database and set all flags
s, err := OpenWriter(context.TODO(), k)
Expand Down Expand Up @@ -82,6 +83,7 @@ func TestOpenWriter_DatabaseAlreadyExists(t *testing.T) {
// Set up dependencies
k := typesmocks.NewKnapsack(t)
k.On("RootDirectory").Return(testRootDir)
k.On("KatcConfigStore").Return(inmemory.NewStore())
k.On("RegisterChangeObserver", mock.Anything, keys.UpdateChannel)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedLauncherVersion)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedOsquerydVersion)
Expand Down Expand Up @@ -126,6 +128,7 @@ func TestFlagsChanged(t *testing.T) {
testRootDir := t.TempDir()
k := typesmocks.NewKnapsack(t)
k.On("RootDirectory").Return(testRootDir)
k.On("KatcConfigStore").Return(inmemory.NewStore())
k.On("RegisterChangeObserver", mock.Anything, keys.UpdateChannel)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedLauncherVersion)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedOsquerydVersion)
Expand Down
1 change: 1 addition & 0 deletions ee/agent/storage/bbolt/stores_bbolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func MakeStores(ctx context.Context, slogger *slog.Logger, db *bbolt.DB) (map[st

var storeNames = []storage.Store{
storage.AgentFlagsStore,
storage.KatcConfigStore,
storage.AutoupdateErrorsStore,
storage.ConfigStore,
storage.ControlStore,
Expand Down
1 change: 1 addition & 0 deletions ee/agent/storage/ci/stores_ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
func MakeStores(t *testing.T, slogger *slog.Logger, db *bbolt.DB) (map[storage.Store]types.KVStore, error) {
var storeNames = []storage.Store{
storage.AgentFlagsStore,
storage.KatcConfigStore,
storage.AutoupdateErrorsStore,
storage.ConfigStore,
storage.ControlStore,
Expand Down
1 change: 1 addition & 0 deletions ee/agent/storage/stores.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Store string

const (
AgentFlagsStore Store = "agent_flags" // The store used for agent control flags.
KatcConfigStore Store = "katc_config" // The store used for Kolide custom ATC configuration
AutoupdateErrorsStore Store = "tuf_autoupdate_errors" // The store used for tracking new autoupdater errors.
ConfigStore Store = "config" // The store used for launcher configuration.
ControlStore Store = "control_service_data" // The store used for control service caching data.
Expand Down
27 changes: 22 additions & 5 deletions ee/agent/types/mocks/knapsack.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ee/agent/types/stores.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "github.com/kolide/launcher/ee/agent/storage"
type Stores interface {
Stores() map[storage.Store]KVStore
AgentFlagsStore() KVStore
KatcConfigStore() KVStore
AutoupdateErrorsStore() KVStore
ConfigStore() KVStore
ControlStore() KVStore
Expand Down
2 changes: 1 addition & 1 deletion pkg/osquery/interactive/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func StartProcess(knapsack types.Knapsack, interactiveRootDir string) (*os.Proce
}

// start building list of osq plugins with the kolide tables
osqPlugins := table.PlatformTables(knapsack.Slogger(), knapsack.OsquerydPath())
osqPlugins := table.PlatformTables(knapsack, knapsack.Slogger(), knapsack.OsquerydPath())

osqueryFlags := knapsack.OsqueryFlags()
// if we were not provided a config path flag, try to add default config
Expand Down
5 changes: 5 additions & 0 deletions pkg/osquery/interactive/interactive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (

"github.com/kolide/kit/fsutil"
"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/mocks"
"github.com/kolide/launcher/pkg/log/multislogger"
"github.com/kolide/launcher/pkg/packaging"
Expand Down Expand Up @@ -97,6 +99,9 @@ func TestProc(t *testing.T) {
mockSack.On("OsqueryFlags").Return(tt.osqueryFlags)
mockSack.On("Slogger").Return(multislogger.NewNopLogger())
mockSack.On("RootDirectory").Maybe().Return("whatever_the_root_launcher_dir_is")
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
mockSack.On("KatcConfigStore").Return(store)

proc, _, err := StartProcess(mockSack, rootDir)

Expand Down
19 changes: 17 additions & 2 deletions pkg/osquery/runtime/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ func (r *Runner) FlagsChanged(flagKeys ...keys.FlagKey) {
}
}

// Ping satisfies the control.subscriber interface -- the runner subscribes to changes to
// the katc_config subsystem.
func (r *Runner) Ping() {
r.slogger.Log(context.TODO(), slog.LevelDebug,
"Kolide ATC configuration changed, restarting instance to apply",
)

if err := r.Restart(); err != nil {
r.slogger.Log(context.TODO(), slog.LevelError,
"could not restart osquery instance after Kolide ATC configuration changed",
"err", err,
)
}
}

// Restart allows you to cleanly shutdown the current instance and launch a new
// instance with the same configurations.
func (r *Runner) Restart() error {
Expand Down Expand Up @@ -469,7 +484,7 @@ func (r *Runner) launchOsqueryInstance() error {
)
}

// Now spawn an extension manage to for the tables. We need to
// Now spawn an extension manager for the tables. We need to
// start this one in the background, because the runner.Start
// function needs to return promptly enough for osquery to use
// it to enroll. Very racy
Expand All @@ -482,7 +497,7 @@ func (r *Runner) launchOsqueryInstance() error {
"errgroup", "kolide extension manager server launch",
)

plugins := table.PlatformTables(r.knapsack.Slogger().With("component", "platform_tables"), currentOsquerydBinaryPath)
plugins := table.PlatformTables(r.knapsack, r.knapsack.Slogger().With("component", "platform_tables"), currentOsquerydBinaryPath)

if len(plugins) == 0 {
return nil
Expand Down
8 changes: 8 additions & 0 deletions pkg/osquery/runtime/runtime_posix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"testing"
"time"

"github.com/kolide/launcher/ee/agent/storage"
storageci "github.com/kolide/launcher/ee/agent/storage/ci"
typesMocks "github.com/kolide/launcher/ee/agent/types/mocks"
"github.com/kolide/launcher/pkg/log/multislogger"
"github.com/kolide/launcher/pkg/threadsafebuffer"
Expand Down Expand Up @@ -51,6 +53,9 @@ func TestOsquerySlowStart(t *testing.T) {
slogger := multislogger.New(slog.NewJSONHandler(&logBytes, &slog.HandlerOptions{Level: slog.LevelDebug}))
k.On("Slogger").Return(slogger.Logger)
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

runner := New(
k,
Expand Down Expand Up @@ -95,6 +100,9 @@ func TestExtensionSocketPath(t *testing.T) {
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

extensionSocketPath := filepath.Join(rootDirectory, "sock")
runner := New(
Expand Down
18 changes: 18 additions & 0 deletions pkg/osquery/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,9 @@ func TestWithOsqueryFlags(t *testing.T) {
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

runner := New(
k,
Expand Down Expand Up @@ -368,6 +371,9 @@ func TestFlagsChanged(t *testing.T) {
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

// Start the runner
runner := New(
Expand Down Expand Up @@ -461,6 +467,9 @@ func TestSimplePath(t *testing.T) {
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

runner := New(
k,
Expand Down Expand Up @@ -489,6 +498,9 @@ func TestMultipleShutdowns(t *testing.T) {
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

runner := New(
k,
Expand Down Expand Up @@ -516,6 +528,9 @@ func TestOsqueryDies(t *testing.T) {
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

runner := New(
k,
Expand Down Expand Up @@ -610,6 +625,9 @@ func setupOsqueryInstanceForTests(t *testing.T) (runner *Runner, teardown func()
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe()
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("LatestOsquerydPath", mock.Anything).Return(testOsqueryBinaryDirectory)
store, err := storageci.NewStore(t, multislogger.NewNopLogger(), storage.KatcConfigStore.String())
require.NoError(t, err)
k.On("KatcConfigStore").Return(store)

runner = New(
k,
Expand Down
Loading

0 comments on commit 05ea065

Please sign in to comment.