From 36c6ecd8de37b35eee48d96f49d7d997a442692d Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Wed, 6 Mar 2024 15:47:37 +0100 Subject: [PATCH] fim(ebpf): enrich file events with process data --- CHANGELOG.next.asciidoc | 49 +------- auditbeat/docs/fields.asciidoc | 70 +++++++++++ .../module/file_integrity/_meta/fields.yml | 35 ++++++ auditbeat/module/file_integrity/event.go | 29 +++++ .../module/file_integrity/event_linux.go | 95 ++++++++++++++- auditbeat/module/file_integrity/fields.go | 2 +- auditbeat/tests/system/test_file_integrity.py | 6 + go.mod | 10 +- go.sum | 18 +-- libbeat/ebpf/sys/sys.go | 94 +++++++++++++++ libbeat/ebpf/sys/time.go | 109 ++++++++++++++++++ 11 files changed, 451 insertions(+), 66 deletions(-) create mode 100644 libbeat/ebpf/sys/sys.go create mode 100644 libbeat/ebpf/sys/time.go diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 2c2a762be6ca..d00e2c8ff217 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -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* @@ -285,51 +286,3 @@ Setting environmental variable ELASTIC_NETINFO:false in Elastic Agent pod will d ==== Known Issues - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/auditbeat/docs/fields.asciidoc b/auditbeat/docs/fields.asciidoc index 9eee5f008fc1..dc6e791da624 100644 --- a/auditbeat/docs/fields.asciidoc +++ b/auditbeat/docs/fields.asciidoc @@ -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 diff --git a/auditbeat/module/file_integrity/_meta/fields.yml b/auditbeat/module/file_integrity/_meta/fields.yml index d020c5db7770..80446e5f4489 100644 --- a/auditbeat/module/file_integrity/_meta/fields.yml +++ b/auditbeat/module/file_integrity/_meta/fields.yml @@ -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. diff --git a/auditbeat/module/file_integrity/event.go b/auditbeat/module/file_integrity/event.go index 22813a47f22e..c08516bc4f56 100644 --- a/auditbeat/module/file_integrity/event.go +++ b/auditbeat/module/file_integrity/event.go @@ -134,6 +134,8 @@ 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. @@ -141,6 +143,33 @@ type Event struct { 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"` diff --git a/auditbeat/module/file_integrity/event_linux.go b/auditbeat/module/file_integrity/event_linux.go index 7643d03a6b42..492c0aa81b3d 100644 --- a/auditbeat/module/file_integrity/event_linux.go +++ b/auditbeat/module/file_integrity/event_linux.go @@ -26,6 +26,7 @@ import ( "strconv" "time" + "github.com/elastic/beats/v7/libbeat/ebpf/sys" "github.com/elastic/ebpfevents" ) @@ -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: @@ -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 @@ -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 @@ -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) @@ -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{ @@ -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 { @@ -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) @@ -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 diff --git a/auditbeat/module/file_integrity/fields.go b/auditbeat/module/file_integrity/fields.go index 9e7b566facaa..0d94bc5922dc 100644 --- a/auditbeat/module/file_integrity/fields.go +++ b/auditbeat/module/file_integrity/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFileIntegrity returns asset data. // This is the base64 encoded zlib format compressed contents of module/file_integrity. func AssetFileIntegrity() string { - return "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" + return "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" } diff --git a/auditbeat/tests/system/test_file_integrity.py b/auditbeat/tests/system/test_file_integrity.py index e6b03306c3a2..6717b52673a0 100644 --- a/auditbeat/tests/system/test_file_integrity.py +++ b/auditbeat/tests/system/test_file_integrity.py @@ -172,6 +172,12 @@ def _test_non_recursive(self, backend): # assert file inside subdir is not reported assert self.log_contains(file3) is False + # assert process data + if backend == "ebpf": + # pick a random event + evt = objs[0] + print("grepme", evt) + @unittest.skipIf(os.getenv("CI") is not None and platform.system() == 'Darwin', 'Flaky test: https://github.com/elastic/beats/issues/24678') def test_non_recursive__fsnotify(self): diff --git a/go.mod b/go.mod index d087675031e3..275077e01634 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/elastic/beats/v7 -go 1.21 +go 1.21.0 + +toolchain go1.22.0 require ( cloud.google.com/go/bigquery v1.55.0 @@ -200,7 +202,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.4.0 + github.com/elastic/ebpfevents v0.5.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 @@ -223,6 +225,7 @@ require ( github.com/pkg/xattr v0.4.9 github.com/sergi/go-diff v1.3.1 github.com/shirou/gopsutil/v3 v3.22.10 + github.com/tklauser/go-sysconf v0.3.10 go.elastic.co/apm/module/apmelasticsearch/v2 v2.4.8 go.elastic.co/apm/module/apmhttp/v2 v2.4.8 go.elastic.co/apm/v2 v2.4.8 @@ -270,7 +273,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cilium/ebpf v0.12.3 // indirect + github.com/cilium/ebpf v0.13.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect @@ -359,7 +362,6 @@ require ( github.com/sirupsen/logrus v1.9.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stretchr/objx v0.5.0 // indirect - github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect github.com/urso/diag v0.0.0-20200210123136-21b3cc8eb797 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect diff --git a/go.sum b/go.sum index 746d5023ae99..dedaf3822233 100644 --- a/go.sum +++ b/go.sum @@ -434,8 +434,8 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= -github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= +github.com/cilium/ebpf v0.13.2 h1:uhLimLX+jF9BTPPvoCUYh/mBeoONkjgaJ9w9fn0mRj4= +github.com/cilium/ebpf v0.13.2/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= @@ -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.4.0 h1:M80eAeJnzvGQgU9cjJqkjFca9pjM3aq/TuZxJeom4bI= -github.com/elastic/ebpfevents v0.4.0/go.mod h1:o21z5xup/9dK8u0Hg9bZRflSqqj1Zu5h2dg2hSTcUPQ= +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/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.0 h1:GHFzDJIWpdgI0qDk5EcqbQJGvwTsl2E2vQK3/xe+MYQ= @@ -764,8 +764,8 @@ github.com/foxcpp/go-mockdns v0.0.0-20201212160233-ede2f9158d15/go.mod h1:tPg4cp github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= -github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= @@ -911,6 +911,8 @@ github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= @@ -1631,7 +1633,6 @@ github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1724,8 +1725,9 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rootless-containers/rootlesskit v1.1.0 h1:cRaRIYxY8oce4eE/zeAUZhgKu/4tU1p9YHN4+suwV7M= github.com/rootless-containers/rootlesskit v1.1.0/go.mod h1:H+o9ndNe7tS91WqU0/+vpvc+VaCd7TCIWaJjnV0ujUo= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= diff --git a/libbeat/ebpf/sys/sys.go b/libbeat/ebpf/sys/sys.go new file mode 100644 index 000000000000..64270066b80f --- /dev/null +++ b/libbeat/ebpf/sys/sys.go @@ -0,0 +1,94 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build linux + +package sys + +import ( + "encoding/base64" + "fmt" + "os" + "strings" + "sync" + "time" +) + +const ( + inodeFile = "/proc/self/ns/pid" + bootIDFile = "/proc/sys/kernel/random/boot_id" +) + +var ( + pidNSInodeOnce struct { + sync.Once + value uint64 + err error + } + bootIDOnce struct { + sync.Once + value string + err error + } +) + +func PidNSInode() (uint64, error) { + pidNSInodeOnce.Do(func() { + var b []byte + b, pidNSInodeOnce.err = os.ReadFile(inodeFile) + if pidNSInodeOnce.err != nil { + return + } + _, pidNSInodeOnce.err = fmt.Sscanf(string(b), "pid:[%d]", &pidNSInodeOnce.value) + }) + return pidNSInodeOnce.value, pidNSInodeOnce.err +} + +func BootID() (string, error) { + bootIDOnce.Do(func() { + var b []byte + b, bootIDOnce.err = os.ReadFile(bootIDFile) + if bootIDOnce.err != nil { + return + } + bootIDOnce.value = strings.TrimRight(string(b), "\n") + }) + return bootIDOnce.value, bootIDOnce.err +} + +func EntityID(pid uint32, start time.Time) (string, error) { + pidNsInode, err := PidNSInode() + if err != nil { + return "", err + } + + bootID, err := BootID() + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString( + []byte( + fmt.Sprintf("%d__%s__%d__%d", + pidNsInode, + bootID, + uint64(pid), + uint64(start.Unix()), + ), + ), + ), nil +} diff --git a/libbeat/ebpf/sys/time.go b/libbeat/ebpf/sys/time.go new file mode 100644 index 000000000000..c90b6d1135ce --- /dev/null +++ b/libbeat/ebpf/sys/time.go @@ -0,0 +1,109 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build linux + +package sys + +import ( + "sync" + "time" + + "github.com/prometheus/procfs" + "github.com/tklauser/go-sysconf" +) + +var ( + bootTimeOnce struct { + sync.Once + value time.Time + err error + } + ticksPerSecondOnce struct { + sync.Once + value uint64 + err error + } +) + +func BootTime() (time.Time, error) { + bootTimeOnce.Do(func() { + var ( + fs procfs.FS + stat procfs.Stat + ) + fs, bootTimeOnce.err = procfs.NewDefaultFS() + if bootTimeOnce.err != nil { + return + } + + stat, bootTimeOnce.err = fs.Stat() + if bootTimeOnce.err != nil { + return + } + bootTimeOnce.value = time.Unix(int64(stat.BootTime), 0) + }) + return bootTimeOnce.value, bootTimeOnce.err +} + +func TicksPerSecond() (uint64, error) { + ticksPerSecondOnce.Do(func() { + var tps int64 + tps, ticksPerSecondOnce.err = sysconf.Sysconf(sysconf.SC_CLK_TCK) + if ticksPerSecondOnce.err != nil { + return + } + ticksPerSecondOnce.value = uint64(tps) + }) + return ticksPerSecondOnce.value, ticksPerSecondOnce.err +} + +func TicksToNs(ticks uint64) (uint64, error) { + tps, err := TicksPerSecond() + if err != nil { + return 0, err + } + + return ticks * uint64(time.Second.Nanoseconds()) / tps, nil +} + +func TimeFromNsSinceBoot(ns uint64) (time.Time, error) { + bt, err := BootTime() + if err != nil { + return time.Time{}, err + } + + reduced, err := reduceTimestampPrecision(ns) + if err != nil { + return time.Time{}, err + } + + return bt.Add(time.Duration(reduced)), nil +} + +// When generating an `entity_id` in ECS we need to reduce the precision of a +// process's start time to that of procfs. Process start times can come from either +// eBPF (high precision) or other sources. We must reduce them all to the +// lowest common denominator such that entity ID's generated are always consistent. +func reduceTimestampPrecision(ns uint64) (uint64, error) { + tps, err := TicksPerSecond() + if err != nil { + return 0, err + } + + return ns - (ns % (uint64(time.Second.Nanoseconds()) / tps)), nil +}