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}