diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 9c5603c93b8..c9b46c8cc12 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -112,6 +112,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 fceb186e2c7..f2fc5cf6279 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -12257,11 +12257,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..9b4f972055a 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,23 @@ 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 err != nil { + event.errors = append(event.errors, err) } if event.Action == Deleted { @@ -158,6 +174,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 e9a1a79dd8f..45d196c5474 100644 --- a/go.mod +++ b/go.mod @@ -200,7 +200,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.7 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 73542c3b469..ef717aec4c3 100644 --- a/go.sum +++ b/go.sum @@ -663,8 +663,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.7 h1:+KVjltN0rPsBrU8b156gV4lOTBgG/vt0efFCFARrf3g= github.com/elastic/elastic-agent-autodiscover v0.6.7/go.mod h1:hFeFqneS2r4jD0/QzGkrNk0YVdN0JGh7lCWdsH7zcI4= github.com/elastic/elastic-agent-client/v7 v7.8.1 h1:J9wZc/0mUvSEok0X5iR5+n60Jgb+AWooKddb3XgPWqM=