diff --git a/Cargo.lock b/Cargo.lock index 2dddc6ee..2402b541 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,15 +59,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -79,9 +70,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -163,17 +154,6 @@ dependencies = [ "syn 2.0.41", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -239,7 +219,7 @@ source = "git+https://github.com/aya-rs/aya?rev=33b2e45ad313f7468a7d11667f13d9c3 dependencies = [ "assert_matches", "aya-obj", - "bitflags 2.4.0", + "bitflags 2.4.2", "bytes", "lazy_static", "libc", @@ -306,9 +286,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "block-buffer" @@ -447,24 +427,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term 0.12.1", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -472,14 +437,14 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim", ] [[package]] @@ -588,7 +553,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "crossterm_winapi", "libc", "parking_lot", @@ -736,9 +701,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "email-encoding" @@ -1059,15 +1024,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.1" @@ -1276,7 +1232,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "rustix", "windows-sys 0.48.0", ] @@ -1408,7 +1364,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d8de370f98a6cb8a4606618e53e802f93b094ddec0f96988eaec2c27e6e9ce7" dependencies = [ - "clap 4.4.11", + "clap", "termcolor", "threadpool", ] @@ -1421,9 +1377,9 @@ checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1458,22 +1414,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] -name = "memmap" -version = "0.7.0" +name = "memmap2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", - "winapi", -] - -[[package]] -name = "memoffset" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" -dependencies = [ - "autocfg", ] [[package]] @@ -1595,10 +1541,10 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "cfg-if", "libc", - "memoffset 0.9.0", + "memoffset", ] [[package]] @@ -1626,26 +1572,26 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", ] [[package]] name = "nut" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1497c6f9820ced3aed3311bd28b386627aba0b2298a2dab2dde941b9bfe24" +checksum = "2b30ee78b595d9b4e6d315c76ea49c1811b342aced9564afc9a3ac897e762eb4" dependencies = [ - "ansi_term 0.11.0", - "bitflags 1.3.2", - "clap 2.34.0", + "ansi_term", + "bitflags 2.4.2", + "clap", "either", "fnv", "fs2", "hexdump", "lock_api", - "memmap", - "memoffset 0.5.6", + "memmap2", + "memoffset", "page_size", "parking_lot", "poolcache", @@ -1673,7 +1619,7 @@ version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -1733,9 +1679,9 @@ dependencies = [ [[package]] name = "page_size" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" dependencies = [ "libc", "winapi", @@ -1888,7 +1834,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "hex", "lazy_static", "procfs-core", @@ -1901,7 +1847,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "hex", ] @@ -1920,7 +1866,7 @@ version = "0.7.0" dependencies = [ "anyhow", "bpf-common", - "clap 4.4.11", + "clap", "comfy-table", "desktop-notifier", "engine-api", @@ -2168,7 +2114,7 @@ version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -2229,9 +2175,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -2459,12 +2405,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -2604,15 +2544,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.51" @@ -3028,12 +2959,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -3410,7 +3335,7 @@ name = "xtask" version = "0.7.0" dependencies = [ "anyhow", - "clap 4.4.11", + "clap", "num_cpus", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index 985b49de..7b30aa83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,7 +141,7 @@ nix = { version = "0.27.1", features = [ "net", ] } num_cpus = "1.16" -nut = "0.1.1" +nut = "0.1.3" openssl = "0.10.61" proc-macro2 = "1.0.70" procfs = { version = "0.16.0", default-features = false } diff --git a/crates/bpf-common/src/containers/mod.rs b/crates/bpf-common/src/containers/mod.rs index bb12c0cb..3b9bfc8e 100644 --- a/crates/bpf-common/src/containers/mod.rs +++ b/crates/bpf-common/src/containers/mod.rs @@ -11,12 +11,12 @@ use std::{ use diesel::prelude::*; use ini::Ini; -use nix::unistd::{Pid, Uid}; +use nix::unistd::Uid; use serde::{Deserialize, Serialize}; use thiserror::Error; use validatron::Validatron; -use crate::parsing::procfs::{self, ProcfsError}; +use crate::parsing::procfs::ProcfsError; pub mod schema; @@ -158,14 +158,13 @@ impl fmt::Display for ContainerInfo { } impl ContainerInfo { - pub fn from_pid(pid: Pid) -> Result, ContainerError> { - let Some(container_id) = procfs::get_process_container_id(pid)? else { - return Ok(None); - }; - + pub fn from_container_id( + container_id: ContainerId, + uid: Uid, + ) -> Result, ContainerError> { let info = match container_id { ContainerId::Docker(id) => Self::from_docker_id(id), - ContainerId::Libpod(id) => Self::from_libpod_id(id, pid), + ContainerId::Libpod(id) => Self::from_libpod_id(id, uid), }; info.map(Some) @@ -203,9 +202,7 @@ impl ContainerInfo { }) } - fn from_libpod_id(id: String, pid: Pid) -> Result { - let uid = procfs::get_process_user_id(pid)?; - + fn from_libpod_id(id: String, uid: Uid) -> Result { let user_home = unsafe { get_user_home_dir(uid) }.ok_or(ContainerError::HomeDirNotFound { uid })?; diff --git a/crates/bpf-common/src/lib.rs b/crates/bpf-common/src/lib.rs index 307bb231..2dc5efd2 100644 --- a/crates/bpf-common/src/lib.rs +++ b/crates/bpf-common/src/lib.rs @@ -32,6 +32,7 @@ pub fn log_error(msg: &str, err: E } pub use nix::unistd::Pid; +pub use nix::unistd::Uid; #[cfg(all(target_os = "linux", target_arch = "x86"))] #[path = "platform/linux-x86/mod.rs"] diff --git a/crates/bpf-common/src/parsing/procfs.rs b/crates/bpf-common/src/parsing/procfs.rs index 67b64d25..da83416b 100644 --- a/crates/bpf-common/src/parsing/procfs.rs +++ b/crates/bpf-common/src/parsing/procfs.rs @@ -18,9 +18,9 @@ const AT_FDCWD: i32 = -100; lazy_static! { /// Pattern for matching cgroups created by Docker. - static ref RE_CGROUP_DOCKER: Regex = Regex::new(r"docker.(?P[0-9a-f]+)(?:[^0-9a-f])").unwrap(); + static ref RE_CGROUP_DOCKER: Regex = Regex::new(r"docker-(?P[0-9a-f]{64})").unwrap(); /// Pattern for matching cgroups created by libpod/podman. - static ref RE_CGROUP_LIBPOD: Regex = Regex::new(r"libpod(?:-conmon)?-(?P[0-9a-f]+)(?:[^0-9a-f])").unwrap(); + static ref RE_CGROUP_LIBPOD: Regex = Regex::new(r"libpod-(?P[0-9a-f]{64})").unwrap(); } #[derive(Error, Debug)] @@ -136,25 +136,6 @@ pub fn get_process_user_id(pid: Pid) -> Result { Err(ProcfsError::UserNotFound(pid)) } -/// Returns the cpuset cgroup id of a given process. -pub fn get_process_cgroup_id(pid: Pid) -> Option { - let cgroup_path = format!("/proc/{pid}/cgroup"); - let file = match File::open(cgroup_path) { - Ok(f) => f, - Err(_) => return None, - }; - - let reader = BufReader::new(file); - for line in reader.lines().flatten() { - if !line.is_empty() && line.contains(":cpuset:") { - let mut s = line.splitn(3, ':'); - return s.nth(2).map(|s| s.to_string()); - } - } - - None -} - pub fn get_running_processes() -> Result, ProcfsError> { glob("/proc/[0-9]*")? .map(|entry| { @@ -225,13 +206,15 @@ mod test { "3f084b4c7b789c1a0f174da3fcd339e31125d3096b3ff46a0bef4fad71d09362".to_owned() )) ); - // The cgroup pattern observed with podman on Fedora. - let container_id = get_container_id_from_cgroup("0::/machine.slice/libpod-conmon-551ccf517b3394d9b953efeb8296b93451e45c2a8288518e4391d7b1db3cc9ee.scope"); + // Simple case + let container_id = get_container_id_from_cgroup( + "libpod-66e02eba51752490a89c808be0e01bd94514e20554a99b3ce7d62ac2361982e4", + ); assert_eq!( container_id, Some(ContainerId::Libpod( - "551ccf517b3394d9b953efeb8296b93451e45c2a8288518e4391d7b1db3cc9ee".to_owned() + "66e02eba51752490a89c808be0e01bd94514e20554a99b3ce7d62ac2361982e4".to_owned() )) - ) + ); } } diff --git a/crates/bpf-filtering/src/initializer.rs b/crates/bpf-filtering/src/initializer.rs index a4e8dcff..e9bbcae4 100644 --- a/crates/bpf-filtering/src/initializer.rs +++ b/crates/bpf-filtering/src/initializer.rs @@ -73,17 +73,19 @@ pub async fn setup_events_filter( process_tracker.update(TrackerUpdate::Fork { ppid: process.parent, pid: process.pid, + uid: process.uid, timestamp: Timestamp::from(0), namespaces: process.namespaces, - is_new_container: false, + container_id: process.container_id.clone(), }); process_tracker.update(TrackerUpdate::Exec { pid: process.pid, + uid: process.uid, image: process.image.to_string(), timestamp: Timestamp::from(0), argv: Vec::new(), namespaces: process.namespaces, - is_new_container: false, + container_id: process.container_id.clone(), }); } @@ -91,9 +93,20 @@ pub async fn setup_events_filter( let mut apply_new_events = || -> Result<()> { while let Ok(update) = rx_processes.try_recv() { match &update { - TrackerUpdate::Fork { pid, ppid, .. } => { - initializer.update(process_tree.fork(*pid, *ppid)?)? - } + TrackerUpdate::Fork { + pid, + ppid, + uid, + namespaces, + container_id, + .. + } => initializer.update(process_tree.fork( + *pid, + *ppid, + *uid, + *namespaces, + container_id.clone(), + )?)?, TrackerUpdate::Exec { pid, image, .. } => { initializer.update(process_tree.exec(*pid, image)?)? } diff --git a/crates/bpf-filtering/src/process_tree.rs b/crates/bpf-filtering/src/process_tree.rs index 1d6c5506..72d8e989 100644 --- a/crates/bpf-filtering/src/process_tree.rs +++ b/crates/bpf-filtering/src/process_tree.rs @@ -1,8 +1,9 @@ use std::{collections::HashMap, fs, path::Path}; use bpf_common::{ + containers::ContainerId, parsing::procfs::{self, ProcfsError}, - Pid, + Pid, Uid, }; use lazy_static::lazy_static; use pulsar_core::event::Namespaces; @@ -21,9 +22,11 @@ pub(crate) struct ProcessTree { #[derive(Debug)] pub(crate) struct ProcessData { pub(crate) pid: Pid, + pub(crate) uid: Uid, pub(crate) image: String, pub(crate) parent: Pid, pub(crate) namespaces: Namespaces, + pub(crate) container_id: Option, } #[derive(Debug, Error)] @@ -39,6 +42,7 @@ pub(crate) enum Error { } pub(crate) const PID_0: Pid = Pid::from_raw(0); +pub(crate) const UID_0: Uid = Uid::from_raw(0); fn get_process_namespace(pid: Pid, ns_type: &str) -> Result { let path = Path::new("/proc") @@ -104,6 +108,8 @@ impl ProcessTree { // Get process list for pid in procfs::get_running_processes()? { + let uid = procfs::get_process_user_id(pid)?; + let image = procfs::get_process_image(pid) .map(|path| path.to_string_lossy().to_string()) .unwrap_or_else(|err| { @@ -115,13 +121,18 @@ impl ProcessTree { Pid::from_raw(1) }); let namespaces = get_process_namespaces(pid); + + let container_id = procfs::get_process_container_id(pid)?; + processes.insert( pid, ProcessData { pid, + uid, image, parent, namespaces, + container_id, }, ); children.entry(parent).or_default().push(pid); @@ -134,9 +145,11 @@ impl ProcessTree { PID_0, ProcessData { pid: PID_0, + uid: UID_0, image: String::from("kernel"), parent: PID_0, namespaces, + container_id: None, }, ); @@ -167,17 +180,26 @@ impl ProcessTree { /// Add a new entry and return its process info. /// This is needed during initialization to go from raw fork/exec events to /// the full PorcessData needed by the policy filtering setup. - pub(crate) fn fork(&mut self, pid: Pid, ppid: Pid) -> Result<&ProcessData, Error> { + pub(crate) fn fork( + &mut self, + pid: Pid, + ppid: Pid, + uid: Uid, + namespaces: Namespaces, + container_id: Option, + ) -> Result<&ProcessData, Error> { let parent = self.processes.iter().find(|p| p.pid == ppid); match parent { Some(parent) => { let image = parent.image.to_string(); - let namespaces = get_process_namespaces(pid); + self.processes.push(ProcessData { pid, + uid, image, parent: ppid, namespaces, + container_id, }); Ok(self.processes.last().unwrap()) } diff --git a/crates/modules/process-monitor/probes.bpf.c b/crates/modules/process-monitor/probes.bpf.c index 77261eb2..8db56b15 100644 --- a/crates/modules/process-monitor/probes.bpf.c +++ b/crates/modules/process-monitor/probes.bpf.c @@ -27,6 +27,8 @@ char LICENSE[] SEC("license") = "GPL v2"; #define MAX_CONTAINER_RUNTIMES 8 +#define CONTAINER_ID_MAX_BUF 72 + struct namespaces { unsigned int uts; unsigned int ipc; @@ -34,37 +36,64 @@ struct namespaces { unsigned int pid; unsigned int net; unsigned int time; - unsigned int cgroup; + unsigned int cgroup; }; -struct fork_event { +#define OPTION_NONE 0 +#define OPTION_SOME 1 + +struct fork_event +{ + uid_t uid; pid_t ppid; struct namespaces namespaces; - unsigned int is_new_container; + struct + { + u32 discriminant; + struct + { + int container_engine; + struct buffer_index cgroup_id; + } container_id; + } option_index; }; -struct exec_event { +struct exec_event +{ + uid_t uid; struct buffer_index filename; int argc; struct buffer_index argv; struct namespaces namespaces; - unsigned int is_new_container; + struct + { + u32 discriminant; + struct + { + int container_engine; + struct buffer_index cgroup_id; + } container_id; + } option_index; }; -struct exit_event { +struct exit_event +{ u32 exit_code; }; -struct change_parent_event { +struct change_parent_event +{ pid_t ppid; }; -struct cgroup_event { +struct cgroup_event +{ struct buffer_index path; u64 id; }; -struct cgroup_attach_event { +struct cgroup_attach_event +{ pid_t pid; struct buffer_index path; u64 id; @@ -84,7 +113,8 @@ OUTPUT_MAP(process_event, { struct cgroup_attach_event cgroup_attach; }); -struct pending_dead_process { +struct pending_dead_process +{ // Pid of the dead process who left orphans pid_t dead_parent; // Timestamp of the death of parent @@ -96,37 +126,131 @@ struct pending_dead_process { // Temporary map used to communicate the list of orphaned processes from // `sched_process_exit` to `sched_switch`. There we'll be able to read // the new parent of the orphans and emit an EVENT_CHANGE_PARENT. -struct { +struct +{ __uint(type, BPF_MAP_TYPE_QUEUE); __type(value, struct pending_dead_process); __uint(max_entries, MAX_PENDING_DEAD_PARENTS); } orphans_map SEC(".maps"); -struct { +struct +{ __uint(type, BPF_MAP_TYPE_HASH); __type(key, u64); __type(value, u8); __uint(max_entries, MAX_CONTAINER_RUNTIMES); } container_runtimes_map SEC(".maps"); +/* +Identifies the container engine and reads the cgroup id of a process +from its `task_struct` into an given array of character. + +The array size MUST be greater than 72. + +### Input: + `char buf[]`: a pointer to an array of characters + `size_t sz`: size of the buffer + `int *offset`: a pointer to an integer + `struct task_struct *cur_tsk`: a pointer to a process task struct + +### Output: + the return value is: + - `0`: it's a docker container id + - `1`: it's a podman container id + and the characters array `buf` contains the id of the container + at the position specified in the value pointed by `offset`. + +### Errors: + if return value is less than `0`, in particular: + - `-1`: failed to read the id of the memory cgroup for the + current kernel + - `-2`: there are no matches for any known container engine + - `-3`: failed to read the cgroup name from the kernel memory + - `-4`: failed to read the cgroup name of the the parent of + the given process in the case of podman container + - `-5`: failed to parse a `libpod-` cgroup name of the parent + of the process after a successful parse of a `container` + cgroup name for the given process +*/ +static __always_inline int get_container_info(struct task_struct *cur_tsk, char *buf, size_t sz, int *offset) +{ + int cgrp_id; + if (bpf_core_enum_value_exists(enum cgroup_subsys_id, memory_cgrp_id)) + cgrp_id = bpf_core_enum_value(enum cgroup_subsys_id, memory_cgrp_id); + else + return -1; + + // failed when use BPF_PROBE_READ + const char *name = BPF_CORE_READ(cur_tsk, cgroups, subsys[cgrp_id], cgroup, kn, name); + // bpf_printk("name: %s\n", name); + if (bpf_probe_read_kernel_str(buf, sz, name) < 0) + { + bpf_printk("failed to get kernfs node name: %s\n", buf); + return -3; + } + + // Docker case + if (bpf_strncmp(buf, 7, "docker-") == 0) + { + // bpf_printk("docker case: %s\n", buf); + *offset = 7; + return 0; + } + + // Podman case + // + // the check for NULL character is needed to avoid collisions with + // `containerd-` prefixed cgroup name + if (bpf_strncmp(buf, 9, "container") == 0 && buf[9] == '\0') + { + + const char *parent_name = BPF_CORE_READ(cur_tsk, cgroups, subsys[cgrp_id], cgroup, kn, parent, name); + // bpf_printk("podman case step 1: parent name: %s\n", parent_name); + + if (bpf_probe_read_kernel_str(buf, sz, parent_name) < 0) + { + bpf_printk("failed to get parent kernfs node name: %s\n", buf); + return -4; + } + + if (bpf_strncmp(buf, 7, "libpod-") == 0) + { + // bpf_printk("podman case step 2: id: %s\n", buf); + *offset = 7; + return 1; + } + + // Error podman step 2 + bpf_printk("failed parsing podman step 2: id: %s\n", buf); + return -5; + } + + // Error parsing + // bpf_printk("failed parsing container id: %s\n", buf); + return -2; +} + /** * Checks whether the `child` task creates a new container, by: * - * * Comparing its mount namespace with the mount namespace of the `parent`. - * * Checking if the `parent` is a registered container runtime. + * - Comparing its mount namespace with the mount namespace of the `parent`. + * - Checking if the `parent` is a registered container runtime. * * Creation of a mount namespace is not optional in any container runtime or * engine, unlike many others, like PID or network namespace, so it's a reliable * way of recognizing new containers. */ -static __always_inline bool is_new_container(struct task_struct *parent, struct task_struct *child) { +static __always_inline bool is_new_container(struct task_struct *parent, struct task_struct *child) +{ unsigned int parent_mnt_ns = BPF_CORE_READ(parent, nsproxy, mnt_ns, ns.inum); unsigned int child_mnt_ns = BPF_CORE_READ(child, nsproxy, mnt_ns, ns.inum); - if (parent_mnt_ns != child_mnt_ns) { + if (parent_mnt_ns != child_mnt_ns) + { // Check if the parent process is a container runtime we track. unsigned long parent_exe_inode = BPF_CORE_READ(parent, mm, exe_file, f_inode, i_ino); - if (bpf_map_lookup_elem(&container_runtimes_map, &parent_exe_inode)) { + if (bpf_map_lookup_elem(&container_runtimes_map, &parent_exe_inode)) + { return true; } } @@ -138,13 +262,17 @@ static __always_inline bool is_new_container(struct task_struct *parent, struct // from the parent and emits a fork event. SEC("raw_tracepoint/sched_process_fork") int BPF_PROG(sched_process_fork, struct task_struct *parent, - struct task_struct *child) { + struct task_struct *child) +{ pid_t parent_tgid = BPF_CORE_READ(parent, tgid); pid_t child_tgid = BPF_CORE_READ(child, tgid); + char buf[CONTAINER_ID_MAX_BUF]; + // if parent process group matches the child one, we're forking a thread // and we ignore the event. - if (parent_tgid == child_tgid) { + if (parent_tgid == child_tgid) + { return 0; } // Propagate whitelist to child @@ -155,6 +283,9 @@ int BPF_PROG(sched_process_fork, struct task_struct *parent, if (!event) return 0; + u64 uid_gid = bpf_get_current_uid_gid(); + + event->fork.uid = uid_gid; event->fork.ppid = parent_tgid; event->fork.namespaces.uts = BPF_CORE_READ(child, nsproxy, uts_ns, ns.inum); event->fork.namespaces.ipc = BPF_CORE_READ(child, nsproxy, ipc_ns, ns.inum); @@ -163,7 +294,30 @@ int BPF_PROG(sched_process_fork, struct task_struct *parent, event->fork.namespaces.net = BPF_CORE_READ(child, nsproxy, net_ns, ns.inum); event->fork.namespaces.time = BPF_CORE_READ(child, nsproxy, time_ns, ns.inum); event->fork.namespaces.cgroup = BPF_CORE_READ(child, nsproxy, cgroup_ns, ns.inum); - event->fork.is_new_container = is_new_container(parent, child); + + int id_offset; + int container_engine = get_container_info(child, buf, CONTAINER_ID_MAX_BUF, &id_offset); + if (container_engine < 0) + { + // TODO: print error ?? + event->fork.option_index.discriminant = OPTION_NONE; + event->fork.option_index.container_id.container_engine = container_engine; + event->fork.option_index.container_id.cgroup_id.start = 0; + event->fork.option_index.container_id.cgroup_id.len = 0; + } + else + { + event->fork.option_index.discriminant = OPTION_SOME; + event->fork.option_index.container_id.container_engine = container_engine; + buffer_index_init(&event->buffer, &event->fork.option_index.container_id.cgroup_id); + buffer_append_str(&event->buffer, &event->fork.option_index.container_id.cgroup_id, + buf + id_offset, CONTAINER_ID_MAX_BUF); + + // bpf_printk("fork uid: {%u}", event->fork.uid); + // bpf_printk("fork container id: %s", buf); + // bpf_printk("fork id offest: %d", id_offset); + // bpf_printk("fork index created: start [%u] len [%u]", event->fork.option_index.container_id.cgroup_id.start, event->fork.option_index.container_id.cgroup_id.len); + } output_process_event(ctx, event); return 0; @@ -171,27 +325,65 @@ int BPF_PROG(sched_process_fork, struct task_struct *parent, SEC("raw_tracepoint/sched_process_exec") int BPF_PROG(sched_process_exec, struct task_struct *p, pid_t old_pid, - struct linux_binprm *bprm) { + struct linux_binprm *bprm) +{ pid_t tgid = bpf_get_current_pid_tgid() >> 32; + char buf[CONTAINER_ID_MAX_BUF]; + struct process_event *event = init_process_event(EVENT_EXEC, tgid); if (!event) return 0; event->exec.argc = BPF_CORE_READ(bprm, argc); + u64 uid_gid = bpf_get_current_uid_gid(); + + event->exec.uid = uid_gid; event->exec.namespaces.uts = BPF_CORE_READ(p, nsproxy, uts_ns, ns.inum); event->exec.namespaces.ipc = BPF_CORE_READ(p, nsproxy, ipc_ns, ns.inum); event->exec.namespaces.mnt = BPF_CORE_READ(p, nsproxy, mnt_ns, ns.inum); - event->exec.namespaces.pid = BPF_CORE_READ(p, nsproxy, pid_ns_for_children, ns.inum);; + event->exec.namespaces.pid = BPF_CORE_READ(p, nsproxy, pid_ns_for_children, ns.inum); event->exec.namespaces.net = BPF_CORE_READ(p, nsproxy, net_ns, ns.inum); event->exec.namespaces.time = BPF_CORE_READ(p, nsproxy, time_ns, ns.inum); event->exec.namespaces.cgroup = BPF_CORE_READ(p, nsproxy, cgroup_ns, ns.inum); - event->exec.is_new_container = is_new_container(BPF_CORE_READ(p, parent), p); + + int id_offset; + int container_engine = get_container_info(p, buf, CONTAINER_ID_MAX_BUF, &id_offset); + if (container_engine < 0) + { + // TODO: print error ?? + event->exec.option_index.discriminant = OPTION_NONE; + event->exec.option_index.container_id.cgroup_id.start = 0; + event->exec.option_index.container_id.cgroup_id.len = 0; + } + else + { + event->exec.option_index.discriminant = OPTION_SOME; + event->exec.option_index.container_id.container_engine = container_engine; + buffer_index_init(&event->buffer, &event->exec.option_index.container_id.cgroup_id); + buffer_append_str(&event->buffer, &event->exec.option_index.container_id.cgroup_id, + buf + id_offset, CONTAINER_ID_MAX_BUF); + + // bpf_printk("exec uid: {%u}", event->exec.uid); + // bpf_printk("exec container id: %s", buf); + // bpf_printk("fork id offest: %d", id_offset); + // bpf_printk("exec index created: start [%u] len [%u]", event->exec.option_index.container_id.cgroup_id.start, event->exec.option_index.container_id.cgroup_id.len); + } // This is needed because the first MAX_IMAGE_LEN bytes of buffer will // be used as a lookup key for the target and whitelist maps and garbage // would make the search fail. - __builtin_memset((char *)&event->buffer.buffer, 0, MAX_IMAGE_LEN); + u32 len_b = event->buffer.len; + + if (len_b > CONTAINER_ID_MAX_BUF) + { + bpf_printk("out of bounds 2"); + return 0; + } + + u64 *position = event->buffer.buffer + len_b; + + __builtin_memset((char *)position, 0, MAX_IMAGE_LEN); // We want to get the absolute path of the executable we're running. // When executing a process with a relative path, bprm->filename won't be @@ -200,11 +392,14 @@ int BPF_PROG(sched_process_exec, struct task_struct *p, pid_t old_pid, const char *bprm_filename = BPF_CORE_READ(bprm, filename); char first_character = 0; bpf_core_read(&first_character, 1, bprm_filename); - if (first_character == '/') { + if (first_character == '/') + { buffer_index_init(&event->buffer, &event->exec.filename); buffer_append_str(&event->buffer, &event->exec.filename, bprm_filename, BUFFER_MAX); - } else { + } + else + { struct path path = BPF_CORE_READ(bprm, file, f_path); get_path_str(&path, &event->buffer, &event->exec.filename); } @@ -229,7 +424,8 @@ int BPF_PROG(sched_process_exec, struct task_struct *p, pid_t old_pid, } // Context for loop_collect_orphan -struct ctx_collect_orphan { +struct ctx_collect_orphan +{ // Next process to check struct list_head *next; // End of list @@ -240,11 +436,13 @@ struct ctx_collect_orphan { // Used to iterate over the children of a dead process and collect them // into a `struct pending_dead_process`. Used in the collect_orphans loop. -static __always_inline long loop_collect_orphan(u32 i, void *callback_ctx) { +static __always_inline long loop_collect_orphan(u32 i, void *callback_ctx) +{ struct ctx_collect_orphan *c = callback_ctx; // Once we find the same element we started at, we know we've // iterated all children and we can exit the loop. - if (c->next == c->last) { + if (c->next == c->last) + { return LOOP_STOP; } struct task_struct *task = container_of(c->next, struct task_struct, sibling); @@ -258,7 +456,8 @@ static __always_inline long loop_collect_orphan(u32 i, void *callback_ctx) { // When a process exits, we have to add all its children to orphans_map. This // will be checked as soon as possibile (the scheduler iteration which happens // immediately after an exit syscall) for changes to a process parent. -static __always_inline void collect_orphans(pid_t tgid, struct task_struct *p) { +static __always_inline void collect_orphans(pid_t tgid, struct task_struct *p) +{ LOG_DEBUG("%d is DEAD ", tgid); // Collect orphans by iterating the dead process children @@ -271,30 +470,35 @@ static __always_inline void collect_orphans(pid_t tgid, struct task_struct *p) { LOOP(MAX_ORPHANS, MAX_ORPHANS_UNROLL, loop_collect_orphan, &c); // Log a warning if we couln't process all children - if (c.next != c.last) { + if (c.next != c.last) + { LOG_ERROR("Couldn't iterate all children. Too many of them"); } u64 r = bpf_map_push_elem(&orphans_map, &c.pending, 0); - if (r) { + if (r) + { LOG_ERROR("Pushing to orphans_map failed: %d", r); } } // This is attached to tracepoint:sched:sched_process_exit SEC("raw_tracepoint/sched_process_exit") -int BPF_PROG(sched_process_exit, struct task_struct *p) { +int BPF_PROG(sched_process_exit, struct task_struct *p) +{ pid_t tgid = BPF_CORE_READ(p, group_leader, pid); // We want to ignore threads and focus on whole processes, so we have // to wait for the whole process group to exit. Unfortunately, checking // if the current task's pid matches its tgid is not enough because // the main thread could exit before the child one. - if (BPF_CORE_READ(p, signal, live.counter) > 0) { + if (BPF_CORE_READ(p, signal, live.counter) > 0) + { return 0; } // cleanup resources from map_interest - if (!tracker_remove(&GLOBAL_INTEREST_MAP, p)) { + if (!tracker_remove(&GLOBAL_INTEREST_MAP, p)) + { LOG_DEBUG("%d not found in map_interest during exit", tgid); // Multiple threads may exit at the same time, causing the check above // to pass multiple times. Since we want to generate only one event, @@ -317,7 +521,8 @@ int BPF_PROG(sched_process_exit, struct task_struct *p) { } // Context for loop_orphan_adopted -struct ctx_orphan_adopted { +struct ctx_orphan_adopted +{ // eBPF program context used for emitting events void *ctx; // list of pending orphans we should check @@ -326,7 +531,8 @@ struct ctx_orphan_adopted { // Used to iterate over all the orphans left by a dead process and // emit an event with their new parent. Used in the sched_switch loop. -static __always_inline long loop_orphan_adopted(u32 i, void *callback_ctx) { +static __always_inline long loop_orphan_adopted(u32 i, void *callback_ctx) +{ struct ctx_orphan_adopted *c = callback_ctx; if (i >= MAX_ORPHANS) // satisfy verifier (never true) return LOOP_STOP; @@ -346,18 +552,24 @@ static __always_inline long loop_orphan_adopted(u32 i, void *callback_ctx) { /// On task switch, check if there are pending orphans and signal /// their parent changed. SEC("raw_tracepoint/sched_switch") -int BPF_PROG(sched_switch) { +int BPF_PROG(sched_switch) +{ struct ctx_orphan_adopted c; pid_t first = 0; - for (int i = 0; i < 5; i++) { - if (bpf_map_pop_elem(&orphans_map, &c.pending)) { + for (int i = 0; i < 5; i++) + { + if (bpf_map_pop_elem(&orphans_map, &c.pending)) + { return 0; } // Make sure the loop doesn't process the same dead parent multiple times - if (first == 0) { + if (first == 0) + { first = c.pending.dead_parent; - } else if (first == c.pending.dead_parent) { + } + else if (first == c.pending.dead_parent) + { // push the item back bpf_map_push_elem(&orphans_map, &c.pending, 0); break; @@ -367,12 +579,15 @@ int BPF_PROG(sched_switch) { // of the child is set. struct task_struct *first_orphan = c.pending.orphans[0]; pid_t first_new_parent = BPF_CORE_READ(first_orphan, parent, pid); - if (c.pending.dead_parent == first_new_parent) { + if (c.pending.dead_parent == first_new_parent) + { LOG_DEBUG("No new parent set yet for children of dead %d", c.pending.dead_parent); // push the item back bpf_map_push_elem(&orphans_map, &c.pending, 0); - } else { + } + else + { c.ctx = ctx; LOOP(MAX_ORPHANS, MAX_ORPHANS_UNROLL, loop_orphan_adopted, &c); } @@ -381,7 +596,8 @@ int BPF_PROG(sched_switch) { } SEC("raw_tracepoint/cgroup_mkdir") -int BPF_PROG(cgroup_mkdir, struct cgroup *cgrp, const char *path) { +int BPF_PROG(cgroup_mkdir, struct cgroup *cgrp, const char *path) +{ pid_t tgid = tracker_interesting_tgid(&GLOBAL_INTEREST_MAP); if (tgid < 0) return 0; @@ -398,7 +614,8 @@ int BPF_PROG(cgroup_mkdir, struct cgroup *cgrp, const char *path) { } SEC("raw_tracepoint/cgroup_rmdir") -int BPF_PROG(cgroup_rmdir, struct cgroup *cgrp, const char *path) { +int BPF_PROG(cgroup_rmdir, struct cgroup *cgrp, const char *path) +{ pid_t tgid = tracker_interesting_tgid(&GLOBAL_INTEREST_MAP); if (tgid < 0) return 0; @@ -415,7 +632,8 @@ int BPF_PROG(cgroup_rmdir, struct cgroup *cgrp, const char *path) { SEC("raw_tracepoint/cgroup_attach_task") int BPF_PROG(cgroup_attach_task, struct cgroup *cgrp, const char *path, - struct task_struct *task) { + struct task_struct *task) +{ tracker_check_cgroup_rules(&GLOBAL_INTEREST_MAP, &m_cgroup_rules, task, path); // If the event is of interest, emit it as usual diff --git a/crates/modules/process-monitor/src/lib.rs b/crates/modules/process-monitor/src/lib.rs index e9a65d02..9b419437 100644 --- a/crates/modules/process-monitor/src/lib.rs +++ b/crates/modules/process-monitor/src/lib.rs @@ -9,6 +9,7 @@ use bpf_common::{ program::BpfContext, BpfSender, Pid, Program, ProgramBuilder, ProgramError, }; +use nix::unistd::Uid; use pulsar_core::event::Namespaces; use thiserror::Error; use which::which; @@ -64,6 +65,20 @@ pub enum ProcessEventError { ContainerNotFound(Pid), } +#[derive(Debug)] +#[repr(C)] +pub enum COption { + None, + Some(T), +} + +#[derive(Debug)] +#[repr(C)] +pub struct CContainerId { + container_engine: i32, + cgroup_id: BufferIndex, +} + // The events sent from eBPF to userspace must be byte by byte // re-interpretable as Rust types. So pointers to the heap are // not allowed. @@ -71,16 +86,18 @@ pub enum ProcessEventError { #[repr(C)] pub enum ProcessEvent { Fork { + uid: Uid, ppid: Pid, namespaces: Namespaces, - is_new_container: bool, + c_container_id: COption, }, Exec { + uid: Uid, filename: BufferIndex, argc: u32, argv: BufferIndex, // 0 separated strings namespaces: Namespaces, - is_new_container: bool, + c_container_id: COption, }, Exit { exit_code: u32, @@ -120,7 +137,7 @@ fn extract_parameters(argv: &[u8]) -> Vec { pub mod pulsar { use super::*; - use bpf_common::{program::BpfEvent, BpfSenderWrapper}; + use bpf_common::{containers::ContainerId, program::BpfEvent, BpfSenderWrapper}; use pulsar_core::pdk::{ process_tracker::TrackerUpdate, CleanExit, IntoPayload, ModuleContext, ModuleError, Payload, PulsarModule, ShutdownSignal, Version, @@ -151,22 +168,40 @@ pub mod pulsar { BpfSenderWrapper::new(ctx.get_sender(), move |event: &BpfEvent| { let _ = tx_processes.send(match event.payload { ProcessEvent::Fork { + uid, ppid, namespaces, - is_new_container, - } => TrackerUpdate::Fork { - pid: event.pid, - ppid, - timestamp: event.timestamp, - namespaces, - is_new_container, - }, + ref c_container_id, + } => { + let container_id = match c_container_id { + COption::Some(ccid) => { + let id = ccid.cgroup_id.string(&event.buffer).unwrap(); + let container_id = match ccid.container_engine { + 0 => ContainerId::Docker(id), + 1 => ContainerId::Libpod(id), + _ => panic!("unknown container id identifier"), + }; + Some(container_id) + } + COption::None => None, + }; + + TrackerUpdate::Fork { + pid: event.pid, + uid, + ppid, + timestamp: event.timestamp, + namespaces, + container_id, + } + } ProcessEvent::Exec { + uid, ref filename, argc, ref argv, namespaces, - is_new_container, + ref c_container_id, } => { let argv = extract_parameters(argv.bytes(&event.buffer).unwrap_or_else(|err| { @@ -181,14 +216,29 @@ pub mod pulsar { event.pid ) } + + let container_id = match c_container_id { + COption::Some(ccid) => { + let id = ccid.cgroup_id.string(&event.buffer).unwrap(); + let container_id = match ccid.container_engine { + 0 => ContainerId::Docker(id), + 1 => ContainerId::Libpod(id), + _ => panic!("unknown container id identifier"), + }; + Some(container_id) + } + COption::None => None, + }; + TrackerUpdate::Exec { pid: event.pid, + uid, // ignoring this error since it will be catched in IntoPayload image: filename.string(&event.buffer).unwrap_or_default(), timestamp: event.timestamp, argv, namespaces, - is_new_container, + container_id, } } ProcessEvent::Exit { .. } => TrackerUpdate::Exit { diff --git a/crates/pulsar-core/src/pdk/process_tracker.rs b/crates/pulsar-core/src/pdk/process_tracker.rs index 679c2fc8..07c6a646 100644 --- a/crates/pulsar-core/src/pdk/process_tracker.rs +++ b/crates/pulsar-core/src/pdk/process_tracker.rs @@ -1,10 +1,11 @@ use std::collections::{BTreeMap, HashMap}; use bpf_common::{ - containers::{ContainerError, ContainerInfo}, + containers::{ContainerId, ContainerInfo}, time::Timestamp, Pid, }; +use nix::unistd::Uid; use thiserror::Error; use tokio::{ sync::{mpsc, oneshot}, @@ -25,6 +26,7 @@ pub struct ProcessTrackerHandle { tx: mpsc::UnboundedSender, } +#[derive(Debug)] enum TrackerRequest { GetProcessInfo(InfoRequest), UpdateProcess(TrackerUpdate), @@ -35,18 +37,20 @@ enum TrackerRequest { pub enum TrackerUpdate { Fork { pid: Pid, + uid: Uid, timestamp: Timestamp, ppid: Pid, namespaces: Namespaces, - is_new_container: bool, + container_id: Option, }, Exec { pid: Pid, + uid: Uid, timestamp: Timestamp, image: String, argv: Vec, namespaces: Namespaces, - is_new_container: bool, + container_id: Option, }, SetNewParent { pid: Pid, @@ -58,12 +62,14 @@ pub enum TrackerUpdate { }, } +#[derive(Debug)] struct InfoRequest { pid: Pid, ts: Timestamp, tx_reply: oneshot::Sender>, } +#[derive(Debug)] struct DescendantRequest { pid: Pid, image: String, @@ -251,43 +257,27 @@ impl ProcessTracker { } } - fn get_container_info( - &mut self, - pid: Pid, - is_new_container: bool, - ) -> Result, ContainerError> { - match ContainerInfo::from_pid(pid)? { - Some(container_info) => { - if is_new_container { - log::debug!("Detected a new container {}", container_info.id); - } else { - log::debug!( - "Detected an already existing container {}", - container_info.id - ); - } - Ok(Some(container_info)) - } - None => Ok(None), - } - } - fn handle_update(&mut self, mut update: TrackerUpdate) { match update { TrackerUpdate::Fork { pid, + uid, timestamp, ppid, namespaces, - is_new_container, + container_id, } => { - let container = match self.get_container_info(pid, is_new_container) { - Ok(container) => container, - Err(err) => { - log::error!("{err}"); - None - } - }; + let container = + container_id.and_then(|c_id| { + match ContainerInfo::from_container_id(c_id, uid) { + Ok(container) => container, + Err(err) => { + log::error!("{err}"); + None + } + } + }); + self.processes.insert( pid, ProcessData { @@ -313,19 +303,23 @@ impl ProcessTracker { } TrackerUpdate::Exec { pid, + uid, timestamp, ref mut image, ref mut argv, namespaces, - is_new_container, + ref container_id, } => { - let container = match self.get_container_info(pid, is_new_container) { - Ok(container) => container, - Err(err) => { - log::error!("{err}"); - None + let container = container_id.clone().and_then(|c_id| { + match ContainerInfo::from_container_id(c_id, uid) { + Ok(container) => container, + Err(err) => { + log::error!("{err}"); + None + } } - }; + }); + if let Some(p) = self.processes.get_mut(&pid) { p.exec_changes.insert(timestamp, std::mem::take(image)); p.argv = std::mem::take(argv); @@ -489,6 +483,7 @@ mod tests { const PID_1: Pid = Pid::from_raw(42); const PID_2: Pid = Pid::from_raw(43); + const UID_USER: Uid = Uid::from_raw(1000); const NAMESPACES_1: Namespaces = Namespaces { uts: 4026531835, @@ -519,17 +514,19 @@ mod tests { process_tracker.update(TrackerUpdate::Fork { ppid: PID_1, pid: PID_2, + uid: UID_USER, timestamp: 10.into(), namespaces: NAMESPACES_1, - is_new_container: false, + container_id: None, }); process_tracker.update(TrackerUpdate::Exec { pid: PID_2, + uid: UID_USER, image: "/bin/after_exec".to_string(), timestamp: 15.into(), argv: Vec::new(), namespaces: NAMESPACES_1, - is_new_container: false, + container_id: None, }); process_tracker.update(TrackerUpdate::Exit { pid: PID_2, @@ -580,18 +577,20 @@ mod tests { }); process_tracker.update(TrackerUpdate::Exec { pid: PID_2, + uid: UID_USER, image: "/bin/after_exec".to_string(), timestamp: 15.into(), argv: Vec::new(), namespaces: NAMESPACES_1, - is_new_container: false, + container_id: None, }); process_tracker.update(TrackerUpdate::Fork { ppid: PID_1, pid: PID_2, + uid: UID_USER, timestamp: 10.into(), namespaces: NAMESPACES_1, - is_new_container: false, + container_id: None, }); assert_eq!( process_tracker.get(PID_2, 9.into()).await,