use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct HealthCheckOptions {
#[serde(default = "default_health_interval")]
pub interval: Duration,
#[serde(default = "default_health_timeout")]
pub timeout: Duration,
#[serde(default = "default_health_retries")]
pub retries: u32,
#[serde(default = "default_health_start_period")]
pub start_period: Duration,
}
fn default_health_interval() -> Duration {
Duration::from_secs(30)
}
fn default_health_timeout() -> Duration {
Duration::from_secs(10)
}
fn default_health_retries() -> u32 {
3
}
fn default_health_start_period() -> Duration {
Duration::from_secs(60)
}
impl Default for HealthCheckOptions {
fn default() -> Self {
Self {
interval: default_health_interval(),
timeout: default_health_timeout(),
retries: default_health_retries(),
start_period: default_health_start_period(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecurityOptions {
#[serde(default = "default_jailer_enabled")]
pub jailer_enabled: bool,
#[serde(default = "default_seccomp_enabled")]
pub seccomp_enabled: bool,
#[serde(default)]
pub uid: Option<u32>,
#[serde(default)]
pub gid: Option<u32>,
#[serde(default)]
pub new_pid_ns: bool,
#[serde(default)]
pub new_net_ns: bool,
#[serde(default = "default_chroot_base")]
pub chroot_base: PathBuf,
#[serde(default = "default_chroot_enabled")]
pub chroot_enabled: bool,
#[serde(default = "default_close_fds")]
pub close_fds: bool,
#[serde(default = "default_sanitize_env")]
pub sanitize_env: bool,
#[serde(default = "default_env_allowlist")]
pub env_allowlist: Vec<String>,
#[serde(default)]
pub resource_limits: ResourceLimits,
#[serde(default)]
pub sandbox_profile: Option<PathBuf>,
#[serde(default = "default_network_enabled")]
pub network_enabled: bool,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ResourceLimits {
#[serde(default)]
pub max_open_files: Option<u64>,
#[serde(default)]
pub max_file_size: Option<u64>,
#[serde(default)]
pub max_processes: Option<u64>,
#[serde(default)]
pub max_memory: Option<u64>,
#[serde(default)]
pub max_cpu_time: Option<u64>,
}
fn default_jailer_enabled() -> bool {
cfg!(target_os = "macos")
}
fn default_seccomp_enabled() -> bool {
false
}
fn default_chroot_base() -> PathBuf {
PathBuf::from("/srv/boxlite")
}
fn default_chroot_enabled() -> bool {
cfg!(target_os = "linux")
}
fn default_close_fds() -> bool {
true
}
fn default_sanitize_env() -> bool {
true
}
fn default_env_allowlist() -> Vec<String> {
vec![
"RUST_LOG".to_string(),
"PATH".to_string(),
"HOME".to_string(),
"USER".to_string(),
"LANG".to_string(),
"TERM".to_string(),
]
}
fn default_network_enabled() -> bool {
true
}
impl Default for SecurityOptions {
fn default() -> Self {
Self {
jailer_enabled: default_jailer_enabled(),
seccomp_enabled: default_seccomp_enabled(),
uid: None,
gid: None,
new_pid_ns: false,
new_net_ns: false,
chroot_base: default_chroot_base(),
chroot_enabled: default_chroot_enabled(),
close_fds: default_close_fds(),
sanitize_env: default_sanitize_env(),
env_allowlist: default_env_allowlist(),
resource_limits: ResourceLimits::default(),
sandbox_profile: None,
network_enabled: default_network_enabled(),
}
}
}
impl SecurityOptions {
pub fn development() -> Self {
Self {
jailer_enabled: false,
seccomp_enabled: false,
chroot_enabled: false,
close_fds: false,
sanitize_env: false,
..Default::default()
}
}
pub fn standard() -> Self {
Self {
jailer_enabled: cfg!(any(target_os = "linux", target_os = "macos")),
seccomp_enabled: cfg!(target_os = "linux"),
..Default::default()
}
}
pub fn maximum() -> Self {
Self {
jailer_enabled: true,
seccomp_enabled: cfg!(target_os = "linux"),
uid: Some(65534), gid: Some(65534), new_pid_ns: cfg!(target_os = "linux"),
new_net_ns: false, chroot_enabled: cfg!(target_os = "linux"),
close_fds: true,
sanitize_env: true,
env_allowlist: vec!["RUST_LOG".to_string()],
resource_limits: ResourceLimits {
max_open_files: Some(1024),
max_file_size: Some(1024 * 1024 * 1024), max_processes: Some(100),
max_memory: None, max_cpu_time: None, },
..Default::default()
}
}
pub fn is_full_isolation_available() -> bool {
cfg!(target_os = "linux")
}
pub fn builder() -> SecurityOptionsBuilder {
SecurityOptionsBuilder::new()
}
}
#[derive(Debug, Clone)]
pub struct SecurityOptionsBuilder {
inner: SecurityOptions,
}
impl Default for SecurityOptionsBuilder {
fn default() -> Self {
Self::new()
}
}
impl SecurityOptionsBuilder {
pub fn new() -> Self {
Self {
inner: SecurityOptions::default(),
}
}
pub fn development() -> Self {
Self {
inner: SecurityOptions::development(),
}
}
pub fn standard() -> Self {
Self {
inner: SecurityOptions::standard(),
}
}
pub fn maximum() -> Self {
Self {
inner: SecurityOptions::maximum(),
}
}
pub fn jailer_enabled(&mut self, enabled: bool) -> &mut Self {
self.inner.jailer_enabled = enabled;
self
}
pub fn seccomp_enabled(&mut self, enabled: bool) -> &mut Self {
self.inner.seccomp_enabled = enabled;
self
}
pub fn uid(&mut self, uid: u32) -> &mut Self {
self.inner.uid = Some(uid);
self
}
pub fn gid(&mut self, gid: u32) -> &mut Self {
self.inner.gid = Some(gid);
self
}
pub fn new_pid_ns(&mut self, enabled: bool) -> &mut Self {
self.inner.new_pid_ns = enabled;
self
}
pub fn new_net_ns(&mut self, enabled: bool) -> &mut Self {
self.inner.new_net_ns = enabled;
self
}
pub fn chroot_base(&mut self, path: impl Into<PathBuf>) -> &mut Self {
self.inner.chroot_base = path.into();
self
}
pub fn chroot_enabled(&mut self, enabled: bool) -> &mut Self {
self.inner.chroot_enabled = enabled;
self
}
pub fn close_fds(&mut self, enabled: bool) -> &mut Self {
self.inner.close_fds = enabled;
self
}
pub fn sanitize_env(&mut self, enabled: bool) -> &mut Self {
self.inner.sanitize_env = enabled;
self
}
pub fn env_allowlist(&mut self, vars: Vec<String>) -> &mut Self {
self.inner.env_allowlist = vars;
self
}
pub fn allow_env(&mut self, var: impl Into<String>) -> &mut Self {
self.inner.env_allowlist.push(var.into());
self
}
pub fn resource_limits(&mut self, limits: ResourceLimits) -> &mut Self {
self.inner.resource_limits = limits;
self
}
pub fn max_open_files(&mut self, limit: u64) -> &mut Self {
self.inner.resource_limits.max_open_files = Some(limit);
self
}
pub fn max_file_size_bytes(&mut self, bytes: u64) -> &mut Self {
self.inner.resource_limits.max_file_size = Some(bytes);
self
}
pub fn max_processes(&mut self, limit: u64) -> &mut Self {
self.inner.resource_limits.max_processes = Some(limit);
self
}
pub fn max_memory_bytes(&mut self, bytes: u64) -> &mut Self {
self.inner.resource_limits.max_memory = Some(bytes);
self
}
pub fn max_cpu_time_seconds(&mut self, seconds: u64) -> &mut Self {
self.inner.resource_limits.max_cpu_time = Some(seconds);
self
}
pub fn sandbox_profile(&mut self, path: impl Into<PathBuf>) -> &mut Self {
self.inner.sandbox_profile = Some(path.into());
self
}
pub fn network_enabled(&mut self, enabled: bool) -> &mut Self {
self.inner.network_enabled = enabled;
self
}
pub fn build(&self) -> SecurityOptions {
self.inner.clone()
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct AdvancedBoxOptions {
#[serde(default)]
pub security: SecurityOptions,
#[serde(default)]
pub isolate_mounts: bool,
#[serde(default)]
pub health_check: Option<HealthCheckOptions>,
}