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`]    — single-threaded launcher and target exec lifecycle.
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 exec::maybe_run_launcher;
21pub use probe::ProbeResult;
22
23use crate::{
24    error::CoreError,
25    profile::SandboxProfile,
26    sandbox::{BackendInfo, BackendOptions, SandboxBackend},
27};
28
29/// Linux backend wrapping Landlock + seccomp-bpf.
30#[derive(Debug)]
31pub struct LinuxSandbox {
32    info: BackendInfo,
33    options: BackendOptions,
34    probe: ProbeResult,
35}
36
37impl LinuxSandbox {
38    /// Probe the kernel and construct the backend. Returns
39    /// [`CoreError::BackendUnavailable`] on kernels older than 5.13.
40    pub fn new() -> Result<Self, CoreError> {
41        Self::new_with_options(BackendOptions::default())
42    }
43
44    /// Constructor with capability-specific runtime options.
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(
76        &self,
77        profile: &SandboxProfile,
78        proxy_port: Option<u16>,
79    ) -> Result<String, CoreError> {
80        Ok(policy::render(
81            profile,
82            proxy_port,
83            &self.probe,
84            self.options,
85        ))
86    }
87
88    fn run(
89        &self,
90        profile: &SandboxProfile,
91        proxy_port: Option<u16>,
92        command: &[String],
93        extra_env: &HashMap<String, String>,
94        pid_tx: Option<tokio::sync::oneshot::Sender<u32>>,
95    ) -> impl std::future::Future<Output = Result<ExitStatus, CoreError>> + Send {
96        let probe = self.probe.clone();
97        let options = self.options;
98        let profile = profile.clone();
99        let command = command.to_vec();
100        let extra_env = extra_env.clone();
101        async move {
102            exec::run_sandboxed(
103                &profile, proxy_port, &command, &extra_env, &probe, options, pid_tx,
104            )
105            .await
106        }
107    }
108}