Skip to content

Commit

Permalink
fim(ebpf): enrich file events with process data
Browse files Browse the repository at this point in the history
  • Loading branch information
mmat11 committed Mar 6, 2024
1 parent ae312c5 commit 36c6ecd
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 66 deletions.
49 changes: 1 addition & 48 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Setting environmental variable ELASTIC_NETINFO:false in Elastic Agent pod will d

- 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]

*Filebeat*

Expand Down Expand Up @@ -285,51 +286,3 @@ Setting environmental variable ELASTIC_NETINFO:false in Elastic Agent pod will d


==== Known Issues
















































70 changes: 70 additions & 0 deletions auditbeat/docs/fields.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -18578,6 +18578,76 @@ type: keyword
--
[float]
=== process
These fields contains process and user information. Available only on Linux when using the eBPF backend.
*`process.entity_id`*::
+
--
Globally unique identifier for a process.
type: keyword
--
*`process.executable`*::
+
--
Process command.
type: keyword
--
*`process.pid`*::
+
--
PID.
type: integer
--
*`process.user.id`*::
+
--
User ID (euid).
type: integer
--
*`process.user.name`*::
+
--
User name.
type: keyword
--
*`process.group.id`*::
+
--
Group ID (egid).
type: integer
--
*`process.group.name`*::
+
--
Group name.
type: keyword
--
[[exported-fields-host-processor]]
== Host fields
Expand Down
35 changes: 35 additions & 0 deletions auditbeat/module/file_integrity/_meta/fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,38 @@
- name: xxh64
type: keyword
description: XX64 hash of the file.

- name: process
type: group
description: >
These fields contains process and user information.
Available only on Linux when using the eBPF backend.
fields:
- name: entity_id
type: keyword
description: Globally unique identifier for a process.

- name: executable
type: keyword
description: Process command.

- name: pid
type: integer
description: PID.

- name: user.id
type: integer
description: User ID (euid).

- name: user.name
type: keyword
description: User name.

- name: group.id
type: integer
description: Group ID (egid).

- name: group.name
type: keyword
description: Group name.
29 changes: 29 additions & 0 deletions auditbeat/module/file_integrity/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,42 @@ type Event struct {
Action Action `json:"action"` // Action (like created, updated).
Hashes map[HashType]Digest `json:"hash,omitempty"` // File hashes.
ParserResults mapstr.M `json:"file,omitempty"` // Results from running file parsers.
Process Process `json:"process,omitempty"` // Process data. Available only on Linux when using the eBPF backend.
// TODO(matt): ContainerID string `json:"container_id,omitempty"` // Unique container ID. Available only on Linux when using the eBPF backend.

// Metadata
rtt time.Duration // Time taken to collect the info.
errors []error // Errors that occurred while collecting the info.
hashFailed bool // Set when hashing the file failed.
}

// Process contain information about a process.
// These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation.
type Process struct {
// Unique identifier for the process.
// The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process.
// Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts.
EntityID string `json:"entity_id,omitempty"`
// Process name. Sometimes called program name or similar.
Name string `json:"name,omitempty"`
// The effective user (euid).
User struct {
// Unique identifier of the user.
ID string `json:"id,omitempty"`
// Short name or login of the user.
Name string `json:"name,omitempty"`
} `json:"user,omitempty"`
// The effective group (egid).
Group struct {
// Unique identifier for the group on the system/platform.
ID string `json:"id,omitempty"`
// Name of the group.
Name string `json:"name,omitempty"`
} `json:"group,omitempty"`
// Process id.
PID uint32 `json:"pid,omitempty"`
}

// Metadata contains file metadata.
type Metadata struct {
Inode uint64 `json:"inode"`
Expand Down
95 changes: 90 additions & 5 deletions auditbeat/module/file_integrity/event_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"strconv"
"time"

"github.com/elastic/beats/v7/libbeat/ebpf/sys"
"github.com/elastic/ebpfevents"
)

