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};
20use serde::{Deserialize, Serialize};
21
22#[cfg(target_os = "macos")]
23mod macos;
24#[cfg(target_os = "macos")]
25pub use macos::MacosSandbox as Sandbox;
26#[cfg(target_os = "macos")]
27pub use macos::sbpl;
28
29#[cfg(target_os = "linux")]
30mod linux;
31#[cfg(target_os = "linux")]
32pub use linux::LinuxSandbox as Sandbox;
33#[cfg(target_os = "linux")]
34pub use linux::maybe_run_launcher;
35#[cfg(target_os = "linux")]
36pub use linux::policy;
37
38/// A platform-specific sandbox backend.
39///
40/// Implementations turn a resolved [`SandboxProfile`] into kernel-enforced
41/// restrictions on a spawned child process. The orchestrator holds the
42/// concrete `Sandbox` re-export (never `dyn SandboxBackend`); object safety
43/// is intentionally not required.
44pub trait SandboxBackend: Send + Sync {
45 /// Human-readable backend identifier, e.g. `"sandbox-exec"` or
46 /// `"landlock+seccomp"`. Surfaced in `sbe inspect` output and audit logs.
47 fn name(&self) -> &'static str;
48
49 /// What this backend can enforce on the current kernel/host. Populated
50 /// during construction (`Self::new` performs the kernel probe); this
51 /// accessor is infallible.
52 fn info(&self) -> &BackendInfo;
53
54 /// Render the resolved policy for `--dry-run` and `sbe inspect`. Output
55 /// must be deterministic and platform-stable so tests can assert on
56 /// substrings.
57 ///
58 /// - macOS: canonical SBPL Scheme document.
59 /// - Linux: a YAML document listing the Landlock ruleset, the seccomp action table, the proxy
60 /// env, and the resolved [`BackendFeatures`].
61 fn render_policy(
62 &self,
63 profile: &SandboxProfile,
64 proxy_port: Option<u16>,
65 ) -> Result<String, CoreError>;
66
67 /// Run the user command under the sandbox and return its exit status.
68 ///
69 /// The backend owns the per-invocation lifecycle: compile profile →
70 /// platform artifact (SBPL tempfile / Ruleset+BpfProgram), spawn child
71 /// with policy applied before `execve`, wait, clean up.
72 ///
73 /// Proxy lifecycle, audit logging, config resolution and exit-code
74 /// mapping live in the orchestrator. `extra_env` is the
75 /// orchestrator-merged environment (proxy vars + `profile.env`).
76 fn run(
77 &self,
78 profile: &SandboxProfile,
79 proxy_port: Option<u16>,
80 command: &[String],
81 extra_env: &HashMap<String, String>,
82 pid_tx: Option<tokio::sync::oneshot::Sender<u32>>,
83 ) -> impl std::future::Future<Output = Result<ExitStatus, CoreError>> + Send;
84}
85
86/// What a backend can enforce on the current kernel/host.
87#[derive(Debug, Clone)]
88pub struct BackendInfo {
89 /// Backend identifier; matches [`SandboxBackend::name`].
90 pub name: &'static str,
91 /// Kernel version string for diagnostics; e.g. "Darwin 24.6.0" or "Linux 6.8.0".
92 pub kernel: String,
93 /// Feature flags resolved from the live kernel probe.
94 pub features: BackendFeatures,
95}
96
97/// Granular feature bits derived from the kernel probe.
98#[derive(Debug, Clone, Copy, Default)]
99pub struct BackendFeatures {
100 /// FS write allowlist enforceable.
101 pub fs_write: bool,
102 /// FS read denylist enforceable.
103 pub fs_read: bool,
104 /// Per-path exec allowlist enforceable.
105 pub exec_allowlist: bool,
106 /// Outbound TCP can be pinned to specific port(s).
107 pub net_port_filter: bool,
108 /// `--audit` streaming of violation events supported.
109 pub audit_stream: bool,
110}
111
112/// Runtime knobs the CLI feeds into `Sandbox::new_with_options`.
113#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
114pub struct BackendOptions {
115 /// Legacy compatibility bit retained for launcher payload compatibility.
116 /// It maps only to insecure Linux network compatibility.
117 pub allow_degraded: bool,
118
119 /// Explicit opt-in to Linux's destination-port-only compatibility mode.
120 /// This never disables filesystem or privilege-escalation lints.
121 pub allow_insecure_network: bool,
122}