From 5b0b68240dcf277c728885a793fb84bb33a58eb6 Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Mon, 8 Apr 2024 19:40:22 +0200 Subject: [PATCH] [Auditbeat] fim(ebpf): enrich file events with container id (#38328) * fim(ebpf): enrich file events with container id * fix(fim/ebpf): make container id event field ecs-compliant --------- Co-authored-by: Panos Koutsovasilis --- CHANGELOG.next.asciidoc | 1 + NOTICE.txt | 4 +- auditbeat/module/file_integrity/event.go | 23 +++++---- .../module/file_integrity/event_linux.go | 49 +++++++++++++------ .../module/file_integrity/event_linux_test.go | 9 ++-- go.mod | 2 +- go.sum | 4 +- 7 files changed, 61 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 39b96748ce4..377c3c9f133 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -149,6 +149,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - 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] +- Add container id to file events (Linux only, eBPF backend). {pull}38328[38328] *Filebeat* diff --git a/NOTICE.txt b/NOTICE.txt index f846edfc9ec..97385dbd48d 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -12287,11 +12287,11 @@ SOFTWARE. -------------------------------------------------------------------------------- Dependency : github.com/elastic/ebpfevents -Version: v0.5.0 +Version: v0.6.0 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/elastic/ebpfevents@v0.5.0/LICENSE.txt: +Contents of probable licence file $GOMODCACHE/github.com/elastic/ebpfevents@v0.6.0/LICENSE.txt: The https://github.com/elastic/ebpfevents repository contains source code under various licenses: diff --git a/auditbeat/module/file_integrity/event.go b/auditbeat/module/file_integrity/event.go index b282aaaf3d2..63463acbe0d 100644 --- a/auditbeat/module/file_integrity/event.go +++ b/auditbeat/module/file_integrity/event.go @@ -126,15 +126,16 @@ func (d Digest) MarshalText() ([]byte, error) { return []byte(d.String()), nil } // Event describes the filesystem change and includes metadata about the file. type Event struct { - Timestamp time.Time `json:"timestamp"` // Time of event. - Path string `json:"path"` // The path associated with the event. - TargetPath string `json:"target_path,omitempty"` // Target path for symlinks. - Info *Metadata `json:"info"` // File metadata (if the file exists). - Source Source `json:"source"` // Source of the event. - 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. + Timestamp time.Time `json:"timestamp"` // Time of event. + Path string `json:"path"` // The path associated with the event. + TargetPath string `json:"target_path,omitempty"` // Target path for symlinks. + Info *Metadata `json:"info"` // File metadata (if the file exists). + Source Source `json:"source"` // Source of the event. + 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. + 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. @@ -400,6 +401,10 @@ func buildMetricbeatEvent(e *Event, existedBefore bool) mb.Event { out.MetricSetFields.Put("process", process) } + if e.ContainerID != "" { + out.MetricSetFields.Put("container.id", e.ContainerID) + } + if len(e.Hashes) > 0 { hashes := make(mapstr.M, len(e.Hashes)) for hashType, digest := range e.Hashes { diff --git a/auditbeat/module/file_integrity/event_linux.go b/auditbeat/module/file_integrity/event_linux.go index c0eb2d57b15..3f849e359b1 100644 --- a/auditbeat/module/file_integrity/event_linux.go +++ b/auditbeat/module/file_integrity/event_linux.go @@ -23,6 +23,7 @@ import ( "os" "os/user" "path/filepath" + "regexp" "strconv" "time" @@ -30,6 +31,9 @@ import ( "github.com/elastic/ebpfevents" ) +// cgroupRegex captures 64-character lowercase hexadecimal container IDs found in cgroup paths. +var cgroupRegex = regexp.MustCompile(`[-/]([0-9a-f]{64})(\.scope)?$`) + // NewEventFromEbpfEvent creates a new Event from an ebpfevents.Event. func NewEventFromEbpfEvent( ee ebpfevents.Event, @@ -39,12 +43,12 @@ func NewEventFromEbpfEvent( isExcludedPath func(string) bool, ) (Event, bool) { var ( - path, target string - action Action - metadata Metadata - process Process - err error - errors []error + path, target, cgroupPath string + action Action + metadata Metadata + process Process + err error + errors []error ) switch ee.Type { case ebpfevents.EventTypeFileCreate: @@ -67,6 +71,8 @@ func NewEventFromEbpfEvent( if err != nil { errors = append(errors, err) } + + cgroupPath = fileCreateEvent.CgroupPath case ebpfevents.EventTypeFileRename: action = Moved @@ -87,6 +93,8 @@ func NewEventFromEbpfEvent( if err != nil { errors = append(errors, err) } + + cgroupPath = fileRenameEvent.CgroupPath case ebpfevents.EventTypeFileDelete: action = Deleted @@ -102,6 +110,8 @@ func NewEventFromEbpfEvent( if err != nil { errors = append(errors, err) } + + cgroupPath = fileDeleteEvent.CgroupPath case ebpfevents.EventTypeFileModify: fileModifyEvent := ee.Body.(*ebpfevents.FileModify) @@ -128,17 +138,20 @@ func NewEventFromEbpfEvent( if err != nil { errors = append(errors, err) } + + cgroupPath = fileModifyEvent.CgroupPath } event := Event{ - Timestamp: time.Now().UTC(), - Path: path, - TargetPath: target, - Info: &metadata, - Source: SourceEBPF, - Action: action, - Process: &process, - errors: errors, + Timestamp: time.Now().UTC(), + Path: path, + TargetPath: target, + Info: &metadata, + Source: SourceEBPF, + Action: action, + Process: &process, + ContainerID: containerIDFromCgroupPath(cgroupPath), + errors: errors, } if event.Action == Deleted { @@ -158,6 +171,14 @@ func NewEventFromEbpfEvent( return event, true } +func containerIDFromCgroupPath(path string) string { + matches := cgroupRegex.FindStringSubmatch(path) + if len(matches) > 1 { + return matches[1] + } + return "" +} + func metadataFromFileCreate(evt *ebpfevents.FileCreate) (Metadata, error) { var md Metadata fillExtendedAttributes(&md, evt.Path) diff --git a/auditbeat/module/file_integrity/event_linux_test.go b/auditbeat/module/file_integrity/event_linux_test.go index beac9878909..fec2e6f70a6 100644 --- a/auditbeat/module/file_integrity/event_linux_test.go +++ b/auditbeat/module/file_integrity/event_linux_test.go @@ -29,6 +29,7 @@ import ( ) func TestNewEventFromEbpfEvent(t *testing.T) { + containerID := "d12fe576354a1805165303a4e34a69e5fe8db791ceb7e545f17811d1fbfba68f" ebpfEvent := ebpfevents.Event{ Header: ebpfevents.Header{ Type: ebpfevents.EventTypeFileCreate, @@ -52,6 +53,7 @@ func TestNewEventFromEbpfEvent(t *testing.T) { Suid: 5, Sgid: 6, }, + CgroupPath: "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod123.slice/cri-containerd-" + containerID + ".scope", }, } event, ok := NewEventFromEbpfEvent( @@ -72,9 +74,10 @@ func TestNewEventFromEbpfEvent(t *testing.T) { Group: event.Info.Group, Mode: os.FileMode(0o644), }, - Process: event.Process, // 1:1 copy this as it changes on every machine - Source: SourceEBPF, - errors: nil, + Process: event.Process, // 1:1 copy this as it changes on every machine + ContainerID: containerID, + Source: SourceEBPF, + errors: nil, } event.Timestamp = expectedEvent.Timestamp diff --git a/go.mod b/go.mod index 76f6f27c3ba..8d635c7fc96 100644 --- a/go.mod +++ b/go.mod @@ -201,7 +201,7 @@ require ( github.com/aws/smithy-go v1.13.5 github.com/awslabs/kinesis-aggregation/go/v2 v2.0.0-20220623125934-28468a6701b5 github.com/elastic/bayeux v1.0.5 - github.com/elastic/ebpfevents v0.5.0 + github.com/elastic/ebpfevents v0.6.0 github.com/elastic/elastic-agent-autodiscover v0.6.8 github.com/elastic/elastic-agent-libs v0.7.5 github.com/elastic/elastic-agent-shipper-client v0.5.1-0.20230228231646-f04347b666f3 diff --git a/go.sum b/go.sum index 7900b675836..e16a2e8839b 100644 --- a/go.sum +++ b/go.sum @@ -672,8 +672,8 @@ github.com/elastic/bayeux v1.0.5 h1:UceFq01ipmT3S8DzFK+uVAkbCdiPR0Bqei8qIGmUeY0= github.com/elastic/bayeux v1.0.5/go.mod h1:CSI4iP7qeo5MMlkznGvYKftp8M7qqP/3nzmVZoXHY68= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3 h1:lnDkqiRFKm0rxdljqrj3lotWinO9+jFmeDXIC4gvIQs= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3/go.mod h1:aPqzac6AYkipvp4hufTyMj5PDIphF3+At8zr7r51xjY= -github.com/elastic/ebpfevents v0.5.0 h1:QkyMAYWo3fXFbYtXAXU8sZu2SQ4LXVYC6gLXIWXy02E= -github.com/elastic/ebpfevents v0.5.0/go.mod h1:ESG9gw7N+n5yCCMgdg1IIJENKWSmX7+X0Fi9GUs9nvU= +github.com/elastic/ebpfevents v0.6.0 h1:BrL3m7JFK7U6h2jkbk3xAWWs//IZnugCHEDds5u2v68= +github.com/elastic/ebpfevents v0.6.0/go.mod h1:ESG9gw7N+n5yCCMgdg1IIJENKWSmX7+X0Fi9GUs9nvU= github.com/elastic/elastic-agent-autodiscover v0.6.8 h1:BSXz+QwjZAEt08G+T3GDGl14Bh9a6zD8luNCvZut/b8= github.com/elastic/elastic-agent-autodiscover v0.6.8/go.mod h1:hFeFqneS2r4jD0/QzGkrNk0YVdN0JGh7lCWdsH7zcI4= github.com/elastic/elastic-agent-client/v7 v7.8.1 h1:J9wZc/0mUvSEok0X5iR5+n60Jgb+AWooKddb3XgPWqM=