prevent live queries to stall if a detail query override was set for a team (#14296)

alternative approach for #14286
This commit is contained in:
Roberto Dip
2023-10-04 17:02:55 -03:00
committed by GitHub
parent 9c7079f50c
commit a31e433776
7 changed files with 124 additions and 4 deletions
+1
View File
@@ -0,0 +1 @@
* Fixed a bug that would cause live queries to stall if a detail query override was set for a team.
+1
View File
@@ -79,6 +79,7 @@ func (s *integrationTestSuite) TestSlowOsqueryHost() {
SkipCreateTestUsers: true,
//nolint:gosec // G112: server is just run for testing this explicit config.
HTTPServerConfig: &http.Server{ReadTimeout: 2 * time.Second},
EnableCachedDS: true,
},
)
defer func() {
+93 -3
View File
@@ -54,9 +54,10 @@ func (s *integrationEnterpriseTestSuite) SetupSuite() {
License: &fleet.LicenseInfo{
Tier: fleet.TierPremium,
},
Pool: s.redisPool,
Lq: s.lq,
Logger: log.NewLogfmtLogger(os.Stdout),
Pool: s.redisPool,
Lq: s.lq,
Logger: log.NewLogfmtLogger(os.Stdout),
EnableCachedDS: true,
}
users, server := RunServerForTestsWithDS(s.T(), s.ds, &config)
s.server = server
@@ -4106,3 +4107,92 @@ func (s *integrationEnterpriseTestSuite) TestOrbitConfigExtensions() {
}
}`), http.StatusBadRequest)
}
func (s *integrationEnterpriseTestSuite) TestTeamConfigDetailQueriesOverrides() {
ctx := context.Background()
t := s.T()
teamName := t.Name() + "team1"
team := &fleet.Team{
Name: teamName,
Description: "desc team1",
}
s.Do("POST", "/api/latest/fleet/teams", team, http.StatusOK)
spec := []byte(fmt.Sprintf(`
name: %s
features:
additional_queries:
time: SELECT * FROM time
enable_host_users: true
detail_query_overrides:
users: null
software_linux: "select * from blah;"
disk_encryption_linux: null
`, teamName))
s.applyTeamSpec(spec)
team, err := s.ds.TeamByName(ctx, teamName)
require.NoError(t, err)
require.NotNil(t, team.Config.Features.DetailQueryOverrides)
require.Nil(t, team.Config.Features.DetailQueryOverrides["users"])
require.Nil(t, team.Config.Features.DetailQueryOverrides["disk_encryption_linux"])
require.NotNil(t, team.Config.Features.DetailQueryOverrides["software_linux"])
require.Equal(t, "select * from blah;", *team.Config.Features.DetailQueryOverrides["software_linux"])
// create a linux host
linuxHost, err := s.ds.NewHost(context.Background(), &fleet.Host{
DetailUpdatedAt: time.Now().Add(-10 * time.Hour),
LabelUpdatedAt: time.Now().Add(-10 * time.Hour),
PolicyUpdatedAt: time.Now().Add(-10 * time.Hour),
SeenTime: time.Now().Add(-1 * time.Minute),
OsqueryHostID: ptr.String(t.Name()),
NodeKey: ptr.String(t.Name()),
UUID: uuid.New().String(),
Hostname: fmt.Sprintf("%sfoo.local", t.Name()),
Platform: "linux",
})
require.NoError(t, err)
// add the host to team1
err = s.ds.AddHostsToTeam(context.Background(), &team.ID, []uint{linuxHost.ID})
require.NoError(t, err)
// get distributed queries for the host
s.lq.On("QueriesForHost", linuxHost.ID).Return(map[string]string{fmt.Sprintf("%d", linuxHost.ID): "select 1 from osquery;"}, nil)
req := getDistributedQueriesRequest{NodeKey: *linuxHost.NodeKey}
var dqResp getDistributedQueriesResponse
s.DoJSON("POST", "/api/osquery/distributed/read", req, http.StatusOK, &dqResp)
require.NotContains(t, dqResp.Queries, "fleet_detail_query_users")
require.NotContains(t, dqResp.Queries, "fleet_detail_query_disk_encryption_linux")
require.Contains(t, dqResp.Queries, "fleet_detail_query_software_linux")
require.Contains(t, dqResp.Queries, "fleet_distributed_query_17")
spec = []byte(fmt.Sprintf(`
name: %s
features:
additional_queries:
time: SELECT * FROM time
enable_host_users: true
detail_query_overrides:
software_linux: "select * from blah;"
`, teamName))
s.applyTeamSpec(spec)
team, err = s.ds.TeamByName(ctx, teamName)
require.NoError(t, err)
require.NotNil(t, team.Config.Features.DetailQueryOverrides)
require.Nil(t, team.Config.Features.DetailQueryOverrides["users"])
require.Nil(t, team.Config.Features.DetailQueryOverrides["disk_encryption_linux"])
require.NotNil(t, team.Config.Features.DetailQueryOverrides["software_linux"])
require.Equal(t, "select * from blah;", *team.Config.Features.DetailQueryOverrides["software_linux"])
// get distributed queries for the host
req = getDistributedQueriesRequest{NodeKey: *linuxHost.NodeKey}
dqResp = getDistributedQueriesResponse{}
s.DoJSON("POST", "/api/osquery/distributed/read", req, http.StatusOK, &dqResp)
require.Contains(t, dqResp.Queries, "fleet_detail_query_users")
require.Contains(t, dqResp.Queries, "fleet_detail_query_disk_encryption_linux")
require.Contains(t, dqResp.Queries, "fleet_detail_query_software_linux")
require.Contains(t, dqResp.Queries, "fleet_distributed_query_17")
}
+1 -1
View File
@@ -1617,7 +1617,7 @@ func GetDetailQueries(
unknownQueries = append(unknownQueries, name)
continue
}
if override == nil {
if override == nil || *override == "" {
delete(generatedMap, name)
} else {
query.Query = *override
@@ -873,6 +873,18 @@ func TestAppConfigReplaceQuery(t *testing.T) {
queries = GetDetailQueries(context.Background(), config.FleetConfig{}, nil, &fleet.Features{EnableHostUsers: true, DetailQueryOverrides: replacementMap})
_, exists := queries["users"]
assert.False(t, exists)
// put the query back again
replacementMap["users"] = ptr.String("select 1 from blah")
queries = GetDetailQueries(context.Background(), config.FleetConfig{}, nil, &fleet.Features{EnableHostUsers: true, DetailQueryOverrides: replacementMap})
assert.NotEqual(t, originalQuery, queries["users"].Query)
assert.Equal(t, "select 1 from blah", queries["users"].Query)
// empty strings are also ignored
replacementMap["users"] = ptr.String("")
queries = GetDetailQueries(context.Background(), config.FleetConfig{}, nil, &fleet.Features{EnableHostUsers: true, DetailQueryOverrides: replacementMap})
_, exists = queries["users"]
assert.False(t, exists)
}
func TestDirectIngestSoftware(t *testing.T) {
+11
View File
@@ -298,6 +298,17 @@ func (ts *withServer) getConfig() *appConfigResponse {
return responseBody
}
func (ts *withServer) applyTeamSpec(yamlSpec []byte) {
var teamSpec any
err := yaml.Unmarshal(yamlSpec, &teamSpec)
require.NoError(ts.s.T(), err)
specsReq := map[string]any{
"specs": []any{teamSpec},
}
ts.Do("POST", "/api/latest/fleet/spec/teams", specsReq, http.StatusOK)
}
func (ts *withServer) LoginSSOUser(username, password string) (fleet.Auth, string) {
t := ts.s.T()
auth, res := ts.loginSSOUser(username, password, "/api/v1/fleet/sso", http.StatusOK)
+5
View File
@@ -17,6 +17,7 @@ import (
eeservice "github.com/fleetdm/fleet/v4/ee/server/service"
"github.com/fleetdm/fleet/v4/server/config"
"github.com/fleetdm/fleet/v4/server/contexts/license"
"github.com/fleetdm/fleet/v4/server/datastore/cached_mysql"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/logging"
"github.com/fleetdm/fleet/v4/server/mail"
@@ -288,9 +289,13 @@ type TestServerOpts struct {
UseMailService bool
APNSTopic string
ProfileMatcher fleet.ProfileMatcher
EnableCachedDS bool
}
func RunServerForTestsWithDS(t *testing.T, ds fleet.Datastore, opts ...*TestServerOpts) (map[string]fleet.User, *httptest.Server) {
if len(opts) > 0 && opts[0].EnableCachedDS {
ds = cached_mysql.New(ds)
}
var rs fleet.QueryResultStore
if len(opts) > 0 && opts[0].Rs != nil {
rs = opts[0].Rs