refactor(sandbox): defer dmesg hardening

Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
This commit is contained in:
Matthew Grossman
2026-08-14 09:50:33 -07:00
parent 8e70faeeff
commit feb15b4dfe
3 changed files with 14 additions and 54 deletions
@@ -25,6 +25,7 @@ pub const NETNS_IPROUTE2_ROOT: &str = "/var/run/netns";
///
/// TODO: Make the supervisor's helper functionality self-contained so it no
/// longer depends on executable and library paths shared with the image.
/// <https://github.com/NVIDIA/OpenShell/pull/2715>
pub const FORBIDDEN_WORKSPACE_ROOTS: &[&str] = &[
// Kernel-managed OCI runtime mounts.
"/proc",
@@ -1036,6 +1036,11 @@ pub fn build_container_spec_for_image(
// /openshell-sandbox, so it appears at /opt/openshell/bin/openshell-sandbox.
image_volumes,
hostname: format!("sandbox-{}", sandbox.name),
// This is the supervisor's startup cwd, not the workload cwd. Starting
// at the image-selected workspace would let a malformed or inaccessible
// WORKDIR prevent the OCI runtime from launching the supervisor before
// it can validate the mounted workspace and report readiness. Direct and
// SSH children use the resolved workspace passed through `--workdir`.
work_dir: "/".to_string(),
// Override the image's ENTRYPOINT so the supervisor binary runs
// directly. Sandbox images (e.g. the community base image) set
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! Bypass detection monitor — follows kernel log messages with `dmesg` to
//! Bypass detection monitor — reads kernel log messages from `/dev/kmsg` to
//! detect and report direct connection attempts that bypass the HTTP CONNECT
//! proxy.
//!
@@ -12,9 +12,9 @@
//!
//! ## Graceful degradation
//!
//! If an approved `dmesg` binary is unavailable, the monitor logs a one-time
//! warning and returns. The nftables reject rules still provide fast-fail UX —
//! the monitor only adds diagnostic visibility.
//! If `/dev/kmsg` cannot be opened (e.g., restricted container environment),
//! the monitor logs a one-time warning and returns. The nftables reject rules
//! still provide fast-fail UX — the monitor only adds diagnostic visibility.
mod procfs;
@@ -29,21 +29,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
use tokio::sync::mpsc;
use tracing::debug;
const DMESG_PATHS: &[&str] = &[
"/usr/bin/dmesg",
"/bin/dmesg",
"/usr/sbin/dmesg",
"/sbin/dmesg",
];
fn find_dmesg_binary<'a>(candidates: &'a [&'a str]) -> Option<&'a str> {
candidates.iter().copied().find(|candidate| {
let path = std::path::Path::new(candidate);
path.is_absolute() && path.is_file()
})
}
/// A parsed nftables kernel log entry.
/// A parsed nftables log entry from `/dev/kmsg`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BypassEvent {
/// Destination IP address.
@@ -195,23 +181,8 @@ pub fn spawn(
use std::io::BufRead;
use std::process::{Command, Stdio};
// Use only known system locations. The sandbox image's PATH is controlled
// by the image author and may include the writable workspace.
let Some(dmesg_path) = find_dmesg_binary(DMESG_PATHS) else {
let event = NetworkActivityBuilder::new(openshell_ocsf::ctx::ctx())
.activity(ActivityId::Other)
.severity(SeverityId::Low)
.message(
"dmesg not available at an approved system path; bypass detection monitor will not run. \
Bypass REJECT rules still provide fast-fail behavior.",
)
.build();
ocsf_emit!(event);
return None;
};
// Verify dmesg works before spawning the monitor.
let dmesg_check = Command::new(dmesg_path)
// Verify dmesg is available before spawning the monitor.
let dmesg_check = Command::new("dmesg")
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null())
@@ -238,7 +209,7 @@ pub fn spawn(
let handle = tokio::task::spawn_blocking(move || {
// Start dmesg in follow mode to tail new kernel messages.
let mut child = match Command::new(dmesg_path)
let mut child = match Command::new("dmesg")
.args(["--follow", "--notime"])
.stdout(Stdio::piped())
.stderr(Stdio::null())
@@ -411,23 +382,6 @@ fn resolve_process_identity(entrypoint_pid: u32, src_port: u16) -> (String, Stri
mod tests {
use super::*;
#[test]
fn dmesg_lookup_uses_only_existing_absolute_candidates() {
let dir = tempfile::tempdir().unwrap();
let binary = dir.path().join("dmesg");
std::fs::write(&binary, "test").unwrap();
let binary = binary.to_str().unwrap();
assert_eq!(
find_dmesg_binary(&["relative/dmesg", "/definitely/missing/dmesg", binary]),
Some(binary)
);
assert_eq!(
find_dmesg_binary(&["relative/dmesg", "/definitely/missing/dmesg"]),
None
);
}
#[test]
fn parse_kmsg_line_tcp_bypass() {
let line = "6,1234,5678,-;openshell:bypass:sandbox-abcd1234:IN= OUT=veth-s-abcd1234 \