Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::caps::CapabilityBit;
use crate::namespace::Namespace;
use crate::seccomp::SeccompFilter;
use anyhow::{Result, bail};
use libc::{gid_t, pid_t, uid_t};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -74,6 +75,12 @@ pub struct ExecutableSpec {
/// If `true`, sets `PR_SET_NO_NEW_PRIVS` before
/// spawning the target executable.
pub no_new_privs: bool,

/// An optional seccomp-bpf filter program. Applied after capabilities
/// are set and `PR_SET_NO_NEW_PRIVS` is enabled, but before `execvpe()`.
/// Requires `no_new_privs = true`.
#[serde(default)]
pub seccomp: Option<SeccompFilter>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod config;
pub mod mount;
pub mod namespace;
pub mod runner;
pub mod seccomp;
pub mod signal;
pub mod unshare;
pub mod wrap;
42 changes: 42 additions & 0 deletions src/seccomp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/// A seccomp-bpf filter program.
///
/// The caller builds the BPF program as a list of `(code, jt, jf, k)`
/// instructions. Styrolite installs it via `seccomp(2)` after
/// capabilities are set but before `execvpe()`.
///
/// Requires `no_new_privs = true` on the `ExecutableSpec`.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct SeccompFilter {
/// BPF instructions as `(code, jt, jf, k)` tuples.
pub instructions: Vec<(u16, u8, u8, u32)>,
}

impl SeccompFilter {
/// Install the seccomp filter via `seccomp(2)` with
/// `SECCOMP_FILTER_FLAG_TSYNC`.
///
/// Uses `seccomp(2)` instead of `prctl(PR_SET_SECCOMP)` to synchronize the
/// filter across all threads via `SECCOMP_FILTER_FLAG_TSYNC`.
///
/// # Safety
///
/// Must be called after `prctl(PR_SET_NO_NEW_PRIVS, 1)` and before
/// `execvpe()`. The caller must ensure the BPF program is valid.
pub unsafe fn install(&self) -> std::io::Result<()> {
let filters: Vec<libc::sock_filter> = self
.instructions
.iter()
.map(|&(code, jt, jf, k)| libc::sock_filter { code, jt, jf, k })
.collect();
let prog = libc::sock_fprog {
len: filters.len() as u16,
filter: filters.as_ptr() as *mut _,
};

let ret = unsafe { libc::syscall(libc::SYS_seccomp, 1u64, 1u64, &prog as *const _) };
if ret != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
}
7 changes: 7 additions & 0 deletions src/wrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,13 @@ impl ExecutableSpec {
self.set_no_new_privs()?;
}

if let Some(filter) = &self.seccomp {
if !self.no_new_privs {
bail!("seccomp filter requires no_new_privs = true");
}
unsafe { filter.install()? };
}

unsafe {
if libc::execvpe(
program_cstring.as_ptr(),
Expand Down
Loading