Spawn the bypass monitor as a background tokio task. Uses `dmesg --follow` to tail the kernel ring buffer for nftables log entries matching the given namespace. Falls back gracefully if `dmesg` is not available. We use `dmesg` rather than reading `/dev/kmsg` directly because the container runtime's device cgroup policy blocks direct `/dev/kmsg` access even with `CAP_SYSLOG`. The `dmesg` command
(
namespace_name: String,
entrypoint_pid: Arc<AtomicU32>,
denial_tx: Option<mpsc::UnboundedSender<DenialEvent>>,
activity_tx: Option<ActivitySender>,
)
| 118 | /// Returns a `JoinHandle` if the monitor was started, or `None` if `dmesg` |
| 119 | /// is not available. |
| 120 | pub fn spawn( |
| 121 | namespace_name: String, |
| 122 | entrypoint_pid: Arc<AtomicU32>, |
| 123 | denial_tx: Option<mpsc::UnboundedSender<DenialEvent>>, |
| 124 | activity_tx: Option<ActivitySender>, |
| 125 | ) -> Option<tokio::task::JoinHandle<()>> { |
| 126 | use std::io::BufRead; |
| 127 | use std::process::{Command, Stdio}; |
| 128 | |
| 129 | // Verify dmesg is available before spawning the monitor. |
| 130 | let dmesg_check = Command::new("dmesg") |
| 131 | .arg("--version") |
| 132 | .stdout(Stdio::null()) |
| 133 | .stderr(Stdio::null()) |
| 134 | .status(); |
| 135 | |
| 136 | if !dmesg_check.is_ok_and(|s| s.success()) { |
| 137 | let event = NetworkActivityBuilder::new(openshell_ocsf::ctx::ctx()) |
| 138 | .activity(ActivityId::Other) |
| 139 | .severity(SeverityId::Low) |
| 140 | .message( |
| 141 | "dmesg not available; bypass detection monitor will not run. \ |
| 142 | Bypass REJECT rules still provide fast-fail behavior.", |
| 143 | ) |
| 144 | .build(); |
| 145 | ocsf_emit!(event); |
| 146 | return None; |
| 147 | } |
| 148 | |
| 149 | let namespace_prefix = format!("openshell:bypass:{namespace_name}:"); |
| 150 | debug!( |
| 151 | namespace = %namespace_name, |
| 152 | "Starting bypass detection monitor via dmesg --follow" |
| 153 | ); |
| 154 | |
| 155 | let handle = tokio::task::spawn_blocking(move || { |
| 156 | // Start dmesg in follow mode to tail new kernel messages. |
| 157 | let mut child = match Command::new("dmesg") |
| 158 | .args(["--follow", "--notime"]) |
| 159 | .stdout(Stdio::piped()) |
| 160 | .stderr(Stdio::null()) |
| 161 | .spawn() |
| 162 | { |
| 163 | Ok(c) => c, |
| 164 | Err(e) => { |
| 165 | let event = NetworkActivityBuilder::new(openshell_ocsf::ctx::ctx()) |
| 166 | .activity(ActivityId::Other) |
| 167 | .severity(SeverityId::Low) |
| 168 | .message(format!( |
| 169 | "Failed to start dmesg --follow; bypass monitor will not run: {e}" |
| 170 | )) |
| 171 | .build(); |
| 172 | ocsf_emit!(event); |
| 173 | return; |
| 174 | } |
| 175 | }; |
| 176 | |
| 177 | let Some(stdout) = child.stdout.take() else { |
no test coverage detected