From 54408ff9e4cfb40604d4eee963ffa9a00622400a Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Fri, 20 Jan 2017 14:48:54 -0500 Subject: [PATCH] move osquery enroll secret to appconfig (#1004) For #995 --- server/config/config.go | 4 - server/datastore/mysql/app_configs.go | 5 +- ...170118191001_AddEnrollSecretToAppConfig.go | 23 +++++ server/kolide/app.go | 7 ++ server/service/endpoint_middleware_test.go | 4 +- server/service/endpoint_setup.go | 30 ++++--- server/service/service_appconfig.go | 14 +++- server/service/service_osquery.go | 7 +- server/service/service_osquery_test.go | 83 ++++++++----------- tools/osquery/README.md | 2 + tools/osquery/docker-compose.yml | 4 +- 11 files changed, 113 insertions(+), 70 deletions(-) create mode 100644 server/datastore/mysql/migrations/tables/20170118191001_AddEnrollSecretToAppConfig.go diff --git a/server/config/config.go b/server/config/config.go index e2e4bc1ad8..0eec7ab56a 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -58,7 +58,6 @@ type SessionConfig struct { // OsqueryConfig defines configs related to osquery type OsqueryConfig struct { - EnrollSecret string NodeKeySize int StatusLogFile string ResultLogFile string @@ -122,7 +121,6 @@ func (man Manager) addConfigs() { man.addConfigDuration("session.duration", 24*90*time.Hour) // Osquery - man.addConfigString("osquery.enroll_secret", "") man.addConfigInt("osquery.node_key_size", 24) man.addConfigString("osquery.status_log_file", "/tmp/osquery_status") man.addConfigString("osquery.result_log_file", "/tmp/osquery_result") @@ -171,7 +169,6 @@ func (man Manager) LoadConfig() KolideConfig { Duration: man.getConfigDuration("session.duration"), }, Osquery: OsqueryConfig{ - EnrollSecret: man.getConfigString("osquery.enroll_secret"), NodeKeySize: man.getConfigInt("osquery.node_key_size"), StatusLogFile: man.getConfigString("osquery.status_log_file"), ResultLogFile: man.getConfigString("osquery.result_log_file"), @@ -374,7 +371,6 @@ func TestConfig() KolideConfig { Duration: 24 * 90 * time.Hour, }, Osquery: OsqueryConfig{ - EnrollSecret: "", NodeKeySize: 24, StatusLogFile: "", ResultLogFile: "", diff --git a/server/datastore/mysql/app_configs.go b/server/datastore/mysql/app_configs.go index f1ede2224e..8248692246 100644 --- a/server/datastore/mysql/app_configs.go +++ b/server/datastore/mysql/app_configs.go @@ -33,6 +33,7 @@ func (d *Datastore) SaveAppConfig(info *kolide.AppConfig) error { org_name, org_logo_url, kolide_server_url, + osquery_enroll_secret, smtp_configured, smtp_sender_address, smtp_server, @@ -46,11 +47,12 @@ func (d *Datastore) SaveAppConfig(info *kolide.AppConfig) error { smtp_verify_ssl_certs, smtp_enable_start_tls ) - VALUES( 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) + VALUES( 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) ON DUPLICATE KEY UPDATE org_name = VALUES(org_name), org_logo_url = VALUES(org_logo_url), kolide_server_url = VALUES(kolide_server_url), + osquery_enroll_secret = VALUES(osquery_enroll_secret), smtp_configured = VALUES(smtp_configured), smtp_sender_address = VALUES(smtp_sender_address), smtp_server = VALUES(smtp_server), @@ -69,6 +71,7 @@ func (d *Datastore) SaveAppConfig(info *kolide.AppConfig) error { info.OrgName, info.OrgLogoURL, info.KolideServerURL, + info.EnrollSecret, info.SMTPConfigured, info.SMTPSenderAddress, info.SMTPServer, diff --git a/server/datastore/mysql/migrations/tables/20170118191001_AddEnrollSecretToAppConfig.go b/server/datastore/mysql/migrations/tables/20170118191001_AddEnrollSecretToAppConfig.go new file mode 100644 index 0000000000..5d9d90650c --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20170118191001_AddEnrollSecretToAppConfig.go @@ -0,0 +1,23 @@ +package tables + +import "database/sql" + +func init() { + MigrationClient.AddMigration(Up_20170118191001, Down_20170118191001) +} + +func Up_20170118191001(tx *sql.Tx) error { + _, err := tx.Exec( + "ALTER TABLE `app_configs` " + + "ADD COLUMN `osquery_enroll_secret` VARCHAR(255) NOT NULL DEFAULT '';", + ) + return err +} + +func Down_20170118191001(tx *sql.Tx) error { + _, err := tx.Exec( + "ALTER TABLE `app_configs` " + + "DROP COLUMN `osquery_enroll_secret`;", + ) + return err +} diff --git a/server/kolide/app.go b/server/kolide/app.go index 947849f43d..07b8d2d4b8 100644 --- a/server/kolide/app.go +++ b/server/kolide/app.go @@ -78,6 +78,12 @@ type AppConfig struct { OrgName string `db:"org_name"` OrgLogoURL string `db:"org_logo_url"` KolideServerURL string `db:"kolide_server_url"` + + // EnrollSecret is the config value that must be given by osqueryd hosts + // on enrollment. + // See https://osquery.readthedocs.io/en/stable/deployment/remote/#remote-authentication + EnrollSecret string `db:"osquery_enroll_secret"` + // SMTPConfigured is a flag that indicates if smtp has been successfully // tested with the settings provided by an admin user. SMTPConfigured bool `db:"smtp_configured"` @@ -170,6 +176,7 @@ type OrgInfo struct { // ServerSettings contains general settings about the kolide App. type ServerSettings struct { KolideServerURL *string `json:"kolide_server_url"` + EnrollSecret *string `json:"osquery_enroll_secret"` } type OrderDirection int diff --git a/server/service/endpoint_middleware_test.go b/server/service/endpoint_middleware_test.go index fde93e1d88..e4bb9d6ba5 100644 --- a/server/service/endpoint_middleware_test.go +++ b/server/service/endpoint_middleware_test.go @@ -199,6 +199,8 @@ func TestGetNodeKey(t *testing.T) { func TestAuthenticatedHost(t *testing.T) { ds, err := inmem.New(config.TestConfig()) require.Nil(t, err) + _, err = ds.NewAppConfig(&kolide.AppConfig{EnrollSecret: "foobarbaz"}) + require.Nil(t, err) svc, err := newTestService(ds, nil) require.Nil(t, err) @@ -210,7 +212,7 @@ func TestAuthenticatedHost(t *testing.T) { ) ctx := context.Background() - goodNodeKey, err := svc.EnrollAgent(ctx, "", "host123") + goodNodeKey, err := svc.EnrollAgent(ctx, "foobarbaz", "host123") assert.Nil(t, err) require.NotEmpty(t, goodNodeKey) diff --git a/server/service/endpoint_setup.go b/server/service/endpoint_setup.go index e161418799..155cde719f 100644 --- a/server/service/endpoint_setup.go +++ b/server/service/endpoint_setup.go @@ -11,12 +11,14 @@ type setupRequest struct { Admin *kolide.UserPayload `json:"admin"` OrgInfo *kolide.OrgInfo `json:"org_info"` KolideServerURL *string `json:"kolide_server_url"` + EnrollSecret *string `json:"osquery_enroll_secret"` } type setupResponse struct { Admin *kolide.User `json:"admin,omitempty"` OrgInfo *kolide.OrgInfo `json:"org_info,omitempty"` KolideServerURL *string `json:"kolide_server_url"` + EnrollSecret *string `json:"osquery_enroll_secret"` Token *string `json:"token,omitempty"` Err error `json:"error,omitempty"` } @@ -32,22 +34,29 @@ func makeSetupEndpoint(svc kolide.Service) endpoint.Endpoint { err error ) req := request.(setupRequest) + if req.OrgInfo != nil { + configPayload.OrgInfo = req.OrgInfo + } + configPayload.ServerSettings = &kolide.ServerSettings{} + if req.KolideServerURL != nil { + configPayload.ServerSettings.KolideServerURL = req.KolideServerURL + } + if req.EnrollSecret != nil { + configPayload.ServerSettings.EnrollSecret = req.EnrollSecret + } + config, err = svc.NewAppConfig(ctx, configPayload) + if err != nil { + return setupResponse{Err: err}, nil + } + // creating the user should be the last action. If there's a user + // present and other errors occur, the setup endpoint closes. if req.Admin != nil { admin, err = svc.NewAdminCreatedUser(ctx, *req.Admin) if err != nil { return setupResponse{Err: err}, nil } } - if req.OrgInfo != nil { - configPayload.OrgInfo = req.OrgInfo - } - if req.KolideServerURL != nil { - configPayload.ServerSettings = &kolide.ServerSettings{KolideServerURL: req.KolideServerURL} - } - config, err = svc.NewAppConfig(ctx, configPayload) - if err != nil { - return setupResponse{Err: err}, nil - } + // If everything works to this point, log the user in and return token. If // the login fails for some reason, ignore the error and don't return // a token, forcing the user to log in manually @@ -63,6 +72,7 @@ func makeSetupEndpoint(svc kolide.Service) endpoint.Endpoint { OrgLogoURL: &config.OrgLogoURL, }, KolideServerURL: &config.KolideServerURL, + EnrollSecret: &config.EnrollSecret, Token: token, }, nil } diff --git a/server/service/service_appconfig.go b/server/service/service_appconfig.go index 2420d28a65..8543c00421 100644 --- a/server/service/service_appconfig.go +++ b/server/service/service_appconfig.go @@ -32,7 +32,16 @@ func (svc service) NewAppConfig(ctx context.Context, p kolide.AppConfigPayload) if err != nil { return nil, err } - newConfig, err := svc.ds.NewAppConfig(appConfigFromAppConfigPayload(p, *config)) + fromPayload := appConfigFromAppConfigPayload(p, *config) + if fromPayload.EnrollSecret == "" { + // generate a random string if the user hasn't set one in the form + // TODO: actually generate the string. Until there's a UI to + // set/view the secret, we need to be ablet o enroll hosts so this value + // is hardcoded. + rand := "qrsvavgrylfrpher" + fromPayload.EnrollSecret = rand + } + newConfig, err := svc.ds.NewAppConfig(fromPayload) if err != nil { return nil, err } @@ -95,6 +104,9 @@ func appConfigFromAppConfigPayload(p kolide.AppConfigPayload, config kolide.AppC if p.ServerSettings != nil && p.ServerSettings.KolideServerURL != nil { config.KolideServerURL = *p.ServerSettings.KolideServerURL } + if p.ServerSettings != nil && p.ServerSettings.EnrollSecret != nil { + config.EnrollSecret = *p.ServerSettings.EnrollSecret + } populateSMTP := func(p *kolide.SMTPSettingsPayload) { if p.SMTPAuthenticationMethod != nil { diff --git a/server/service/service_osquery.go b/server/service/service_osquery.go index d26aaf111d..e64572a278 100644 --- a/server/service/service_osquery.go +++ b/server/service/service_osquery.go @@ -50,7 +50,12 @@ func (svc service) AuthenticateHost(ctx context.Context, nodeKey string) (*kolid } func (svc service) EnrollAgent(ctx context.Context, enrollSecret, hostIdentifier string) (string, error) { - if enrollSecret != svc.config.Osquery.EnrollSecret { + config, err := svc.ds.AppConfig() + if err != nil { + return "", osqueryError{message: "getting enroll secret: " + err.Error(), nodeInvalid: true} + } + + if enrollSecret != config.EnrollSecret { return "", osqueryError{message: "invalid enroll secret", nodeInvalid: true} } diff --git a/server/service/service_osquery_test.go b/server/service/service_osquery_test.go index 4254d579b1..e198e80e34 100644 --- a/server/service/service_osquery_test.go +++ b/server/service/service_osquery_test.go @@ -24,12 +24,7 @@ import ( ) func TestEnrollAgent(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - + ds, svc, _ := setupOsqueryTests(t) ctx := context.Background() hosts, err := ds.ListHosts(kolide.ListOptions{}) @@ -37,7 +32,7 @@ func TestEnrollAgent(t *testing.T) { assert.Len(t, hosts, 0) nodeKey, err := svc.EnrollAgent(ctx, "", "host123") - assert.Nil(t, err) + require.Nil(t, err) assert.NotEmpty(t, nodeKey) hosts, err = ds.ListHosts(kolide.ListOptions{}) @@ -46,12 +41,7 @@ func TestEnrollAgent(t *testing.T) { } func TestEnrollAgentIncorrectEnrollSecret(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - + ds, svc, _ := setupOsqueryTests(t) ctx := context.Background() hosts, err := ds.ListHosts(kolide.ListOptions{}) @@ -68,18 +58,11 @@ func TestEnrollAgentIncorrectEnrollSecret(t *testing.T) { } func TestSubmitStatusLogs(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - mockClock := clock.NewMockClock() - - svc, err := newTestServiceWithClock(ds, nil, mockClock) - assert.Nil(t, err) - + ds, svc, mockClock := setupOsqueryTests(t) ctx := context.Background() - _, err = svc.EnrollAgent(ctx, "", "host123") - assert.Nil(t, err) + _, err := svc.EnrollAgent(ctx, "", "host123") + require.Nil(t, err) hosts, err := ds.ListHosts(kolide.ListOptions{}) require.Nil(t, err) @@ -140,18 +123,11 @@ func TestSubmitStatusLogs(t *testing.T) { } func TestSubmitResultLogs(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - mockClock := clock.NewMockClock() - - svc, err := newTestServiceWithClock(ds, nil, mockClock) - assert.Nil(t, err) - + ds, svc, mockClock := setupOsqueryTests(t) ctx := context.Background() - _, err = svc.EnrollAgent(ctx, "", "host123") - assert.Nil(t, err) + _, err := svc.EnrollAgent(ctx, "", "host123") + require.Nil(t, err) hosts, err := ds.ListHosts(kolide.ListOptions{}) require.Nil(t, err) @@ -249,18 +225,12 @@ func TestHostDetailQueries(t *testing.T) { } func TestLabelQueries(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - mockClock := clock.NewMockClock() - - svc, err := newTestServiceWithClock(ds, nil, mockClock) - assert.Nil(t, err) + ds, svc, mockClock := setupOsqueryTests(t) ctx := context.Background() - _, err = svc.EnrollAgent(ctx, "", "host123") - assert.Nil(t, err) + _, err := svc.EnrollAgent(ctx, "", "host123") + require.Nil(t, err) hosts, err := ds.ListHosts(kolide.ListOptions{}) require.Nil(t, err) @@ -467,14 +437,7 @@ func TestGetClientConfig(t *testing.T) { } func TestDetailQueries(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - mockClock := clock.NewMockClock() - - svc, err := newTestServiceWithClock(ds, nil, mockClock) - assert.Nil(t, err) - + ds, svc, mockClock := setupOsqueryTests(t) ctx := context.Background() nodeKey, err := svc.EnrollAgent(ctx, "", "host123") @@ -617,6 +580,9 @@ func TestDistributedQueries(t *testing.T) { ds, err := inmem.New(config.TestConfig()) require.Nil(t, err) + _, err = ds.NewAppConfig(&kolide.AppConfig{EnrollSecret: ""}) + require.Nil(t, err) + mockClock := clock.NewMockClock() rs := pubsub.NewInmemQueryResults() @@ -740,6 +706,9 @@ func TestOrphanedQueryCampaign(t *testing.T) { ds, err := inmem.New(config.TestConfig()) require.Nil(t, err) + _, err = ds.NewAppConfig(&kolide.AppConfig{EnrollSecret: ""}) + require.Nil(t, err) + rs := pubsub.NewInmemQueryResults() svc, err := newTestService(ds, rs) @@ -792,3 +761,17 @@ func TestOrphanedQueryCampaign(t *testing.T) { require.Nil(t, err) assert.Equal(t, kolide.QueryComplete, campaign.Status) } + +func setupOsqueryTests(t *testing.T) (kolide.Datastore, kolide.Service, *clock.MockClock) { + ds, err := inmem.New(config.TestConfig()) + require.Nil(t, err) + + _, err = ds.NewAppConfig(&kolide.AppConfig{EnrollSecret: ""}) + require.Nil(t, err) + + mockClock := clock.NewMockClock() + svc, err := newTestServiceWithClock(ds, nil, mockClock) + require.Nil(t, err) + + return ds, svc, mockClock +} diff --git a/tools/osquery/README.md b/tools/osquery/README.md index 27cef3c681..69f86c8755 100644 --- a/tools/osquery/README.md +++ b/tools/osquery/README.md @@ -25,6 +25,8 @@ Before using the following commands, set the environment variable `LOCALHOST` to set `KOLIDE_OSQUERY_VERSION` to either `1.8.2` or `latest` (currently 2.1.2) to indicate which version of osquery that you want to run on your containers. +You will also need to set the environment variable `ENROLL_SECRET` to the value of your kolide enroll secret(configured during the app setup or in the app config UI). + ### Running osqueryd The osqueryd instances are configured to use the TLS plugins at `$LOCALHOST:8080`. Using the `example_config.json` in this directory should configure Kolide with the appropriate settings for these `osqueryd` containers to connect. diff --git a/tools/osquery/docker-compose.yml b/tools/osquery/docker-compose.yml index 2ec0608bae..af9fa084c8 100644 --- a/tools/osquery/docker-compose.yml +++ b/tools/osquery/docker-compose.yml @@ -10,7 +10,7 @@ services: extra_hosts: - "dockerhost:${LOCALHOST}" environment: - ENROLL_SECRET: '' + ENROLL_SECRET: "${ENROLL_SECRET}" command: osqueryd --flagfile=/etc/osquery/osquery.flags ulimits: core: @@ -25,7 +25,7 @@ services: extra_hosts: - "dockerhost:${LOCALHOST}" environment: - ENROLL_SECRET: '' + ENROLL_SECRET: "${ENROLL_SECRET}" command: osqueryd --flagfile=/etc/osquery/osquery.flags ulimits: core: