What Is the Role of Seccomp in CubeSandbox's Security Model?
Seccomp acts as the primary host-side sandboxing mechanism in CubeSandbox, enforcing per-thread whitelist of Linux syscalls to block unauthorized operations and minimize kernel attack surface.
CubeSandbox, TencentCloud's open-source sandboxing solution, relies on seccomp (secure computing mode) to harden the Virtual Machine Monitor (VMM) process. By installing BPF filters that restrict which system calls each thread can invoke, the project implements a least-privilege security model that isolates guest workloads from the host operating system.
Per-Thread Syscall Filtering
The security model applies per-thread rule selection rather than a global process policy. In hypervisor/vmm/src/seccomp_filters.rs (lines 21-28), the Thread enum defines distinct groups for the VMM process:
pub enum Thread {
Api,
SignalHandler,
Vcpu,
Vmm,
PtyForeground,
All,
}
Each variant maps to a specific vector of allowed syscalls. For example, the signal-handler thread receives only the minimal ioctls required for terminal management, while the VMM thread receives broader—but still strictly enumerated—rules for hypervisor operations. This separation ensures that a compromised API thread cannot invoke dangerous hypervisor commands reserved for the VMM thread.
Hypervisor-Specific Rule Construction
Rules are constructed using the and! and or! macros (lines 35-46 in hypervisor/vmm/src/seccomp_filters.rs) to build complex SeccompRule objects. These rules match syscall numbers and arguments, such as allowing ioctl only with specific hypervisor-dependent request codes.
The functions create_vmm_ioctl_seccomp_rule_common_kvm() and create_vmm_ioctl_seccomp_rule_common_mshv() enumerate exact ioctl numbers permitted for KVM and MSHV respectively (lines 10-65):
fn create_vmm_ioctl_seccomp_rule_common_kvm() -> Vec<SeccompRule> {
// Whitelist specific KVM ioctls
vec![
allow_ioctl(KVM_CREATE_VM),
allow_ioctl(KVM_CREATE_VCPU),
// ... additional allowed ioctls
]
}
This approach ensures that even if a guest compromises the VMM, it cannot invoke arbitrary hypervisor commands or escalate privileges through unapproved kernel interfaces.
BPF Filter Generation and Application
The thread_rules() function (lines 440-574 in hypervisor/vmm/src/seccomp_filters.rs) aggregates rules for all thread types into a single vector of (syscall, Vec<SeccompRule>) tuples. The get_seccomp_filter() function (lines 998-1046) then translates these rules into a BPF program based on the configured SeccompAction:
- Allow: Permits the syscall (used for debugging)
- Log: Records violations without blocking (audit mode)
- KillProcess: Terminates the VMM immediately upon violation (production hardening)
When a VM initializes, Vm::new_from_memory_manager() stores the chosen action in seccomp_action. During startup or in Vm::run(), the filter is applied via apply_filter(&filter) (referenced in hypervisor/vmm/src/vm.rs lines 71-78):
impl Vm {
fn run(&self) -> Result<()> {
let filter = get_seccomp_filter(&self.seccomp_action, Thread::All, self.hypervisor_type)?;
apply_filter(&filter)?;
// VM execution proceeds under seccomp restrictions
}
}
Runtime Extensibility
CubeSandbox supports dynamic rule injection through set_runtime_seccomp_rules() (lines 776-782 in hypervisor/vmm/src/seccomp_filters.rs). This allows components like the Cubelet agent to append additional syscall allowances after the VM process has started, accommodating hot-plug devices or changing workload requirements without restarting the VMM.
Code Examples
The following examples demonstrate how seccomp configuration flows from the orchestration layer down to the VMM implementation.
Configuring Seccomp Action in Go
The Cubelet component specifies the enforcement level when creating a VM:
cfg := cubepb.VmConfig{
SeccompAction: cubepb.SeccompAction_KILL_PROCESS,
// Additional configuration...
}
vm, err := cubelet.StartVm(cfg)
Applying the Filter in Rust
The VMM receives the action and installs the filter:
use seccompiler::{apply_filter, SeccompAction};
fn apply_vm_seccomp(action: &SeccompAction, hypervisor: HypervisorType) -> Result<()> {
let filter = get_seccomp_filter(action, Thread::All, hypervisor)?;
apply_filter(&filter).map_err(|e| Error::ApplySeccompFilter(e))?;
Ok(())
}
Adding Runtime Rules
Components can inject additional allowances:
use hypervisor::vmm::seccomp_filters::{set_runtime_seccomp_rules, SeccompRule, SeccompCondition};
fn allow_runtime_ioctl() {
let cond = SeccompCondition::new(1, ArgLen::Dword, Eq, KVM_IOCTL_MAGIC).unwrap();
let rule = SeccompRule::new(vec![cond]).unwrap();
set_runtime_seccomp_rules(vec![(libc::SYS_ioctl, vec![rule])]);
}
Summary
- Seccomp provides the foundational host-side isolation in CubeSandbox by whitelisting syscalls per VMM thread.
- Per-thread granularity minimizes attack surfaces by restricting the signal-handler, API, and VCPU threads to only their essential operations.
- Hypervisor-specific rules in
hypervisor/vmm/src/seccomp_filters.rsstrictly control KVM and MSHV ioctl commands. - Configurable enforcement via
SeccompActionsupports debugging (Allow), auditing (Log), and strict production modes (KillProcess). - Runtime extensibility through
set_runtime_seccomp_rules()enables dynamic security policy updates without VM restarts.
Frequently Asked Questions
What happens when CubeSandbox blocks a syscall?
When the seccomp filter encounters a non-whitelisted syscall, the configured SeccompAction determines the response. If set to KillProcess (the production default), the kernel immediately terminates the VMM process. If set to Log, the violation is recorded for auditing while the syscall returns an error code to the caller.
Can seccomp rules be modified after a VM starts?
Yes. CubeSandbox exposes set_runtime_seccomp_rules() in hypervisor/vmm/src/seccomp_filters.rs, allowing the Cubelet agent or other components to inject additional syscall rules into the global runtime list. These rules are merged into the filter during the next applicable thread execution, supporting hot-plug scenarios without requiring a VM restart.
How does CubeSandbox handle different hypervisors like KVM versus MSHV?
The codebase defines hypervisor-specific rule constructors such as create_vmm_ioctl_seccomp_rule_common_kvm() and create_vmm_ioctl_seccomp_rule_common_mshv() in hypervisor/vmm/src/seccomp_filters.rs. Each function enumerates the exact ioctl numbers permitted for its respective hypervisor type, ensuring the VMM can only issue commands relevant to the underlying virtualization technology.
Why does CubeSandbox use per-thread seccomp rules instead of a single process filter?
Different VMM threads have distinct responsibilities requiring different kernel interfaces. The signal-handler thread only needs terminal ioctls, while the VMM thread requires hypervisor-specific ioctls. By separating rules via the Thread enum, CubeSandbox applies the principle of least privilege—each thread can access only the minimal syscall set necessary for its function, reducing the blast radius of a compromised thread.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →