Skip to main content

sbe_core/sandbox/linux/
mod.rs

1//! Linux sandbox backend — Landlock LSM + seccomp-bpf.
2//!
3//! Layout mirrors the spec (§7):
4//! - [`probe`]   — kernel version + Landlock ABI detection.
5//! - [`policy`]  — deterministic YAML rendering for `--dry-run` / inspect.
6//! - [`landlock`] — `SandboxProfile` → Landlock [`Ruleset`] with pre-opened FDs.
7//! - [`seccomp`] — `SandboxProfile` → seccomp `BpfProgram` bytes.
8//! - [`exec`]    — `Command::pre_exec` wiring (alloc-free closure).
9//!
10//! [`Ruleset`]: ::landlock::Ruleset
11
12mod exec;
13mod landlock;
14pub mod policy;
15mod probe;
16mod seccomp;
17
18use std::{collections::HashMap, process::ExitStatus};
19
20pub use probe::ProbeResult;
21
22use crate::{
23    error::CoreError,
24    profile::SandboxProfile,
25    sandbox::{BackendInfo, BackendOptions, SandboxBackend},
26};
27
28/// Linux backend wrapping Landlock + seccomp-bpf.
29#[derive(Debug)]
30pub struct LinuxSandbox {
31    info: BackendInfo,
32    options: BackendOptions,
33    probe: ProbeResult,
34}
35
36impl LinuxSandbox {
37    /// Probe the kernel and construct the backend. Returns
38    /// [`CoreError::BackendUnavailable`] on kernels older than 5.13.
39    pub fn new() -> Result<Self, CoreError> {
40        Self::new_with_options(BackendOptions::default())
41    }
42
43    /// Constructor with runtime options ([`BackendOptions::allow_degraded`]
44    /// surfaces here).
45    pub fn new_with_options(options: BackendOptions) -> Result<Self, CoreError> {
46        let probe = probe::run()?;
47        let features = probe.features();
48        let info = BackendInfo {
49            name: "landlock+seccomp",
50            kernel: probe.kernel.clone(),
51            features,
52        };
53        Ok(Self {
54            info,
55            options,
56            probe,
57        })
58    }
59
60    /// Borrow the live probe — used by `render_policy` and `exec`.
61    pub fn probe(&self) -> &ProbeResult {
62        &self.probe
63    }
64}
65
66impl SandboxBackend for LinuxSandbox {
67    fn name(&self) -> &'static str {
68        self.info.name
69    }
70
71    fn info(&self) -> &BackendInfo {
72        &self.info
73    }
74
75    fn render_policy(&self, profile: &SandboxProfile, proxy_port: Option<u16>) -> String {
76        policy::render(profile, proxy_port, &self.probe, self.options)
77    }
78
79    fn run(
80        &self,
81        profile: &SandboxProfile,
82        proxy_port: Option<u16>,
83        command: &[String],
84        extra_env: &HashMap<String, String>,
85    ) -> impl std::future::Future<Output = Result<ExitStatus, CoreError>> + Send {
86        let probe = self.probe.clone();
87        let options = self.options;
88        let profile = profile.clone();
89        let command = command.to_vec();
90        let extra_env = extra_env.clone();
91        async move {
92            exec::run_sandboxed(&profile, proxy_port, &command, &extra_env, &probe, options).await
93        }
94    }
95}