Expand All @@ -41,7 +42,9 @@ func NewEventFromEbpfEvent(
path, target string
action Action
metadata Metadata
process Process
err error
errors = make([]error, 0)
)
switch ee.Type {
case ebpfevents.EventTypeFileCreate:
Expand All @@ -54,7 +57,16 @@ func NewEventFromEbpfEvent(
return event, false
}
target = fileCreateEvent.SymlinkTargetPath

metadata, err = metadataFromFileCreate(fileCreateEvent)
if err != nil {
errors = append(errors, err)
}

process, err = processFromFileCreate(fileCreateEvent)
if err != nil {
errors = append(errors, err)
}
case ebpfevents.EventTypeFileRename:
action = Moved

Expand All @@ -65,7 +77,16 @@ func NewEventFromEbpfEvent(
return event, false
}
target = fileRenameEvent.SymlinkTargetPath

metadata, err = metadataFromFileRename(fileRenameEvent)
if err != nil {
errors = append(errors, err)
}

process, err = processFromFileRename(fileRenameEvent)
if err != nil {
errors = append(errors, err)
}
case ebpfevents.EventTypeFileDelete:
action = Deleted

Expand All @@ -76,6 +97,11 @@ func NewEventFromEbpfEvent(
return event, false
}
target = fileDeleteEvent.SymlinkTargetPath

process, err = processFromFileDelete(fileDeleteEvent)
if err != nil {
errors = append(errors, err)
}
case ebpfevents.EventTypeFileModify:
fileModifyEvent := ee.Body.(*ebpfevents.FileModify)

Expand All @@ -92,7 +118,16 @@ func NewEventFromEbpfEvent(
return event, false
}
target = fileModifyEvent.SymlinkTargetPath

metadata, err = metadataFromFileModify(fileModifyEvent)
if err != nil {
errors = append(errors, err)
}

process, err = processFromFileModify(fileModifyEvent)
if err != nil {
errors = append(errors, err)
}
}

event := Event{
Expand All @@ -102,10 +137,8 @@ func NewEventFromEbpfEvent(
Info: &metadata,
Source: SourceEBPF,
Action: action,
errors: make([]error, 0),
}
if err != nil {
event.errors = append(event.errors, err)
Process: process,
errors: errors,
}

if event.Action == Deleted {
Expand All @@ -115,7 +148,6 @@ func NewEventFromEbpfEvent(
case FileType:
fillHashes(&event, path, maxFileSize, hashTypes, fileParsers)
case SymlinkType:
var err error
event.TargetPath, err = filepath.EvalSymlinks(event.Path)
if err != nil {
event.errors = append(event.errors, err)
Expand Down Expand Up @@ -147,6 +179,59 @@ func metadataFromFileModify(evt *ebpfevents.FileModify) (Metadata, error) {
return md, err
}

func newProcess(pid uint32, start uint64, comm string, euid, egid uint32) (Process, error) {
var (
p Process
err error
)

t, err := sys.TimeFromNsSinceBoot(start)
if err != nil {
return p, err
}

p.EntityID, err = sys.EntityID(pid, t)
if err != nil {
return p, err
}
p.Name = comm
p.PID = pid

p.User.ID = strconv.FormatUint(uint64(euid), 10)
u, err := user.LookupId(p.User.ID)
if err == nil {
p.User.Name = u.Name
} else {
p.User.Name = "n/a"
}

p.Group.ID = strconv.FormatUint(uint64(egid), 10)
g, err := user.LookupId(p.Group.ID)
if err == nil {
p.Group.Name = g.Name
} else {
p.Group.Name = "n/a"
}

return p, err
}

func processFromFileCreate(evt *ebpfevents.FileCreate) (Process, error) {
return newProcess(evt.Pids.Tgid, evt.Pids.StartTimeNs, evt.Comm, evt.Creds.Euid, evt.Creds.Egid)
}

func processFromFileRename(evt *ebpfevents.FileRename) (Process, error) {
return newProcess(evt.Pids.Tgid, evt.Pids.StartTimeNs, evt.Comm, evt.Creds.Euid, evt.Creds.Egid)
}

func processFromFileModify(evt *ebpfevents.FileModify) (Process, error) {
return newProcess(evt.Pids.Tgid, evt.Pids.StartTimeNs, evt.Comm, evt.Creds.Euid, evt.Creds.Egid)
}

func processFromFileDelete(evt *ebpfevents.FileDelete) (Process, error) {
return newProcess(evt.Pids.Tgid, evt.Pids.StartTimeNs, evt.Comm, evt.Creds.Euid, evt.Creds.Egid)
}

func fillFileInfo(md *Metadata, finfo ebpfevents.FileInfo) error {
md.Inode = finfo.Inode
md.UID = finfo.Uid
Expand Down
2 changes: 1 addition & 1 deletion auditbeat/module/file_integrity/fields.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 36c6ecd

Please sign in to comment.