use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SandboxPolicy {
pub seccomp_enabled: bool,
pub seccomp_profile: Option<String>,
pub landlock_rules: Vec<LandlockRule>,
pub network: NetworkPolicy,
pub read_only_rootfs: bool,
pub memory_limit_mb: Option<u64>,
pub cpu_limit: Option<f64>,
pub max_pids: Option<u32>,
pub data_dir: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SeccompProfile {
pub name: String,
pub allowed_syscalls: Vec<String>,
pub log_blocked: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LandlockRule {
pub path: String,
pub access: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct NetworkPolicy {
pub enabled: bool,
pub allowed_hosts: Vec<String>,
pub allowed_ports: Vec<u16>,
}
impl SandboxPolicy {
pub fn minimal() -> Self {
Self::default()
}
pub fn basic() -> Self {
Self {
seccomp_enabled: true,
seccomp_profile: Some("basic".into()),
network: NetworkPolicy {
enabled: false,
..Default::default()
},
..Default::default()
}
}
pub fn strict() -> Self {
Self {
seccomp_enabled: true,
seccomp_profile: Some("strict".into()),
network: NetworkPolicy {
enabled: false,
..Default::default()
},
read_only_rootfs: true,
memory_limit_mb: Some(512),
cpu_limit: Some(1.0),
max_pids: Some(64),
..Default::default()
}
}
pub fn from_preset(name: &str) -> std::result::Result<Self, String> {
match name.to_lowercase().as_str() {
"minimal" => Ok(Self::minimal()),
"basic" => Ok(Self::basic()),
"strict" => Ok(Self::strict()),
other => Err(format!(
"unknown policy: {other} (use minimal, basic, strict)"
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn minimal_policy() {
let p = SandboxPolicy::minimal();
assert!(!p.seccomp_enabled);
assert!(!p.network.enabled);
assert!(!p.read_only_rootfs);
}
#[test]
fn basic_policy() {
let p = SandboxPolicy::basic();
assert!(p.seccomp_enabled);
assert!(!p.network.enabled);
}
#[test]
fn strict_policy() {
let p = SandboxPolicy::strict();
assert!(p.seccomp_enabled);
assert!(p.read_only_rootfs);
assert!(p.memory_limit_mb.is_some());
assert!(p.max_pids.is_some());
}
#[test]
fn from_preset_all() {
assert!(
!SandboxPolicy::from_preset("minimal")
.unwrap()
.seccomp_enabled
);
assert!(SandboxPolicy::from_preset("basic").unwrap().seccomp_enabled);
assert!(
SandboxPolicy::from_preset("strict")
.unwrap()
.read_only_rootfs
);
}
#[test]
fn from_preset_case_insensitive() {
assert!(
SandboxPolicy::from_preset("STRICT")
.unwrap()
.read_only_rootfs
);
assert!(SandboxPolicy::from_preset("Basic").unwrap().seccomp_enabled);
}
#[test]
fn from_preset_unknown() {
let err = SandboxPolicy::from_preset("unknown").unwrap_err();
assert!(err.contains("unknown policy"));
}
#[test]
fn serde_roundtrip() {
let p = SandboxPolicy::strict();
let json = serde_json::to_string(&p).unwrap();
let back: SandboxPolicy = serde_json::from_str(&json).unwrap();
assert!(back.seccomp_enabled);
assert!(back.read_only_rootfs);
}
}