Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auditbeat Processor to enrich auditd events with session view information #37640

Merged
merged 55 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
88d0a0e
Add a add_session_metadata auditbeat processor
mjwolf Jan 10, 2024
d5a01bf
Add unit tests for add_session_metadata
mjwolf Jan 12, 2024
7cec455
Calculate process entry leader
mjwolf Jan 15, 2024
7de480d
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Jan 15, 2024
782504c
Add entry leader tests
mjwolf Jan 16, 2024
6b7037b
Update CHANGELOG
mjwolf Jan 16, 2024
0f610cb
Merge branch 'main' into session_view_processor_ebpf
mjwolf Jan 17, 2024
c688f5a
Apply suggestions from code review
mjwolf Jan 17, 2024
f35b4c7
Rework directory structure
mjwolf Jan 17, 2024
bf38e89
Remove DB interface
mjwolf Jan 17, 2024
910aba2
Pass DB by reference in tests
mjwolf Jan 18, 2024
0cbb970
Rework entry leader tests
mjwolf Jan 18, 2024
06b7064
Reformat processor
mjwolf Jan 18, 2024
be57ad8
Only build tests on Linux
mjwolf Jan 18, 2024
9ad7811
Add linux build directive to all files in processor
mjwolf Jan 18, 2024
f6aad7e
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Jan 18, 2024
3598b3c
Changes for PR feedback
mjwolf Jan 22, 2024
743e8da
Merge branch 'elastic:main' into session_view_processor_ebpf
mjwolf Jan 22, 2024
17fdf1c
Add empty file to add_session_metadata package
mjwolf Jan 22, 2024
1ab4752
Fix linter warnings
mjwolf Jan 23, 2024
23e097c
Merge branch 'main' into session_view_processor_ebpf
mjwolf Jan 23, 2024
882f8a4
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Jan 24, 2024
145f627
Use single channel from epbevents
mjwolf Jan 24, 2024
e9aea4d
Use watcher for ebpf events
mjwolf Jan 25, 2024
c001219
remove seccomp init
mjwolf Jan 26, 2024
a5986dd
Update x-pack/auditbeat/internal/ebpf/watcher_linux.go
mjwolf Jan 26, 2024
5658c76
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Jan 27, 2024
d5da140
Add seccomp policy modification
mjwolf Jan 29, 2024
ca81839
Merge branch 'session_view_processor_ebpf' of github.com:mjwolf/beats…
mjwolf Jan 29, 2024
7fe0ba4
Use buffered channel in watcher client
mjwolf Jan 29, 2024
9c59a7b
Move ebpf watcher to libbeat
mjwolf Jan 30, 2024
26d759b
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Feb 1, 2024
4a304df
Remove error return value that was never used
mjwolf Feb 1, 2024
e7a45ea
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Feb 2, 2024
5dbe5dd
Make capNames an array
mjwolf Feb 2, 2024
d675c53
Merge branch 'main' into session_view_processor_ebpf
mjwolf Feb 5, 2024
1837289
Merge branch 'main' into session_view_processor_ebpf
mjwolf Feb 5, 2024
7017a79
Merge branch 'main' into session_view_processor_ebpf
mjwolf Feb 6, 2024
10e9525
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Feb 7, 2024
eeab397
Update ebpfevents lib
mjwolf Feb 7, 2024
7de070e
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Feb 20, 2024
565eaa1
Rename package
mjwolf Feb 20, 2024
12643f4
Merge branch 'elastic:main' into session_view_processor_ebpf
mjwolf Feb 20, 2024
1f05b14
Use consistant capitialization for initialisms
mjwolf Feb 20, 2024
0ecb7bf
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Feb 20, 2024
fe4f0a3
Merge branch 'session_view_processor_ebpf' of github.com:mjwolf/beats…
mjwolf Feb 20, 2024
f2443cd
Change some struct member visibility
mjwolf Feb 22, 2024
9a52b90
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Mar 11, 2024
36c8998
Remove possibilities of panics
mjwolf Mar 14, 2024
5f5f777
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Mar 14, 2024
51949ae
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Mar 28, 2024
52e2b60
Fix up more code
mjwolf Mar 28, 2024
7382b76
Fix lint error
mjwolf Mar 29, 2024
ba0f81a
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Mar 29, 2024
eb9792b
Merge remote-tracking branch 'origin/main' into session_view_processo…
mjwolf Apr 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff]

