Skip to main content

sbe_core/sandbox/
mod.rs

1//! Platform-specific sandbox backends.
2//!
3//! [`SandboxBackend`] is the seam between the orchestrator and the kernel.
4//! Backends are selected at compile time via `cfg(target_os = "...")` and
5//! re-exported as [`Sandbox`] — callers outside this module never name the
6//! platform-specific type.
7//!
8//! ## Construction contract
9//!
10//! Every backend exposes `pub fn new() -> Result<Self, CoreError>`.
11//! Construction performs the kernel/feature probe; the resulting
12//! [`BackendInfo`] is stable for the lifetime of the instance.
13//! If the platform cannot host the backend at all (kernel <5.13 on Linux,
14//! `sandbox-exec` missing on macOS), construction fails with
15//! [`CoreError::BackendUnavailable`] — no degraded silent path.
16
17use std::{collections::HashMap, process::ExitStatus};
18
19use crate::{error::CoreError, profile::SandboxProfile};
20
21#[cfg(target_os = "macos")]
22mod macos;
23#[cfg(target_os = "macos")]
24pub use macos::MacosSandbox as Sandbox;
25#[cfg(target_os = "macos")]
26pub use macos::sbpl;
27
28#[cfg(target_os = "linux")]
29mod linux;
30#[cfg(target_os = "linux")]
31pub use linux::LinuxSandbox as Sandbox;
32#[cfg(target_os = "linux")]
33pub use linux::policy;
34
35/// A platform-specific sandbox backend.
36///
37/// Implementations turn a resolved [`SandboxProfile`] into kernel-enforced
38/// restrictions on a spawned child process. The orchestrator holds the
39/// concrete `Sandbox` re-export (never `dyn SandboxBackend`); object safety
40/// is intentionally not required.
41pub trait SandboxBackend: Send + Sync {
42    /// Human-readable backend identifier, e.g. `"sandbox-exec"` or
43    /// `"landlock+seccomp"`. Surfaced in `sbe inspect` output and audit logs.
44    fn name(&self) -> &'static str;
45
46    /// What this backend can enforce on the current kernel/host. Populated
47    /// during construction (`Self::new` performs the kernel probe); this
48    /// accessor is infallible.
49    fn info(&self) -> &BackendInfo;
50
51    /// Render the resolved policy for `--dry-run` and `sbe inspect`. Output
52    /// must be deterministic and platform-stable so tests can assert on
53    /// substrings.
54    ///
55    /// - macOS: canonical SBPL Scheme document.
56    /// - Linux: a YAML document listing the Landlock ruleset, the seccomp action table, the proxy
57    ///   env, and the resolved [`BackendFeatures`].
58    fn render_policy(&self, profile: &SandboxProfile, proxy_port: Option<u16>) -> String;
59
60    /// Run the user command under the sandbox and return its exit status.
61    ///
62    /// The backend owns the per-invocation lifecycle: compile profile →
63    /// platform artifact (SBPL tempfile / Ruleset+BpfProgram), spawn child
64    /// with policy applied before `execve`, wait, clean up.
65    ///
66    /// Proxy lifecycle, audit logging, config resolution and exit-code
67    /// mapping live in the orchestrator. `extra_env` is the
68    /// orchestrator-merged environment (proxy vars + `profile.env`).
69    fn run(
70        &self,
71        profile: &SandboxProfile,
72        proxy_port: Option<u16>,
73        command: &[String],
74        extra_env: &HashMap<String, String>,
75    ) -> impl std::future::Future<Output = Result<ExitStatus, CoreError>> + Send;
76}
77
78/// What a backend can enforce on the current kernel/host.
79#[derive(Debug, Clone)]
80pub struct BackendInfo {
81    /// Backend identifier; matches [`SandboxBackend::name`].
82    pub name: &'static str,
83    /// Kernel version string for diagnostics; e.g. "Darwin 24.6.0" or "Linux 6.8.0".
84    pub kernel: String,
85    /// Feature flags resolved from the live kernel probe.
86    pub features: BackendFeatures,
87}
88
89/// Granular feature bits derived from the kernel probe.
90#[derive(Debug, Clone, Copy, Default)]
91pub struct BackendFeatures {
92    /// FS write allowlist enforceable.
93    pub fs_write: bool,
94    /// FS read denylist enforceable.
95    pub fs_read: bool,
96    /// Per-path exec allowlist enforceable.
97    pub exec_allowlist: bool,
98    /// Outbound TCP can be pinned to specific port(s).
99    pub net_port_filter: bool,
100    /// `--audit` streaming of violation events supported.
101    pub audit_stream: bool,
102}
103
104/// Runtime knobs the CLI feeds into `Sandbox::new_with_options`.
105#[derive(Debug, Clone, Copy, Default)]
106pub struct BackendOptions {
107    /// Proceed even when the backend cannot fully enforce the requested
108    /// profile (e.g., Landlock without ABI v4 net support). The backend
109    /// prints a single warning line naming the missing capability.
110    pub allow_degraded: bool,
111}