Skip to content

Commit

Permalink
feat: cgroup kernel side
Browse files Browse the repository at this point in the history
  • Loading branch information
banditopazzo committed Jan 16, 2024
1 parent a0004f2 commit c8a9fc9
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 147 deletions.
23 changes: 10 additions & 13 deletions crates/bpf-common/src/containers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -158,14 +158,13 @@ impl fmt::Display for ContainerInfo {
}

impl ContainerInfo {
pub fn from_pid(pid: Pid) -> Result<Option<Self>, 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<Option<Self>, 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)
Expand Down Expand Up @@ -203,15 +202,13 @@ impl ContainerInfo {
})
}

fn from_libpod_id(id: String, pid: Pid) -> Result<Self, ContainerError> {
let uid = procfs::get_process_user_id(pid)?;

fn from_libpod_id(id: String, uid: Uid) -> Result<Self, ContainerError> {
let user_home =
unsafe { get_user_home_dir(uid) }.ok_or(ContainerError::HomeDirNotFound { uid })?;

let user_home = Path::new(&user_home);

let libpod_database_backend = LibpodDatabaseBackend::of_user(uid, user_home)?;
let libpod_database_backend = LibpodDatabaseBackend::of_user(uid, &user_home)?;

let config = match libpod_database_backend {
LibpodDatabaseBackend::Auto => {
Expand Down Expand Up @@ -357,7 +354,7 @@ impl LibpodDatabaseBackend {

unsafe fn get_user_home_dir(uid: Uid) -> Option<OsString> {
let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
n if n < 0 => 512_usize,
n if n < 0 => 512 as usize,
n => n as usize,
};
let mut buf = Vec::with_capacity(amt);
Expand Down
1 change: 1 addition & 0 deletions crates/bpf-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub fn log_error<E: std::error::Error + Send + Sync + 'static>(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"]
Expand Down
33 changes: 8 additions & 25 deletions crates/bpf-common/src/parsing/procfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<id>[0-9a-f]+)(?:[^0-9a-f])").unwrap();
static ref RE_CGROUP_DOCKER: Regex = Regex::new(r"docker-(?P<id>[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<id>[0-9a-f]+)(?:[^0-9a-f])").unwrap();
static ref RE_CGROUP_LIBPOD: Regex = Regex::new(r"libpod-(?P<id>[0-9a-f]{64})").unwrap();
}

#[derive(Error, Debug)]
Expand Down Expand Up @@ -136,25 +136,6 @@ pub fn get_process_user_id(pid: Pid) -> Result<Uid, ProcfsError> {
Err(ProcfsError::UserNotFound(pid))
}

/// Returns the cpuset cgroup id of a given process.
pub fn get_process_cgroup_id(pid: Pid) -> Option<String> {
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<Vec<Pid>, ProcfsError> {
glob("/proc/[0-9]*")?
.map(|entry| {
Expand Down Expand Up @@ -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()
))
)
);
}
}
23 changes: 18 additions & 5 deletions crates/bpf-filtering/src/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,27 +73,40 @@ 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(),
});
}

// apply pending changes
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.clone(),
container_id.clone(),
)?)?,
TrackerUpdate::Exec { pid, image, .. } => {
initializer.update(process_tree.exec(*pid, image)?)?
}
Expand Down
28 changes: 25 additions & 3 deletions crates/bpf-filtering/src/process_tree.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<ContainerId>,
}

#[derive(Debug, Error)]
Expand All @@ -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<u32, Error> {
let path = Path::new("/proc")
Expand Down Expand Up @@ -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| {
Expand All @@ -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);
Expand All @@ -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,
},
);

Expand Down Expand Up @@ -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<ContainerId>,
) -> 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())
}
Expand Down
Loading

0 comments on commit c8a9fc9

Please sign in to comment.