diff --git a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx
index 349d8f9f62..1d62936bf9 100644
--- a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx
+++ b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx
@@ -27,6 +27,7 @@ const formFields = [
'org_logo_url', 'org_name', 'osquery_enroll_secret', 'password', 'port', 'sender_address',
'server', 'user_name', 'verify_ssl_certs', 'idp_name', 'entity_id', 'issuer_uri', 'idp_image_url',
'metadata', 'metadata_url', 'enable_sso', 'enable_smtp', 'host_expiry_enabled', 'host_expiry_window',
+ 'live_query_disabled',
];
const Header = ({ showAdvancedOptions }) => {
const CaratIcon = ;
@@ -63,6 +64,7 @@ class AppConfigForm extends Component {
enable_smtp: formFieldInterface.isRequired,
host_expiry_enabled: formFieldInterface.isRequired,
host_expiry_window: formFieldInterface.isRequired,
+ live_query_disabled: formFieldInterface.isRequired,
}).isRequired,
handleSubmit: PropTypes.func.isRequired,
smtpConfigured: PropTypes.bool.isRequired,
@@ -111,6 +113,7 @@ class AppConfigForm extends Component {
+
@@ -120,6 +123,7 @@ class AppConfigForm extends Component {
Enable STARTTLS - Detects if STARTTLS is enabled in your SMTP server and starts to use it. (Default: On)
Host Expiry - When enabled, allows automatic cleanup of hosts that have not communicated with Fleet in some number of days. (Default: Off)
Host Expiry Window - If a host has not communicated with Fleet in the specified number of days, it will be removed.
+ Disable Live Queries - When enabled, disables the ability to run live queries (ad hoc queries executed via the UI or fleetctl). (Default: Off)
);
diff --git a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx
index 298e345e29..2e5bce61cd 100644
--- a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx
+++ b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx
@@ -86,7 +86,7 @@ describe('AppConfigForm - form', () => {
it('renders advanced options when "Advanced Options" is clicked', () => {
expect(form.find({ name: 'domain' }).hostNodes().length).toEqual(1);
- expect(form.find('Slider').length).toEqual(3);
+ expect(form.find('Slider').length).toEqual(4);
});
it('disables host expiry window by default', () => {
@@ -102,5 +102,10 @@ describe('AppConfigForm - form', () => {
const inputElement = InputField.find('input');
expect(inputElement.hasClass('input-field--disabled')).toBe(false);
});
+
+ it('renders live query disabled input', () => {
+ form.find({ name: 'live_query_disabled' });
+ expect(form.length).toEqual(1);
+ });
});
});
diff --git a/frontend/interfaces/config.js b/frontend/interfaces/config.js
index 669393b9d1..90f5ce853b 100644
--- a/frontend/interfaces/config.js
+++ b/frontend/interfaces/config.js
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
export default PropTypes.shape({
+ live_query_disabled: PropTypes.bool,
authentication_method: PropTypes.string,
authentication_type: PropTypes.string,
configured: PropTypes.bool,
diff --git a/frontend/kolide/endpoints.js b/frontend/kolide/endpoints.js
index 0d3f4f57f3..69ab871f6f 100644
--- a/frontend/kolide/endpoints.js
+++ b/frontend/kolide/endpoints.js
@@ -34,5 +34,6 @@ export default {
return `/v1/kolide/users/${id}/admin`;
},
SSO: '/v1/kolide/sso',
+ STATUS_LIVE_QUERY: '/v1/kolide/status/live_query',
STATUS_RESULT_STORE: '/v1/kolide/status/result_store',
};
diff --git a/frontend/kolide/helpers.js b/frontend/kolide/helpers.js
index 07d37dcb03..fcc575f372 100644
--- a/frontend/kolide/helpers.js
+++ b/frontend/kolide/helpers.js
@@ -74,7 +74,7 @@ const filterTarget = (targetType) => {
export const formatConfigDataForServer = (config) => {
const orgInfoAttrs = pick(config, ['org_logo_url', 'org_name']);
- const serverSettingsAttrs = pick(config, ['kolide_server_url', 'osquery_enroll_secret']);
+ const serverSettingsAttrs = pick(config, ['kolide_server_url', 'osquery_enroll_secret', 'live_query_disabled']);
const smtpSettingsAttrs = pick(config, [
'authentication_method', 'authentication_type', 'domain', 'enable_ssl_tls',
'enable_start_tls', 'password', 'port', 'sender_address', 'server', 'user_name', 'verify_ssl_certs',
diff --git a/frontend/kolide/helpers.tests.js b/frontend/kolide/helpers.tests.js
index bc29d800d4..5aae3b753c 100644
--- a/frontend/kolide/helpers.tests.js
+++ b/frontend/kolide/helpers.tests.js
@@ -38,6 +38,7 @@ describe('Kolide API - helpers', () => {
enable_start_tls: true,
host_expiry_enabled: false,
host_expiry_window: 0,
+ live_query_disabled: false,
};
it('splits config into categories for the server', () => {
diff --git a/frontend/kolide/status.js b/frontend/kolide/status.js
index 50fe25c872..2ffd4dbefd 100644
--- a/frontend/kolide/status.js
+++ b/frontend/kolide/status.js
@@ -6,6 +6,12 @@ export default (client) => {
const { STATUS_RESULT_STORE } = endpoints;
const endpoint = client.baseURL + STATUS_RESULT_STORE;
+ return client.authenticatedGet(endpoint);
+ },
+ live_query: () => {
+ const { STATUS_LIVE_QUERY } = endpoints;
+ const endpoint = client.baseURL + STATUS_LIVE_QUERY;
+
return client.authenticatedGet(endpoint);
},
};
diff --git a/frontend/pages/queries/QueryPage/QueryPage.jsx b/frontend/pages/queries/QueryPage/QueryPage.jsx
index e9579cfd6c..458f28a67d 100644
--- a/frontend/pages/queries/QueryPage/QueryPage.jsx
+++ b/frontend/pages/queries/QueryPage/QueryPage.jsx
@@ -93,8 +93,8 @@ export class QueryPage extends Component {
dispatch(hostActions.loadAll());
}
- Kolide.status.result_store().catch((response) => {
- this.setState({ resultStoreError: response.message.errors[0].reason });
+ Kolide.status.live_query().catch((response) => {
+ this.setState({ liveQueryError: response.message.errors[0].reason });
});
helpers.selectHosts(dispatch, {
@@ -451,14 +451,14 @@ export class QueryPage extends Component {
return false;
}
- renderResultStoreWarning = () => {
- const { resultStoreError } = this.state;
+ renderLiveQueryWarning = () => {
+ const { liveQueryError } = this.state;
- if (!resultStoreError) {
+ if (!liveQueryError) {
return false;
}
- const message = `Live query disabled due to Redis error: ${resultStoreError}`;
+ const message = `Live query disabled due to error: ${liveQueryError}`;
return (
@@ -505,7 +505,7 @@ export class QueryPage extends Component {
renderTargetsInput = () => {
const { onFetchTargets, onRunQuery, onStopQuery, onTargetSelect } = this;
- const { campaign, queryIsRunning, targetsCount, targetsError, runQueryMilliseconds, resultStoreError } = this.state;
+ const { campaign, queryIsRunning, targetsCount, targetsError, runQueryMilliseconds, liveQueryError } = this.state;
const { selectedTargets } = this.props;
return (
@@ -520,7 +520,7 @@ export class QueryPage extends Component {
selectedTargets={selectedTargets}
targetsCount={targetsCount}
queryTimerMilliseconds={runQueryMilliseconds}
- disableRun={resultStoreError !== undefined}
+ disableRun={liveQueryError !== undefined}
/>
);
}
@@ -536,7 +536,7 @@ export class QueryPage extends Component {
onUpdateQuery,
renderResultsTable,
renderTargetsInput,
- renderResultStoreWarning,
+ renderLiveQueryWarning,
} = this;
const { queryIsRunning } = this.state;
const {
@@ -569,7 +569,7 @@ export class QueryPage extends Component {
title={title}
/>
- {renderResultStoreWarning()}
+ {renderLiveQueryWarning()}
{renderTargetsInput()}
{renderResultsTable()}
diff --git a/frontend/test/stubs.js b/frontend/test/stubs.js
index 5c90248cf3..9bc03bb303 100644
--- a/frontend/test/stubs.js
+++ b/frontend/test/stubs.js
@@ -13,6 +13,7 @@ export const configStub = {
},
server_settings: {
kolide_server_url: '',
+ live_query_disabled: false,
},
smtp_settings: {
configured: false,
@@ -52,6 +53,7 @@ export const flatConfigStub = {
enable_start_tls: true,
host_expiry_enabled: false,
host_expiry_window: 0,
+ live_query_disabled: false,
};
export const hostStub = {
diff --git a/server/datastore/mysql/app_configs.go b/server/datastore/mysql/app_configs.go
index 0b9e082b22..7d06b2c5ce 100644
--- a/server/datastore/mysql/app_configs.go
+++ b/server/datastore/mysql/app_configs.go
@@ -117,9 +117,10 @@ func (d *Datastore) SaveAppConfig(info *kolide.AppConfig) error {
fim_interval,
fim_file_accesses,
host_expiry_enabled,
- host_expiry_window
+ host_expiry_window,
+ live_query_disabled
)
- VALUES( 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
+ VALUES( 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
ON DUPLICATE KEY UPDATE
org_name = VALUES(org_name),
org_logo_url = VALUES(org_logo_url),
@@ -147,7 +148,8 @@ func (d *Datastore) SaveAppConfig(info *kolide.AppConfig) error {
fim_interval = VALUES(fim_interval),
fim_file_accesses = VALUES(fim_file_accesses),
host_expiry_enabled = VALUES(host_expiry_enabled),
- host_expiry_window = VALUES(host_expiry_window)
+ host_expiry_window = VALUES(host_expiry_window),
+ live_query_disabled = VALUES(live_query_disabled)
`
_, err = d.db.Exec(insertStatement,
@@ -178,6 +180,7 @@ func (d *Datastore) SaveAppConfig(info *kolide.AppConfig) error {
info.FIMFileAccesses,
info.HostExpiryEnabled,
info.HostExpiryWindow,
+ info.LiveQueryDisabled,
)
return err
diff --git a/server/datastore/mysql/migrations/tables/20191220130734_AddLiveQueryDisabledToAppConfig.go b/server/datastore/mysql/migrations/tables/20191220130734_AddLiveQueryDisabledToAppConfig.go
new file mode 100644
index 0000000000..77bc7bc2a7
--- /dev/null
+++ b/server/datastore/mysql/migrations/tables/20191220130734_AddLiveQueryDisabledToAppConfig.go
@@ -0,0 +1,25 @@
+package tables
+
+import (
+ "database/sql"
+)
+
+func init() {
+ MigrationClient.AddMigration(Up20191220130734, Down20191220130734)
+}
+
+func Up20191220130734(tx *sql.Tx) error {
+ _, err := tx.Exec(
+ "ALTER TABLE `app_configs` " +
+ "ADD COLUMN `live_query_disabled` TINYINT(1) NOT NULL DEFAULT FALSE;",
+ )
+ return err
+}
+
+func Down20191220130734(tx *sql.Tx) error {
+ _, err := tx.Exec(
+ "ALTER TABLE `app_configs` " +
+ "DROP COLUMN `live_query_disabled`;",
+ )
+ return err
+}
diff --git a/server/kolide/app.go b/server/kolide/app.go
index 1e9c24cf8d..8531afc9a2 100644
--- a/server/kolide/app.go
+++ b/server/kolide/app.go
@@ -140,6 +140,9 @@ type AppConfig struct {
HostExpiryEnabled bool `db:"host_expiry_enabled"`
// HostExpiryWindow defines a number in days after which a host will be removed if it has not communicated with Fleet.
HostExpiryWindow int `db:"host_expiry_window"`
+
+ // LiveQueryDisabled defines whether live queries are disabled.
+ LiveQueryDisabled bool `db:"live_query_disabled"`
}
// ModifyAppConfigRequest contains application configuration information
@@ -228,8 +231,9 @@ type OrgInfo struct {
// ServerSettings contains general settings about the kolide App.
type ServerSettings struct {
- KolideServerURL *string `json:"kolide_server_url,omitempty"`
- EnrollSecret *string `json:"osquery_enroll_secret,omitempty"`
+ KolideServerURL *string `json:"kolide_server_url,omitempty"`
+ EnrollSecret *string `json:"osquery_enroll_secret,omitempty"`
+ LiveQueryDisabled *bool `json:"live_query_disabled,omitempty"`
}
// HostExpirySettings contains settings pertaining to automatic host expiry.
diff --git a/server/kolide/status.go b/server/kolide/status.go
index 97f4d3a850..5445771e58 100644
--- a/server/kolide/status.go
+++ b/server/kolide/status.go
@@ -6,4 +6,8 @@ type StatusService interface {
// StatusResultStore returns nil if the result store is functioning
// correctly, or an error indicating the problem.
StatusResultStore(ctx context.Context) error
+
+ // StatusLiveQuery returns nil if live queries are enabled, or an
+ // error indicating the problem.
+ StatusLiveQuery(ctx context.Context) error
}
diff --git a/server/mock/datastore.go b/server/mock/datastore.go
index ce95d1d9f9..09a7e5fbdb 100644
--- a/server/mock/datastore.go
+++ b/server/mock/datastore.go
@@ -11,6 +11,7 @@ package mock
//go:generate mockimpl -o datastore_osquery_options.go "s *OsqueryOptionsStore" "kolide.OsqueryOptionsStore"
//go:generate mockimpl -o datastore_scheduled_queries.go "s *ScheduledQueryStore" "kolide.ScheduledQueryStore"
//go:generate mockimpl -o datastore_queries.go "s *QueryStore" "kolide.QueryStore"
+//go:generate mockimpl -o datastore_query_results.go "s *QueryResultStore" "kolide.QueryResultStore"
//go:generate mockimpl -o datastore_campaigns.go "s *CampaignStore" "kolide.CampaignStore"
//go:generate mockimpl -o datastore_sessions.go "s *SessionStore" "kolide.SessionStore"
@@ -35,6 +36,7 @@ type Store struct {
PackStore
UserStore
QueryStore
+ QueryResultStore
}
func (m *Store) Drop() error {
diff --git a/server/mock/datastore_query_results.go b/server/mock/datastore_query_results.go
new file mode 100644
index 0000000000..66dc2ffeae
--- /dev/null
+++ b/server/mock/datastore_query_results.go
@@ -0,0 +1,43 @@
+// Automatically generated by mockimpl. DO NOT EDIT!
+
+package mock
+
+import (
+ "context"
+
+ "github.com/kolide/fleet/server/kolide"
+)
+
+var _ kolide.QueryResultStore = (*QueryResultStore)(nil)
+
+type WriteResultFunc func(result kolide.DistributedQueryResult) error
+
+type ReadChannelFunc func(ctx context.Context, query kolide.DistributedQueryCampaign) (<-chan interface{}, error)
+
+type HealthCheckFunc func() error
+
+type QueryResultStore struct {
+ WriteResultFunc WriteResultFunc
+ WriteResultFuncInvoked bool
+
+ ReadChannelFunc ReadChannelFunc
+ ReadChannelFuncInvoked bool
+
+ HealthCheckFunc HealthCheckFunc
+ HealthCheckFuncInvoked bool
+}
+
+func (s *QueryResultStore) WriteResult(result kolide.DistributedQueryResult) error {
+ s.WriteResultFuncInvoked = true
+ return s.WriteResultFunc(result)
+}
+
+func (s *QueryResultStore) ReadChannel(ctx context.Context, query kolide.DistributedQueryCampaign) (<-chan interface{}, error) {
+ s.ReadChannelFuncInvoked = true
+ return s.ReadChannelFunc(ctx, query)
+}
+
+func (s *QueryResultStore) HealthCheck() error {
+ s.HealthCheckFuncInvoked = true
+ return s.HealthCheckFunc()
+}
diff --git a/server/pubsub/redis_query_results.go b/server/pubsub/redis_query_results.go
index 4bfa3af453..40d23a01d5 100644
--- a/server/pubsub/redis_query_results.go
+++ b/server/pubsub/redis_query_results.go
@@ -160,6 +160,8 @@ func (r *redisQueryResults) HealthCheck() error {
conn := r.pool.Get()
defer conn.Close()
- _, err := conn.Do("PING")
- return err
+ if _, err := conn.Do("PING"); err != nil {
+ return errors.Wrap(err, "reading from redis")
+ }
+ return nil
}
diff --git a/server/service/endpoint_appconfig.go b/server/service/endpoint_appconfig.go
index 937634d4e8..acd9968631 100644
--- a/server/service/endpoint_appconfig.go
+++ b/server/service/endpoint_appconfig.go
@@ -63,8 +63,9 @@ func makeGetAppConfigEndpoint(svc kolide.Service) endpoint.Endpoint {
OrgLogoURL: &config.OrgLogoURL,
},
ServerSettings: &kolide.ServerSettings{
- KolideServerURL: &config.KolideServerURL,
- EnrollSecret: &config.EnrollSecret,
+ KolideServerURL: &config.KolideServerURL,
+ EnrollSecret: &config.EnrollSecret,
+ LiveQueryDisabled: &config.LiveQueryDisabled,
},
SMTPSettings: smtpSettings,
SSOSettings: ssoSettings,
@@ -87,8 +88,9 @@ func makeModifyAppConfigEndpoint(svc kolide.Service) endpoint.Endpoint {
OrgLogoURL: &config.OrgLogoURL,
},
ServerSettings: &kolide.ServerSettings{
- KolideServerURL: &config.KolideServerURL,
- EnrollSecret: &config.EnrollSecret,
+ KolideServerURL: &config.KolideServerURL,
+ EnrollSecret: &config.EnrollSecret,
+ LiveQueryDisabled: &config.LiveQueryDisabled,
},
SMTPSettings: smtpSettingsFromAppConfig(config),
SSOSettings: &kolide.SSOSettingsPayload{
diff --git a/server/service/endpoint_appconfig_test.go b/server/service/endpoint_appconfig_test.go
index b25459cdf6..7cfa254973 100644
--- a/server/service/endpoint_appconfig_test.go
+++ b/server/service/endpoint_appconfig_test.go
@@ -41,6 +41,7 @@ func testGetAppConfig(t *testing.T, r *testResource) {
assert.Equal(t, "http://foo.bar/image.png", *configInfo.OrgInfo.OrgLogoURL)
assert.False(t, *configInfo.HostExpirySettings.HostExpiryEnabled)
assert.Equal(t, 0, *configInfo.HostExpirySettings.HostExpiryWindow)
+ assert.False(t, *configInfo.ServerSettings.LiveQueryDisabled)
}
@@ -62,6 +63,7 @@ func testModifyAppConfig(t *testing.T, r *testResource) {
EntityID: "kolide",
HostExpiryEnabled: true,
HostExpiryWindow: 42,
+ LiveQueryDisabled: true,
}
payload := appConfigPayloadFromAppConfig(config)
payload.SMTPTest = new(bool)
@@ -95,6 +97,8 @@ func testModifyAppConfig(t *testing.T, r *testResource) {
// verify that host expiry settings were saved
assert.True(t, saved.HostExpiryEnabled)
assert.Equal(t, 42, saved.HostExpiryWindow)
+ //verify that live query disabled setting was saved
+ assert.True(t, saved.LiveQueryDisabled)
}
@@ -131,7 +135,8 @@ func appConfigPayloadFromAppConfig(config *kolide.AppConfig) *kolide.AppConfigPa
OrgName: &config.OrgName,
},
ServerSettings: &kolide.ServerSettings{
- KolideServerURL: &config.KolideServerURL,
+ KolideServerURL: &config.KolideServerURL,
+ LiveQueryDisabled: &config.LiveQueryDisabled,
},
SMTPSettings: smtpSettingsFromAppConfig(config),
SSOSettings: &kolide.SSOSettingsPayload{
diff --git a/server/service/endpoint_status.go b/server/service/endpoint_status.go
index d13ec148c3..01f9500548 100644
--- a/server/service/endpoint_status.go
+++ b/server/service/endpoint_status.go
@@ -7,15 +7,25 @@ import (
"github.com/kolide/fleet/server/kolide"
)
-type statusResultStoreResponse struct {
+type statusResponse struct {
Err error `json:"error,omitempty"`
}
-func (m statusResultStoreResponse) error() error { return m.Err }
+func (m statusResponse) error() error { return m.Err }
+
+func makeStatusLiveQueryEndpoint(svc kolide.Service) endpoint.Endpoint {
+ return func(ctx context.Context, req interface{}) (interface{}, error) {
+ var resp statusResponse
+ if err := svc.StatusLiveQuery(ctx); err != nil {
+ resp.Err = err
+ }
+ return resp, nil
+ }
+}
func makeStatusResultStoreEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
- var resp statusResultStoreResponse
+ var resp statusResponse
if err := svc.StatusResultStore(ctx); err != nil {
resp.Err = err
}
diff --git a/server/service/handler.go b/server/service/handler.go
index fdded503b5..3876475f00 100644
--- a/server/service/handler.go
+++ b/server/service/handler.go
@@ -98,6 +98,7 @@ type KolideEndpoints struct {
GetFIM endpoint.Endpoint
ModifyFIM endpoint.Endpoint
StatusResultStore endpoint.Endpoint
+ StatusLiveQuery endpoint.Endpoint
}
// MakeKolideServerEndpoints creates the Kolide API endpoints.
@@ -192,6 +193,7 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey, urlPrefix string) Kol
// Authenticated status endpoints
StatusResultStore: authenticatedUser(jwtKey, svc, makeStatusResultStoreEndpoint(svc)),
+ StatusLiveQuery: authenticatedUser(jwtKey, svc, makeStatusLiveQueryEndpoint(svc)),
// Osquery endpoints
EnrollAgent: makeEnrollAgentEndpoint(svc),
@@ -285,6 +287,7 @@ type kolideHandlers struct {
ModifyFIM http.Handler
GetFIM http.Handler
StatusResultStore http.Handler
+ StatusLiveQuery http.Handler
}
func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *kolideHandlers {
@@ -374,6 +377,7 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli
ModifyFIM: newServer(e.ModifyFIM, decodeModifyFIMRequest),
GetFIM: newServer(e.GetFIM, decodeNoParamsRequest),
StatusResultStore: newServer(e.StatusResultStore, decodeNoParamsRequest),
+ StatusLiveQuery: newServer(e.StatusLiveQuery, decodeNoParamsRequest),
}
}
@@ -504,6 +508,7 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) {
r.Handle("/api/v1/kolide/targets", h.SearchTargets).Methods("POST").Name("search_targets")
r.Handle("/api/v1/kolide/status/result_store", h.StatusResultStore).Methods("GET").Name("status_result_store")
+ r.Handle("/api/v1/kolide/status/live_query", h.StatusLiveQuery).Methods("GET").Name("status_live_query")
r.Handle("/api/v1/osquery/enroll", h.EnrollAgent).Methods("POST").Name("enroll_agent")
r.Handle("/api/v1/osquery/config", h.GetClientConfig).Methods("POST").Name("get_client_config")
diff --git a/server/service/service_appconfig.go b/server/service/service_appconfig.go
index 830d5ee817..9ddb0018a9 100644
--- a/server/service/service_appconfig.go
+++ b/server/service/service_appconfig.go
@@ -120,6 +120,9 @@ func appConfigFromAppConfigPayload(p kolide.AppConfigPayload, config kolide.AppC
if p.ServerSettings != nil && p.ServerSettings.EnrollSecret != nil {
config.EnrollSecret = *p.ServerSettings.EnrollSecret
}
+ if p.ServerSettings != nil && p.ServerSettings.LiveQueryDisabled != nil {
+ config.LiveQueryDisabled = *p.ServerSettings.LiveQueryDisabled
+ }
if p.SSOSettings != nil {
if p.SSOSettings.EnableSSO != nil {
diff --git a/server/service/service_appconfig_test.go b/server/service/service_appconfig_test.go
index ca00ae5f0a..8ec9d78608 100644
--- a/server/service/service_appconfig_test.go
+++ b/server/service/service_appconfig_test.go
@@ -48,7 +48,8 @@ func TestCreateAppConfig(t *testing.T) {
OrgName: stringPtr("Acme"),
},
ServerSettings: &kolide.ServerSettings{
- KolideServerURL: stringPtr("https://acme.co:8080/"),
+ KolideServerURL: stringPtr("https://acme.co:8080/"),
+ LiveQueryDisabled: boolPtr(true),
},
},
},
@@ -63,5 +64,6 @@ func TestCreateAppConfig(t *testing.T) {
assert.Equal(t, *payload.OrgInfo.OrgLogoURL, result.OrgLogoURL)
assert.Equal(t, *payload.OrgInfo.OrgName, result.OrgName)
assert.Equal(t, "https://acme.co:8080", result.KolideServerURL)
+ assert.Equal(t, *payload.ServerSettings.LiveQueryDisabled, result.LiveQueryDisabled)
}
}
diff --git a/server/service/service_campaigns.go b/server/service/service_campaigns.go
index ce363891f7..23cd7c31d1 100644
--- a/server/service/service_campaigns.go
+++ b/server/service/service_campaigns.go
@@ -30,6 +30,10 @@ func uintPtr(n uint) *uint {
}
func (svc service) NewDistributedQueryCampaign(ctx context.Context, queryString string, hosts []uint, labels []uint) (*kolide.DistributedQueryCampaign, error) {
+ if err := svc.StatusLiveQuery(ctx); err != nil {
+ return nil, err
+ }
+
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, errNoContext
@@ -57,7 +61,7 @@ func (svc service) NewDistributedQueryCampaign(ctx context.Context, queryString
// Add host targets
for _, hid := range hosts {
_, err = svc.ds.NewDistributedQueryCampaignTarget(&kolide.DistributedQueryCampaignTarget{
- Type: kolide.TargetHost,
+ Type: kolide.TargetHost,
DistributedQueryCampaignID: campaign.ID,
TargetID: hid,
})
@@ -69,7 +73,7 @@ func (svc service) NewDistributedQueryCampaign(ctx context.Context, queryString
// Add label targets
for _, lid := range labels {
_, err = svc.ds.NewDistributedQueryCampaignTarget(&kolide.DistributedQueryCampaignTarget{
- Type: kolide.TargetLabel,
+ Type: kolide.TargetLabel,
DistributedQueryCampaignID: campaign.ID,
TargetID: lid,
})
diff --git a/server/service/service_osquery_test.go b/server/service/service_osquery_test.go
index 175d894e72..146f32a51b 100644
--- a/server/service/service_osquery_test.go
+++ b/server/service/service_osquery_test.go
@@ -816,9 +816,21 @@ func TestDetailQueries(t *testing.T) {
}
func TestNewDistributedQueryCampaign(t *testing.T) {
+ ds := &mock.Store{
+ AppConfigStore: mock.AppConfigStore{
+ AppConfigFunc: func() (*kolide.AppConfig, error) {
+ config := &kolide.AppConfig{}
+ return config, nil
+ },
+ },
+ }
+ rs := &mock.QueryResultStore{
+ HealthCheckFunc: func() error {
+ return nil
+ },
+ }
mockClock := clock.NewMockClock()
- ds := new(mock.Store)
- svc, err := newTestServiceWithClock(ds, nil, mockClock)
+ svc, err := newTestServiceWithClock(ds, rs, mockClock)
require.Nil(t, err)
ds.LabelQueriesForHostFunc = func(host *kolide.Host, cutoff time.Time) (map[string]string, error) {
diff --git a/server/service/service_status.go b/server/service/service_status.go
index e278e22c81..1beef9adee 100644
--- a/server/service/service_status.go
+++ b/server/service/service_status.go
@@ -1,7 +1,24 @@
package service
-import "context"
+import (
+ "context"
+
+ "github.com/pkg/errors"
+)
func (svc service) StatusResultStore(ctx context.Context) error {
return svc.resultStore.HealthCheck()
}
+
+func (svc service) StatusLiveQuery(ctx context.Context) error {
+ cfg, err := svc.AppConfig(ctx)
+ if err != nil {
+ return errors.Wrap(err, "retreiving app config")
+ }
+
+ if cfg.LiveQueryDisabled {
+ return errors.New("disabled by administrator")
+ }
+
+ return svc.StatusResultStore(ctx)
+}