*Auditbeat*

- Added `add_session_metadata` processor, which enables session viewer on Auditbeat data. {pull}37640[37640]
- Add linux capabilities to processes in the system/process. {pull}37453[37453]
- Add opt-in eBPF backend for file_integrity module. {pull}37223[37223]
- Add linux capabilities to processes in the system/process. {pull}37453[37453]
- Add opt-in eBPF backend for file_integrity module. {pull}37223[37223]
- Add process data to file events (Linux only, eBPF backend). {pull}38199[38199]
Expand Down
3 changes: 3 additions & 0 deletions x-pack/auditbeat/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
// Register Auditbeat x-pack modules.
_ "github.com/elastic/beats/v7/x-pack/auditbeat/include"
_ "github.com/elastic/beats/v7/x-pack/libbeat/include"

// Import processors
_ "github.com/elastic/beats/v7/x-pack/auditbeat/processors/sessionmd"
)

// Name of the beat
Expand Down
215 changes: 215 additions & 0 deletions x-pack/auditbeat/processors/sessionmd/add_session_metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

//go:build linux

package sessionmd

import (
"context"
"fmt"
"reflect"
"strconv"
"time"

"github.com/elastic/beats/v7/libbeat/beat"
"github.com/elastic/beats/v7/libbeat/processors"
"github.com/elastic/beats/v7/x-pack/auditbeat/processors/sessionmd/processdb"
"github.com/elastic/beats/v7/x-pack/auditbeat/processors/sessionmd/procfs"
"github.com/elastic/beats/v7/x-pack/auditbeat/processors/sessionmd/provider"
"github.com/elastic/beats/v7/x-pack/auditbeat/processors/sessionmd/provider/ebpf_provider"
cfg "github.com/elastic/elastic-agent-libs/config"
"github.com/elastic/elastic-agent-libs/logp"
"github.com/elastic/elastic-agent-libs/mapstr"
)

const (
processorName = "add_session_metadata"
logName = "processor." + processorName
)

func init() {
processors.RegisterPlugin(processorName, New)
}

type addSessionMetadata struct {
config config
logger *logp.Logger
db *processdb.DB
provider provider.Provider
}

func New(cfg *cfg.C) (beat.Processor, error) {
c := defaultConfig()
if err := cfg.Unpack(&c); err != nil {
return nil, fmt.Errorf("fail to unpack the %v configuration: %w", processorName, err)
}

logger := logp.NewLogger(logName)

ctx := context.Background()
reader := procfs.NewProcfsReader(*logger)
db, err := processdb.NewDB(reader, *logger)
if err != nil {
return nil, fmt.Errorf("failed to create DB: %w", err)
}

backfilledPIDs := db.ScrapeProcfs()
logger.Debugf("backfilled %d processes", len(backfilledPIDs))

switch c.Backend {
case "auto":
// "auto" always uses ebpf, as it's currently the only backend
fallthrough
case "ebpf":
p, err := ebpf_provider.NewProvider(ctx, logger, db)
if err != nil {
return nil, fmt.Errorf("failed to create ebpf provider: %w", err)
}
return &addSessionMetadata{
config: c,
logger: logger,
db: db,
provider: p,
}, nil
default:
return nil, fmt.Errorf("unknown backend configuration")
}
}

func (p *addSessionMetadata) Run(ev *beat.Event) (*beat.Event, error) {
_, err := ev.GetValue(p.config.PIDField)
if err != nil {
// Do not attempt to enrich events without PID; it's not a supported event
return ev, nil //nolint:nilerr // Running on events without PID is expected
}

err = p.provider.UpdateDB(ev)
if err != nil {
return ev, err
}

result, err := p.enrich(ev)
if err != nil {
return ev, fmt.Errorf("enriching event: %w", err)
}
return result, nil
}

