Skip to content

Commit 483d320

Browse files
marc-grmergify[bot]
authored andcommitted
Osquerybeat: emit pack_name and query_name in scheduled query results (#51781)
Scheduled pack queries (both osquery native interval schedules and osquerybeat RRULE schedules) now include pack_name and query_name in their result and response documents. pack_name comes from a new optional `pack_name` config field on a pack (alongside the existing pack_id); query_name comes from the pack's queries config map key. Both fields are only emitted when set. These fields are required by the Osquery Manager dashboards, which group and label scheduled query results by pack and query name. (cherry picked from commit 5d82c71) # Conflicts: # x-pack/osquerybeat/beater/config_plugin.go # x-pack/osquerybeat/beater/recurrence_query_handler.go # x-pack/osquerybeat/internal/config/osquery.go
1 parent 18f3756 commit 483d320

11 files changed

Lines changed: 641 additions & 17 deletions
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: bug-fix
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Emit pack_name and query_name in osquerybeat scheduled query results
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
description: |
20+
Scheduled pack queries (both osquery native interval schedules and osquerybeat
21+
RRULE schedules) now include two additional fields in their result and response
22+
documents: pack_name, taken from the new optional `pack_name` config field on a
23+
pack (alongside the existing pack_id), and query_name, taken from the pack's
24+
queries config map key. Both fields are only emitted when set. These fields are
25+
required by the Osquery Manager dashboards, which group and label scheduled
26+
query results by pack and query name; without them the dashboards cannot render
27+
those results correctly.
28+
29+
# Affected component; a word indicating the component this changeset affects.
30+
component: osquerybeat
31+
32+
# PR URL; optional; the PR number that added the changeset.
33+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
34+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
35+
# Please provide it if you are adding a fragment for a different PR.
36+
#pr: https://github.com/owner/repo/1234
37+
38+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
39+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
40+
#issue: https://github.com/owner/repo/1234

x-pack/osquerybeat/beater/action_handler.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ type actionResultPublisher interface {
2626
}
2727

2828
type queryResultPublisher interface {
29-
Publish(index, idValue, idFieldKey, responseID, spaceID, packID string, meta map[string]interface{}, hits []map[string]interface{}, ecsm ecs.Mapping, reqData interface{})
29+
Publish(index, idValue, idFieldKey, responseID, spaceID, packID, packName, queryName string, meta map[string]interface{}, hits []map[string]interface{}, ecsm ecs.Mapping, reqData interface{})
3030
}
3131

3232
type scheduledResponsePublisher interface {
33-
PublishScheduledResponse(scheduleID, packID, spaceID, responseID string, startedAt, completedAt, plannedScheduleTime time.Time, resultCount int, scheduleExecutionCount int64)
33+
PublishScheduledResponse(scheduleID, packID, packName, queryName, spaceID, responseID string, startedAt, completedAt, plannedScheduleTime time.Time, resultCount int, scheduleExecutionCount int64)
3434
}
3535

3636
type queryProfilePublisher interface {
@@ -172,7 +172,7 @@ func (a *actionHandler) executeQuery(ctx context.Context, index string, ac actio
172172

173173
a.log.Debugf("Completed query in: %v", duration)
174174

175-
a.publisher.Publish(index, ac.ID, "action_id", responseID, "", "", nil, hits, ac.ECSMapping, req["data"])
175+
a.publisher.Publish(index, ac.ID, "action_id", responseID, "", "", "", "", nil, hits, ac.ECSMapping, req["data"])
176176

177177
return len(hits), nil
178178
}

x-pack/osquerybeat/beater/action_handler_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,26 @@ type mockPublisher struct {
3636
idValue string
3737
idFieldKey string
3838
responseID string
39+
spaceID string
40+
packID string
41+
packName string
42+
queryName string
3943
meta map[string]interface{}
4044
hits []map[string]interface{}
4145
ecsm ecs.Mapping
4246
reqData interface{}
4347
profile map[string]interface{}
4448
}
4549

46-
func (p *mockPublisher) Publish(index, idValue, idFieldKey, responseID, spaceID, packID string, meta map[string]interface{}, hits []map[string]interface{}, ecsm ecs.Mapping, reqData interface{}) {
50+
func (p *mockPublisher) Publish(index, idValue, idFieldKey, responseID, spaceID, packID, packName, queryName string, meta map[string]interface{}, hits []map[string]interface{}, ecsm ecs.Mapping, reqData interface{}) {
4751
p.index = index
4852
p.idValue = idValue
4953
p.idFieldKey = idFieldKey
5054
p.responseID = responseID
55+
p.spaceID = spaceID
56+
p.packID = packID
57+
p.packName = packName
58+
p.queryName = queryName
5159
p.meta = meta
5260
p.hits = hits
5361
p.ecsm = ecsm

x-pack/osquerybeat/beater/config_plugin.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,18 @@ type QueryInfo struct {
4343
// Interval is the schedule interval in seconds for native schedules; used to compute schedule_execution_count
4444
Interval int
4545
// PackID is the policy-defined pack identifier for pack queries; empty for top-level schedule queries.
46+
<<<<<<< HEAD
4647
PackID string
4748
Profile bool // whether to collect and publish profile for this query
49+
=======
50+
PackID string
51+
// PackName is the policy-defined human-readable pack name for pack queries; empty for top-level schedule queries.
52+
PackName string
53+
// QueryName is the query's config map key (pack query name or top-level schedule name).
54+
QueryName string
55+
// Profile is whether to collect and publish profile for this query.
56+
Profile bool
57+
>>>>>>> 5d82c7117 (Osquerybeat: emit pack_name and query_name in scheduled query results (#51781))
4858
}
4959

5060
type queryInfoMap map[string]QueryInfo
@@ -234,8 +244,10 @@ func (p *ConfigPlugin) set(inputs []config.InputConfig) (err error) {
234244
osqueryConfig = inputs[0].Osquery
235245
}
236246

237-
// Common code to register query with lookup maps, enforce snapshot and increment queries count
238-
registerQuery := func(name, ns string, qi config.Query, packID string) (config.Query, error) {
247+
// Common code to register query with lookup maps, enforce snapshot and increment queries count.
248+
// queryName is the config map key (pack query name or top-level schedule name); packID/packName
249+
// are the pack identifiers for pack queries and empty for top-level schedule queries.
250+
registerQuery := func(name, ns string, qi config.Query, packID, packName, queryName string) (config.Query, error) {
239251
var ecsm ecs.Mapping
240252
ecsm, err = flattenECSMapping(qi.ECSMapping)
241253
if err != nil {
@@ -250,7 +262,13 @@ func (p *ConfigPlugin) set(inputs []config.InputConfig) (err error) {
250262
SpaceID: qi.SpaceID,
251263
Interval: qi.Interval,
252264
PackID: packID,
265+
<<<<<<< HEAD
253266
Profile: qi.Profile,
267+
=======
268+
PackName: packName,
269+
QueryName: queryName,
270+
Profile: config.ResolveProfiling(globalProfile, qi.Profiling),
271+
>>>>>>> 5d82c7117 (Osquerybeat: emit pack_name and query_name in scheduled query results (#51781))
254272
}
255273
namespaces[name] = ns
256274
queriesCount++
@@ -265,7 +283,14 @@ func (p *ConfigPlugin) set(inputs []config.InputConfig) (err error) {
265283

266284
// Iterate osquery configuration's scheduled queries, add flattened ECS mappings to lookup map
267285
for name, qi := range osqueryConfig.Schedule {
286+
<<<<<<< HEAD
268287
qi, err = registerQuery(name, p.namespace, qi, "")
288+
=======
289+
if err := config.ValidateQueryScheduleMode(qi); err != nil {
290+
return fmt.Errorf("osquery.schedule[%q]: %w", name, err)
291+
}
292+
qi, err = registerQuery(name, p.namespace, qi, "", "", name)
293+
>>>>>>> 5d82c7117 (Osquerybeat: emit pack_name and query_name in scheduled query results (#51781))
269294
if err != nil {
270295
return err
271296
}
@@ -279,7 +304,15 @@ func (p *ConfigPlugin) set(inputs []config.InputConfig) (err error) {
279304
packID = packName
280305
}
281306
for name, qi := range pack.Queries {
307+
<<<<<<< HEAD
282308
qi, err = registerQuery(getPackQueryName(packName, name), p.namespace, qi, packID)
309+
=======
310+
qi, err = config.MergeQueryWithPackScheduleDefaults(pack, qi)
311+
if err != nil {
312+
return fmt.Errorf("osquery.packs[%q].queries[%q]: %w", packName, name, err)
313+
}
314+
qi, err = registerQuery(getPackQueryName(packName, name), p.namespace, qi, packID, pack.PackName, name)
315+
>>>>>>> 5d82c7117 (Osquerybeat: emit pack_name and query_name in scheduled query results (#51781))
283316
if err != nil {
284317
return err
285318
}
@@ -305,7 +338,7 @@ func (p *ConfigPlugin) set(inputs []config.InputConfig) (err error) {
305338
Profile: stream.Profile,
306339
}
307340

308-
qi, err = registerQuery(getPackQueryName(input.Name, stream.ID), p.namespace, qi, input.Name)
341+
qi, err = registerQuery(getPackQueryName(input.Name, stream.ID), p.namespace, qi, input.Name, "", stream.ID)
309342
if err != nil {
310343
return err
311344
}

x-pack/osquerybeat/beater/config_plugin_test.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,14 @@ func TestSet_ScheduleMetadataIncludesSpaceID(t *testing.T) {
531531
if diff := cmp.Diff(queryPeriod, qi.Interval); diff != "" {
532532
t.Error(diff)
533533
}
534+
// query_name for a top-level scheduled query is the schedule config map key.
535+
if diff := cmp.Diff(queryName, qi.QueryName); diff != "" {
536+
t.Error(diff)
537+
}
538+
// Top-level schedule queries do not belong to a pack.
539+
if diff := cmp.Diff("", qi.PackName); diff != "" {
540+
t.Error(diff)
541+
}
534542
}
535543

536544
func TestSet_ScheduleMetadataIncludesPackID(t *testing.T) {
@@ -540,6 +548,7 @@ func TestSet_ScheduleMetadataIncludesPackID(t *testing.T) {
540548
const (
541549
packName = "my-pack"
542550
packID = "pack-uuid-123"
551+
packLabel = "My Pack"
543552
queryName = "uptime_query"
544553
querySQL = "select * from uptime"
545554
queryPeriod = 60
@@ -555,7 +564,8 @@ func TestSet_ScheduleMetadataIncludesPackID(t *testing.T) {
555564
Osquery: &config.OsqueryConfig{
556565
Packs: map[string]config.Pack{
557566
packName: {
558-
PackID: packID,
567+
PackID: packID,
568+
PackName: packLabel,
559569
Queries: map[string]config.Query{
560570
queryName: {
561571
Query: querySQL,
@@ -586,4 +596,11 @@ func TestSet_ScheduleMetadataIncludesPackID(t *testing.T) {
586596
if diff := cmp.Diff(packID, qi.PackID); diff != "" {
587597
t.Error(diff)
588598
}
599+
if diff := cmp.Diff(packLabel, qi.PackName); diff != "" {
600+
t.Error(diff)
601+
}
602+
// query_name is taken from the pack's queries config map key.
603+
if diff := cmp.Diff(queryName, qi.QueryName); diff != "" {
604+
t.Error(diff)
605+
}
589606
}

x-pack/osquerybeat/beater/osquerybeat.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ func (bt *osquerybeat) handleQueryResult(ctx context.Context, cli *osqdcli.Clien
592592
publishResolved := func(resultType, action string, hits []map[string]interface{}) {
593593
totalHits += len(hits)
594594
meta := queryResultMeta(resultType, action, res, scheduleExecutionCount, plannedScheduleTime)
595-
bt.pub.Publish(config.Datastream(ns), scheduleID, "schedule_id", responseID, qi.SpaceID, qi.PackID, meta, hits, qi.ECSMapping, nil)
595+
bt.pub.Publish(config.Datastream(ns), scheduleID, "schedule_id", responseID, qi.SpaceID, qi.PackID, qi.PackName, qi.QueryName, meta, hits, qi.ECSMapping, nil)
596596
}
597597

598598
if res.Action == "snapshot" {
@@ -630,7 +630,7 @@ func (bt *osquerybeat) handleQueryResult(ctx context.Context, cli *osqdcli.Clien
630630
}
631631
}
632632

633-
bt.pub.PublishScheduledResponse(scheduleID, qi.PackID, qi.SpaceID, responseID, runTime, runTime, plannedScheduleTime, totalHits, scheduleExecutionCount)
633+
bt.pub.PublishScheduledResponse(scheduleID, qi.PackID, qi.PackName, qi.QueryName, qi.SpaceID, responseID, runTime, runTime, plannedScheduleTime, totalHits, scheduleExecutionCount)
634634
}
635635

636636
func queryResultMeta(typ, action string, res QueryResult, scheduleExecutionCount int64, plannedScheduleTime time.Time) map[string]interface{} {

x-pack/osquerybeat/beater/osquerybeat_publisher_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ type mockBeatPublisher struct {
2121
var _ osquerybeatPublisher = (*mockBeatPublisher)(nil)
2222
var _ scheduledQueryPublisher = (*mockBeatPublisher)(nil)
2323

24-
func (m *mockBeatPublisher) Publish(index, idValue, idFieldKey, responseID, spaceID, packID string, meta map[string]interface{}, hits []map[string]interface{}, ecsm ecs.Mapping, reqData interface{}) {
24+
func (m *mockBeatPublisher) Publish(index, idValue, idFieldKey, responseID, spaceID, packID, packName, queryName string, meta map[string]interface{}, hits []map[string]interface{}, ecsm ecs.Mapping, reqData interface{}) {
2525
}
2626

2727
func (m *mockBeatPublisher) PublishActionResult(req map[string]interface{}, res map[string]interface{}) {
2828
}
2929

30-
func (m *mockBeatPublisher) PublishScheduledResponse(scheduleID, packID, spaceID, responseID string, startedAt, completedAt, plannedScheduleTime time.Time, resultCount int, scheduleExecutionCount int64) {
30+
func (m *mockBeatPublisher) PublishScheduledResponse(scheduleID, packID, packName, queryName, spaceID, responseID string, startedAt, completedAt, plannedScheduleTime time.Time, resultCount int, scheduleExecutionCount int64) {
3131
}
3232

3333
func (m *mockBeatPublisher) PublishQueryProfile(index, queryName, actionID, responseID string, profile map[string]interface{}, reqData interface{}) {

0 commit comments

Comments
 (0)