use clap::ValueEnum;
use libseccomp::{ScmpAction, ScmpArgCompare, ScmpCompareOp, ScmpFilterContext, ScmpSyscall};
use nix::libc;
#[derive(ValueEnum, Clone, Debug)]
pub enum SeccompRuleName {
CCpp,
CCppFileIO,
Golang,
Node,
General,
}
pub fn load_seccomp_rules(rule_name: &SeccompRuleName) -> Result<(), ()> {
match rule_name {
SeccompRuleName::CCpp => c_cpp_seccomp_rules(false),
SeccompRuleName::CCppFileIO => c_cpp_seccomp_rules(true),
SeccompRuleName::Golang => golang_seccomp_rules(),
SeccompRuleName::Node => node_seccomp_rules(),
SeccompRuleName::General => general_seccomp_rules(),
}
}
fn c_cpp_seccomp_rules(allow_write_file: bool) -> Result<(), ()> {
let syscalls_whitelist = [
"access",
"arch_prctl",
"brk",
"clock_gettime",
"close",
"exit_group",
"faccessat",
"fstat",
"futex",
"getrandom",
"lseek",
"mmap",
"mprotect",
"munmap",
"newfstatat",
"pread64",
"prlimit64",
"read",
"readlink",
"readv",
"rseq",
"set_robust_list",
"set_tid_address",
"write",
"writev",
"execve",
];
let mut filter = ScmpFilterContext::new(ScmpAction::KillProcess).map_err(|_| ())?;
apply_seccomp_filter(&mut filter, &syscalls_whitelist, ScmpAction::Allow)?;
if allow_write_file {
for name in ["open", "openat", "dup", "dup2", "dup3"].iter() {
let syscall = ScmpSyscall::from_name(name).map_err(|_| ())?;
filter
.add_rule(ScmpAction::Allow, syscall)
.map_err(|_| ())?;
}
} else {
let open_sys = ScmpSyscall::from_name("open").map_err(|_| ())?;
let cmp_open = ScmpArgCompare::new(
1,
ScmpCompareOp::MaskedEqual((libc::O_WRONLY | libc::O_RDWR) as u64),
0,
);
filter
.add_rule_conditional(ScmpAction::Allow, open_sys, &[cmp_open])
.map_err(|_| ())?;
let openat_sys = ScmpSyscall::from_name("openat").map_err(|_| ())?;
let cmp_openat = ScmpArgCompare::new(
2,
ScmpCompareOp::MaskedEqual((libc::O_WRONLY | libc::O_RDWR) as u64),
0,
);
filter
.add_rule_conditional(ScmpAction::Allow, openat_sys, &[cmp_openat])
.map_err(|_| ())?;
}
filter.load().map_err(|_| ())?;
Ok(())
}
fn golang_seccomp_rules() -> Result<(), ()> {
let syscalls_blacklist = ["socket", "fork", "vfork", "kill", "execveat"];
let mut filter = ScmpFilterContext::new(ScmpAction::Allow).map_err(|_| ())?;
apply_seccomp_filter(&mut filter, &syscalls_blacklist, ScmpAction::KillProcess)?;
filter.load().map_err(|_| ())?;
Ok(())
}
fn node_seccomp_rules() -> Result<(), ()> {
let syscalls_blacklist = ["socket", "fork", "vfork", "kill", "execveat"];
let mut filter = ScmpFilterContext::new(ScmpAction::Allow).map_err(|_| ())?;
for syscall_name in syscalls_blacklist.iter() {
let syscall = ScmpSyscall::from_name(syscall_name).map_err(|_| ())?;
filter
.add_rule(ScmpAction::KillProcess, syscall)
.map_err(|_| ())?;
}
filter.load().map_err(|_| ())?;
Ok(())
}
fn general_seccomp_rules() -> Result<(), ()> {
let syscalls_blacklist = ["clone", "fork", "vfork", "kill", "execveat"];
let mut filter = ScmpFilterContext::new(ScmpAction::Allow).map_err(|_| ())?;
apply_seccomp_filter(&mut filter, &syscalls_blacklist, ScmpAction::KillProcess)?;
let socket_sys = ScmpSyscall::from_name("socket").map_err(|_| ())?;
filter
.add_rule(ScmpAction::KillProcess, socket_sys)
.map_err(|_| ())?;
let open_sys = ScmpSyscall::from_name("open").map_err(|_| ())?;
let cmp_open_w = ScmpArgCompare::new(
1,
ScmpCompareOp::MaskedEqual(libc::O_WRONLY as u64),
libc::O_WRONLY as u64,
);
filter
.add_rule_conditional(ScmpAction::KillProcess, open_sys, &[cmp_open_w])
.map_err(|_| ())?;
let cmp_open_rw = ScmpArgCompare::new(
1,
ScmpCompareOp::MaskedEqual(libc::O_RDWR as u64),
libc::O_RDWR as u64,
);
filter
.add_rule_conditional(ScmpAction::KillProcess, open_sys, &[cmp_open_rw])
.map_err(|_| ())?;
let openat_sys = ScmpSyscall::from_name("openat").map_err(|_| ())?;
let cmp_openat_w = ScmpArgCompare::new(
2,
ScmpCompareOp::MaskedEqual(libc::O_WRONLY as u64),
libc::O_WRONLY as u64,
);
filter
.add_rule_conditional(ScmpAction::KillProcess, openat_sys, &[cmp_openat_w])
.map_err(|_| ())?;
let cmp_openat_rw = ScmpArgCompare::new(
2,
ScmpCompareOp::MaskedEqual(libc::O_RDWR as u64),
libc::O_RDWR as u64,
);
filter
.add_rule_conditional(ScmpAction::KillProcess, openat_sys, &[cmp_openat_rw])
.map_err(|_| ())?;
filter.load().map_err(|_| ())?;
Ok(())
}
fn apply_seccomp_filter(
filter: &mut ScmpFilterContext,
sys_calls: &[&str],
action: ScmpAction,
) -> Result<(), ()> {
Ok(for syscall_name in sys_calls.iter() {
let syscall = ScmpSyscall::from_name(syscall_name).map_err(|_| ())?;
filter.add_rule(action, syscall).map_err(|_| ())?;
})
}