func (p *addSessionMetadata) String() string {
return fmt.Sprintf("%v=[backend=%s, pid_field=%s, replace_fields=%t]",
processorName, p.config.Backend, p.config.PIDField, p.config.ReplaceFields)
}

func (p *addSessionMetadata) enrich(ev *beat.Event) (*beat.Event, error) {
pidIf, err := ev.GetValue(p.config.PIDField)
if err != nil {
return nil, err
}
pid, err := pidToUInt32(pidIf)
if err != nil {
return nil, fmt.Errorf("cannot parse pid field '%s': %w", p.config.PIDField, err)
}

fullProcess, err := p.db.GetProcess(pid)
if err != nil {
return nil, fmt.Errorf("pid %v not found in db: %w", pid, err)
}

processMap := fullProcess.ToMap()

if b, err := ev.Fields.HasKey("process"); !b || err != nil {
return nil, fmt.Errorf("no process field in event")
}
m, ok := tryToMapStr(ev.Fields["process"])
if !ok {
return nil, fmt.Errorf("process field type not supported")
}

result := ev.Clone()
err = mapstr.MergeFieldsDeep(m, processMap, true)
if err != nil {
return nil, fmt.Errorf("merging enriched fields with event: %w", err)
}
result.Fields["process"] = m

if p.config.ReplaceFields {
if err := p.replaceFields(result); err != nil {
return nil, fmt.Errorf("replace fields: %w", err)
}
}
return result, nil
}

// pidToUInt32 converts PID value to uint32
func pidToUInt32(value interface{}) (pid uint32, err error) {
switch v := value.(type) {
case string:
nr, err := strconv.Atoi(v)
if err != nil {
return 0, fmt.Errorf("error converting string to integer: %w", err)
}
pid = uint32(nr)
case uint32:
pid = v
case int, int8, int16, int32, int64:
pid64 := reflect.ValueOf(v).Int()
if pid = uint32(pid64); int64(pid) != pid64 {
return 0, fmt.Errorf("integer out of range: %d", pid64)
}
case uint, uintptr, uint8, uint16, uint64:
pidu64 := reflect.ValueOf(v).Uint()
if pid = uint32(pidu64); uint64(pid) != pidu64 {
return 0, fmt.Errorf("integer out of range: %d", pidu64)
}
default:
return 0, fmt.Errorf("not an integer or string, but %T", v)
}
return pid, nil
}

// replaceFields replaces event fields with values suitable user with the session viewer in Kibana
// The current version of session view in Kibana expects different values than what are used by auditbeat
// for some fields. This function converts these field to have values that will work with session view.
//
// This function is temporary, and can be removed when this Kibana issue is completed: https://github.com/elastic/kibana/issues/179396.
func (p *addSessionMetadata) replaceFields(ev *beat.Event) error {
kind, err := ev.Fields.GetValue("event.kind")
if err != nil {
return err
}
isAuditdEvent, err := ev.Fields.HasKey("auditd")
if err != nil {
return err
}
if kind == "event" && isAuditdEvent {
// process start
syscall, err := ev.Fields.GetValue("auditd.data.syscall")
if err != nil {
return nil //nolint:nilerr // processor can be called on unsupported events; not an error
}
switch syscall {
case "execveat", "execve":
ev.Fields.Put("event.action", []string{"exec", "fork"})
ev.Fields.Put("event.type", []string{"start"})

case "exit_group":
ev.Fields.Put("event.action", []string{"end"})
ev.Fields.Put("event.type", []string{"end"})
ev.Fields.Put("process.end", time.Now())
}
}
return nil
}

func tryToMapStr(v interface{}) (mapstr.M, bool) {
switch m := v.(type) {
case mapstr.M:
return m, true
case map[string]interface{}:
return mapstr.M(m), true
default:
return nil, false
}
}
Loading
Loading