From 6f82216e4bc1062dca52c185986396165295181d Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 13 Sep 2023 21:27:54 +0200 Subject: [PATCH] Add namespace support to Linux sandbox The previous Linux sandbox would allow for access of abstract namespace sockets and handled network sandboxing through fragile seccomp rules. To both simplify the code and improve our sandboxing, this patch introduces the usage of Linux namespaces to both clear the abstract namespace and create a network namespace. It is now necessary to always lock down the sandbox in a newly created process, since user namespaces cannot be created from multi-threaded applications and require writing to `/proc/self/*id_map` exactly once (every following write from the same process is a permission error). This patch completely removes all seccomp code, however some part of it might still be useful for future sandboxing improvements. --- Cargo.toml | 39 +++- src/error.rs | 27 --- src/lib.rs | 8 +- src/linux/mod.rs | 103 +++++++++- src/linux/seccomp.rs | 460 ------------------------------------------ tests/canonicalize.rs | 3 +- tests/env.rs | 3 +- tests/exec.rs | 3 +- tests/fs.rs | 3 +- tests/full_env.rs | 3 +- tests/full_sandbox.rs | 15 +- tests/net.rs | 90 +-------- 12 files changed, 152 insertions(+), 605 deletions(-) delete mode 100644 src/linux/seccomp.rs diff --git a/Cargo.toml b/Cargo.toml index 6ea7df7..840d749 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,48 @@ rust-version = "1.63.0" license = "GPL-3.0-or-later" edition = "2021" +[[test]] +name = "canonicalize" +path = "tests/canonicalize.rs" +harness = false + +[[test]] +name = "env" +path = "tests/env.rs" +harness = false + +[[test]] +name = "exec" +path = "tests/exec.rs" +harness = false + +[[test]] +name = "fs" +path = "tests/fs.rs" +harness = false + +[[test]] +name = "full_env" +path = "tests/full_env.rs" +harness = false + +[[test]] +name = "full_sandbox" +path = "tests/full_sandbox.rs" +harness = false + +[[test]] +name = "net" +path = "tests/net.rs" +harness = false + [target.'cfg(target_os = "linux")'.dependencies] -seccompiler = "0.2.0" landlock = "0.2.0" libc = "0.2.132" [dev-dependencies] clap = { version = "3.2.17", features = ["derive"] } tempfile = "3.3.0" + +[dependencies] +bitflags = "2.4.0" diff --git a/src/error.rs b/src/error.rs index 3def9c5..016444d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,15 +4,11 @@ use std::error::Error as StdError; #[cfg(target_os = "macos")] use std::ffi::OsString; use std::fmt::{self, Display, Formatter}; -#[cfg(target_os = "macos")] use std::io::Error as IoError; use std::result::Result as StdResult; #[cfg(target_os = "linux")] use landlock::{PathFdError, RulesetError}; -#[cfg(target_os = "linux")] -#[cfg(target_os = "linux")] -use seccompiler::{BackendError, Error as SeccompError}; /// Birdcage result type. pub type Result = StdResult; @@ -24,10 +20,6 @@ pub enum Error { #[cfg(target_os = "linux")] Ruleset(RulesetError), - /// Seccomp errors. - #[cfg(target_os = "linux")] - Seccomp(SeccompError), - /// Invalid sandbox exception path. #[cfg(target_os = "linux")] InvalidPath(PathFdError), @@ -35,7 +27,6 @@ pub enum Error { InvalidPath(InvalidPathError), /// I/O error. - #[cfg(target_os = "macos")] Io(IoError), /// Sandbox activation failed. @@ -50,12 +41,9 @@ impl Display for Error { #[cfg(target_os = "linux")] Self::Ruleset(error) => write!(f, "landlock ruleset error: {error}"), #[cfg(target_os = "linux")] - Self::Seccomp(error) => write!(f, "seccomp error: {error}"), - #[cfg(target_os = "linux")] Self::InvalidPath(error) => write!(f, "invalid path: {error}"), #[cfg(target_os = "macos")] Self::InvalidPath(error) => write!(f, "invalid path: {error:?}"), - #[cfg(target_os = "macos")] Self::Io(error) => write!(f, "input/output error: {error}"), Self::ActivationFailed(error) => { write!(f, "failed to initialize a sufficient sandbox: {error}") @@ -71,20 +59,6 @@ impl From for Error { } } -#[cfg(target_os = "linux")] -impl From for Error { - fn from(error: SeccompError) -> Self { - Self::Seccomp(error) - } -} - -#[cfg(target_os = "linux")] -impl From for Error { - fn from(error: BackendError) -> Self { - Self::Seccomp(SeccompError::Backend(error)) - } -} - #[cfg(target_os = "linux")] impl From for Error { fn from(error: PathFdError) -> Self { @@ -99,7 +73,6 @@ impl From for Error { } } -#[cfg(target_os = "macos")] impl From for Error { fn from(error: IoError) -> Self { Self::Io(error) diff --git a/src/lib.rs b/src/lib.rs index b47e8ef..08f773a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,13 @@ pub trait Sandbox: Sized { /// prohibit access to this resource without creating a new sandbox. fn add_exception(&mut self, exception: Exception) -> Result<&mut Self>; - /// Apply the sandbox restrictions to the current thread. + /// Apply the sandbox restrictions to the current process. + /// + /// # Errors + /// + /// Sandboxing will fail if the calling process is not single-threaded, or + /// has previously been sandboxed. It is recommended to spawn a new process + /// before sandboxing to avoid these issues. fn lock(self) -> Result<()>; } diff --git a/src/linux/mod.rs b/src/linux/mod.rs index 400c374..2a8bfd5 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -1,23 +1,24 @@ //! Linux sandboxing. //! //! This module implements sandboxing on Linux based on the Landlock LSM, -//! combined with seccomp for anything other than the filesystem. +//! combined with namespaces for network filtering. +use std::fs; +use std::io::Error as IoError; + +use bitflags::bitflags; use landlock::{ make_bitflags, Access, AccessFs, Compatible, PathBeneath, PathFd, Ruleset, RulesetAttr, RulesetCreated, RulesetCreatedAttr, RulesetStatus, ABI as LANDLOCK_ABI, }; use crate::error::{Error, Result}; -use crate::linux::seccomp::NetworkFilter; use crate::{Exception, Sandbox}; -mod seccomp; - /// Minimum landlock ABI version. const ABI: LANDLOCK_ABI = LANDLOCK_ABI::V1; -/// Linux sandboxing based on Landlock and Seccomp. +/// Linux sandboxing. pub struct LinuxSandbox { env_exceptions: Vec, landlock: RulesetCreated, @@ -69,9 +70,13 @@ impl Sandbox for LinuxSandbox { crate::restrict_env_variables(&self.env_exceptions); } - // Create and apply seccomp filter. + // Enter a user namespace to unbind abstract namespace sockets. + create_user_namespace(false)?; + + // Create network namespace. if !self.allow_networking { - NetworkFilter::apply()?; + create_user_namespace(true)?; + unshare(Namespaces::NETWORK)?; } // Apply landlock rules. @@ -85,3 +90,87 @@ impl Sandbox for LinuxSandbox { } } } + +/// Create a new user namespace. +/// +/// If the `become_root` flag is set, then the current user will be mapped to +/// UID 0 inside the namespace. Otherwise the current user will be mapped to its +/// UID of the parent namespace. +pub(crate) fn create_user_namespace(become_root: bool) -> Result<()> { + // Get the current UID. + let uid = unsafe { libc::getuid() }; + + // Create the namespace. + unshare(Namespaces::USER)?; + + // Map the UID and GID. + let map = if become_root { format!("0 {uid} 1\n") } else { format!("{uid} {uid} 1\n") }; + fs::write("/proc/self/uid_map", map.as_bytes())?; + fs::write("/proc/self/setgroups", b"deny")?; + fs::write("/proc/self/gid_map", map.as_bytes())?; + + Ok(()) +} + +/// Enter a namespace. +pub(crate) fn unshare(namespaces: Namespaces) -> Result<()> { + let result = unsafe { libc::unshare(namespaces.bits()) }; + if result == 0 { + Ok(()) + } else { + Err(IoError::last_os_error().into()) + } +} + +bitflags! { + /// Unshare system call namespace flags. + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub(crate) struct Namespaces: libc::c_int { + /// Unshare the file descriptor table, so that the calling process no longer + /// shares its file descriptors with any other process. + const FILES = libc::CLONE_FILES; + /// Unshare filesystem attributes, so that the calling process no longer shares + /// its root directory, current directory, or umask attributes with any other process. + const FS = libc::CLONE_FS; + /// Unshare the cgroup namespace. + const CGROUP = libc::CLONE_NEWCGROUP; + /// Unshare the IPC namespace, so that the calling process has a private copy of + /// the IPC namespace which is not shared with any other process. Specifying + /// this flag automatically implies [`Namespaces::SYSVSEM`] as well. + const IPC = libc::CLONE_NEWIPC; + /// Unshare the network namespace, so that the calling process is moved into a + /// new network namespace which is not shared with any previously existing process. + const NETWORK = libc::CLONE_NEWNET; + /// Unshare the mount namespace, so that the calling process has a private copy + /// of its namespace which is not shared with any other process. Specifying this + /// flag automatically implies [`Namespaces::FS`] as well. + const MOUNT = libc::CLONE_NEWNS; + /// Unshare the PID namespace, so that the calling process has a new PID + /// namespace for its children which is not shared with any previously existing + /// process. The calling process is **not** moved into the new namespace. The + /// first child created by the calling process will have the process ID 1 and + /// will assume the role of init in the new namespace. Specifying this flag + /// automatically implies [`libc::CLONE_THREAD`] as well. + const PID = libc::CLONE_NEWPID; + /// Unshare the time namespace, so that the calling process has a new time + /// namespace for its children which is not shared with any previously existing + /// process. The calling process is **not** moved into the new namespace. + const TIME = 0x80; + /// Unshare the user namespace, so that the calling process is moved into a new + /// user namespace which is not shared with any previously existing process. The + /// caller obtains a full set of capabilities in the new namespace. + /// + /// Requires that the calling process is not threaded; specifying this flag + /// automatically implies [`libc::CLONE_THREAD`] and [`Namespaces::FS`] as well. + const USER = libc::CLONE_NEWUSER; + /// Unshare the UTS IPC namespace, so that the calling process has a private + /// copy of the UTS namespace which is not shared with any other process. + const UTS = libc::CLONE_NEWUTS; + /// Unshare System V semaphore adjustment (semadj) values, so that the calling + /// process has a new empty semadj list that is not shared with any other + /// process. If this is the last process that has a reference to the process's + /// current semadj list, then the adjustments in that list are applied to the + /// corresponding semaphores + const SYSVSEM = libc::CLONE_SYSVSEM; + } +} diff --git a/src/linux/seccomp.rs b/src/linux/seccomp.rs deleted file mode 100644 index 73bcfb1..0000000 --- a/src/linux/seccomp.rs +++ /dev/null @@ -1,460 +0,0 @@ -//! Seccomp system call filtering. - -use std::collections::BTreeMap; - -use seccompiler::{ - BpfProgram, SeccompAction, SeccompCmpArgLen, SeccompCmpOp, SeccompCondition, SeccompFilter, - SeccompRule, TargetArch, -}; - -use crate::Result; - -#[cfg(target_arch = "x86_64")] -const ARCH: TargetArch = TargetArch::x86_64; -#[cfg(target_arch = "aarch64")] -const ARCH: TargetArch = TargetArch::aarch64; - -/// Seccomp network filter. -#[derive(Default)] -pub struct NetworkFilter; - -impl NetworkFilter { - /// Apply all rules in this filter. - pub fn apply() -> Result<()> { - let mut rules = BTreeMap::new(); - - // Add unconditionally allowed syscalls. - for syscall in SYSCALL_WHITELIST { - rules.insert(*syscall, Vec::new()); - } - - // Add socket syscalls which do not perform network operations. - Self::add_local_socket_whitelist(&mut rules)?; - - let filter = SeccompFilter::new( - rules, - // Action performed if no rules match. - SeccompAction::Errno(libc::EACCES as u32), - // Action performed if any rule matches. - SeccompAction::Allow, - ARCH, - )?; - let program: BpfProgram = filter.try_into()?; - seccompiler::apply_filter(&program)?; - - Ok(()) - } - - /// Allow local filesystem sockets. - fn add_local_socket_whitelist( - rules: &mut BTreeMap>, - ) -> Result<()> { - // Allow local AF_UNIX/AF_LOCAL sockets. - let allow_unix = SeccompCondition::new( - 0, - SeccompCmpArgLen::Dword, - SeccompCmpOp::Eq, - libc::AF_UNIX as u64, - )?; - let unix_rule = SeccompRule::new(vec![allow_unix])?; - - // Allow local IPC AF_NETLINK sockets. - let allow_netlink = SeccompCondition::new( - 0, - SeccompCmpArgLen::Dword, - SeccompCmpOp::Eq, - libc::AF_NETLINK as u64, - )?; - let netlink_rule = SeccompRule::new(vec![allow_netlink])?; - - let socket_rule = vec![unix_rule, netlink_rule]; - - // Restrict socket creation to allowed socket domain types. - rules.insert(libc::SYS_socketpair, socket_rule.clone()); - rules.insert(libc::SYS_socket, socket_rule); - - Ok(()) - } -} - -/// Unconditionally allowed syscalls for networking. -const SYSCALL_WHITELIST: &[libc::c_long] = &[ - libc::SYS_read, - libc::SYS_write, - #[cfg(target_arch = "x86_64")] - libc::SYS_open, - libc::SYS_close, - #[cfg(target_arch = "x86_64")] - libc::SYS_stat, - libc::SYS_fstat, - #[cfg(target_arch = "x86_64")] - libc::SYS_lstat, - #[cfg(target_arch = "x86_64")] - libc::SYS_poll, - libc::SYS_lseek, - libc::SYS_mmap, - libc::SYS_mprotect, - libc::SYS_munmap, - libc::SYS_brk, - libc::SYS_rt_sigaction, - libc::SYS_rt_sigprocmask, - libc::SYS_rt_sigreturn, - libc::SYS_ioctl, - libc::SYS_pread64, - libc::SYS_pwrite64, - libc::SYS_readv, - libc::SYS_writev, - #[cfg(target_arch = "x86_64")] - libc::SYS_access, - #[cfg(target_arch = "x86_64")] - libc::SYS_pipe, - #[cfg(target_arch = "x86_64")] - libc::SYS_select, - libc::SYS_sched_yield, - libc::SYS_mremap, - libc::SYS_msync, - libc::SYS_mincore, - libc::SYS_madvise, - libc::SYS_shmget, - libc::SYS_shmat, - libc::SYS_shmctl, - libc::SYS_dup, - #[cfg(target_arch = "x86_64")] - libc::SYS_dup2, - #[cfg(target_arch = "x86_64")] - libc::SYS_pause, - libc::SYS_nanosleep, - libc::SYS_getitimer, - #[cfg(target_arch = "x86_64")] - libc::SYS_alarm, - libc::SYS_setitimer, - libc::SYS_getpid, - #[cfg(target_arch = "x86_64")] - libc::SYS_sendfile, - libc::SYS_connect, - libc::SYS_accept, - libc::SYS_sendto, - libc::SYS_recvfrom, - libc::SYS_sendmsg, - libc::SYS_recvmsg, - libc::SYS_shutdown, - libc::SYS_bind, - libc::SYS_listen, - libc::SYS_getsockname, - libc::SYS_getpeername, - libc::SYS_setsockopt, - libc::SYS_getsockopt, - libc::SYS_clone, - #[cfg(target_arch = "x86_64")] - libc::SYS_fork, - #[cfg(target_arch = "x86_64")] - libc::SYS_vfork, - libc::SYS_execve, - libc::SYS_exit, - libc::SYS_wait4, - libc::SYS_kill, - libc::SYS_uname, - libc::SYS_semget, - libc::SYS_semop, - libc::SYS_semctl, - libc::SYS_shmdt, - libc::SYS_msgget, - libc::SYS_msgsnd, - libc::SYS_msgrcv, - libc::SYS_msgctl, - libc::SYS_fcntl, - libc::SYS_flock, - libc::SYS_fsync, - libc::SYS_fdatasync, - libc::SYS_truncate, - libc::SYS_ftruncate, - #[cfg(target_arch = "x86_64")] - libc::SYS_getdents, - libc::SYS_getcwd, - libc::SYS_chdir, - libc::SYS_fchdir, - #[cfg(target_arch = "x86_64")] - libc::SYS_rename, - #[cfg(target_arch = "x86_64")] - libc::SYS_mkdir, - #[cfg(target_arch = "x86_64")] - libc::SYS_rmdir, - #[cfg(target_arch = "x86_64")] - libc::SYS_creat, - #[cfg(target_arch = "x86_64")] - libc::SYS_link, - #[cfg(target_arch = "x86_64")] - libc::SYS_unlink, - #[cfg(target_arch = "x86_64")] - libc::SYS_symlink, - #[cfg(target_arch = "x86_64")] - libc::SYS_readlink, - #[cfg(target_arch = "x86_64")] - libc::SYS_chmod, - libc::SYS_fchmod, - #[cfg(target_arch = "x86_64")] - libc::SYS_chown, - libc::SYS_fchown, - #[cfg(target_arch = "x86_64")] - libc::SYS_lchown, - libc::SYS_umask, - libc::SYS_gettimeofday, - libc::SYS_getrlimit, - libc::SYS_getrusage, - libc::SYS_sysinfo, - libc::SYS_times, - libc::SYS_ptrace, - libc::SYS_getuid, - libc::SYS_syslog, - libc::SYS_getgid, - libc::SYS_setuid, - libc::SYS_setgid, - libc::SYS_geteuid, - libc::SYS_getegid, - libc::SYS_setpgid, - libc::SYS_getppid, - #[cfg(target_arch = "x86_64")] - libc::SYS_getpgrp, - libc::SYS_setsid, - libc::SYS_setreuid, - libc::SYS_setregid, - libc::SYS_getgroups, - libc::SYS_setgroups, - libc::SYS_setresuid, - libc::SYS_getresuid, - libc::SYS_setresgid, - libc::SYS_getresgid, - libc::SYS_getpgid, - libc::SYS_setfsuid, - libc::SYS_setfsgid, - libc::SYS_getsid, - libc::SYS_capget, - libc::SYS_capset, - libc::SYS_rt_sigpending, - libc::SYS_rt_sigtimedwait, - libc::SYS_rt_sigqueueinfo, - libc::SYS_rt_sigsuspend, - libc::SYS_sigaltstack, - #[cfg(target_arch = "x86_64")] - libc::SYS_utime, - #[cfg(target_arch = "x86_64")] - libc::SYS_mknod, - libc::SYS_personality, - #[cfg(target_arch = "x86_64")] - libc::SYS_ustat, - libc::SYS_statfs, - libc::SYS_fstatfs, - libc::SYS_getpriority, - libc::SYS_setpriority, - libc::SYS_sched_setparam, - libc::SYS_sched_getparam, - libc::SYS_sched_setscheduler, - libc::SYS_sched_getscheduler, - libc::SYS_sched_get_priority_max, - libc::SYS_sched_get_priority_min, - libc::SYS_sched_rr_get_interval, - libc::SYS_mlock, - libc::SYS_munlock, - libc::SYS_mlockall, - libc::SYS_munlockall, - libc::SYS_vhangup, - #[cfg(target_arch = "x86_64")] - libc::SYS_modify_ldt, - libc::SYS_pivot_root, - libc::SYS_prctl, - #[cfg(target_arch = "x86_64")] - libc::SYS_arch_prctl, - libc::SYS_adjtimex, - libc::SYS_setrlimit, - libc::SYS_chroot, - libc::SYS_sync, - libc::SYS_acct, - libc::SYS_settimeofday, - libc::SYS_umount2, - libc::SYS_swapon, - libc::SYS_swapoff, - libc::SYS_reboot, - libc::SYS_sethostname, - libc::SYS_setdomainname, - #[cfg(target_arch = "x86_64")] - libc::SYS_get_kernel_syms, - libc::SYS_quotactl, - libc::SYS_gettid, - libc::SYS_readahead, - libc::SYS_setxattr, - libc::SYS_lsetxattr, - libc::SYS_fsetxattr, - libc::SYS_getxattr, - libc::SYS_lgetxattr, - libc::SYS_fgetxattr, - libc::SYS_listxattr, - libc::SYS_llistxattr, - libc::SYS_flistxattr, - libc::SYS_removexattr, - libc::SYS_lremovexattr, - libc::SYS_fremovexattr, - libc::SYS_tkill, - #[cfg(target_arch = "x86_64")] - libc::SYS_time, - libc::SYS_futex, - libc::SYS_sched_setaffinity, - libc::SYS_sched_getaffinity, - #[cfg(target_arch = "x86_64")] - libc::SYS_set_thread_area, - libc::SYS_io_setup, - libc::SYS_io_destroy, - libc::SYS_io_getevents, - libc::SYS_io_submit, - libc::SYS_io_cancel, - #[cfg(target_arch = "x86_64")] - libc::SYS_get_thread_area, - libc::SYS_lookup_dcookie, - #[cfg(target_arch = "x86_64")] - libc::SYS_epoll_create, - #[cfg(target_arch = "x86_64")] - libc::SYS_epoll_ctl_old, - #[cfg(target_arch = "x86_64")] - libc::SYS_epoll_wait_old, - libc::SYS_remap_file_pages, - libc::SYS_getdents64, - libc::SYS_set_tid_address, - libc::SYS_restart_syscall, - libc::SYS_semtimedop, - #[cfg(target_arch = "x86_64")] - libc::SYS_fadvise64, - libc::SYS_timer_create, - libc::SYS_timer_settime, - libc::SYS_timer_gettime, - libc::SYS_timer_getoverrun, - libc::SYS_timer_delete, - libc::SYS_clock_settime, - libc::SYS_clock_gettime, - libc::SYS_clock_getres, - libc::SYS_clock_nanosleep, - libc::SYS_exit_group, - #[cfg(target_arch = "x86_64")] - libc::SYS_epoll_wait, - libc::SYS_epoll_ctl, - libc::SYS_tgkill, - #[cfg(target_arch = "x86_64")] - libc::SYS_utimes, - libc::SYS_mbind, - libc::SYS_set_mempolicy, - libc::SYS_get_mempolicy, - libc::SYS_mq_open, - libc::SYS_mq_unlink, - libc::SYS_mq_timedsend, - libc::SYS_mq_timedreceive, - libc::SYS_mq_notify, - libc::SYS_mq_getsetattr, - libc::SYS_waitid, - libc::SYS_add_key, - libc::SYS_request_key, - libc::SYS_keyctl, - libc::SYS_ioprio_set, - libc::SYS_ioprio_get, - #[cfg(target_arch = "x86_64")] - libc::SYS_inotify_init, - libc::SYS_inotify_add_watch, - libc::SYS_inotify_rm_watch, - libc::SYS_migrate_pages, - libc::SYS_openat, - libc::SYS_mkdirat, - libc::SYS_mknodat, - libc::SYS_fchownat, - #[cfg(target_arch = "x86_64")] - libc::SYS_futimesat, - libc::SYS_newfstatat, - libc::SYS_unlinkat, - libc::SYS_renameat, - libc::SYS_linkat, - libc::SYS_symlinkat, - libc::SYS_readlinkat, - libc::SYS_fchmodat, - libc::SYS_faccessat, - libc::SYS_pselect6, - libc::SYS_ppoll, - libc::SYS_unshare, - libc::SYS_set_robust_list, - libc::SYS_get_robust_list, - libc::SYS_splice, - libc::SYS_tee, - libc::SYS_sync_file_range, - libc::SYS_vmsplice, - libc::SYS_move_pages, - libc::SYS_utimensat, - libc::SYS_epoll_pwait, - #[cfg(target_arch = "x86_64")] - libc::SYS_signalfd, - libc::SYS_timerfd_create, - #[cfg(target_arch = "x86_64")] - libc::SYS_eventfd, - libc::SYS_fallocate, - libc::SYS_timerfd_settime, - libc::SYS_timerfd_gettime, - libc::SYS_accept4, - libc::SYS_signalfd4, - libc::SYS_eventfd2, - libc::SYS_epoll_create1, - libc::SYS_dup3, - libc::SYS_pipe2, - libc::SYS_inotify_init1, - libc::SYS_preadv, - libc::SYS_pwritev, - libc::SYS_rt_tgsigqueueinfo, - libc::SYS_perf_event_open, - libc::SYS_recvmmsg, - libc::SYS_fanotify_init, - libc::SYS_fanotify_mark, - libc::SYS_prlimit64, - libc::SYS_name_to_handle_at, - libc::SYS_open_by_handle_at, - libc::SYS_clock_adjtime, - libc::SYS_syncfs, - libc::SYS_sendmmsg, - libc::SYS_setns, - libc::SYS_getcpu, - libc::SYS_process_vm_readv, - libc::SYS_process_vm_writev, - libc::SYS_kcmp, - libc::SYS_sched_setattr, - libc::SYS_sched_getattr, - libc::SYS_renameat2, - libc::SYS_seccomp, - libc::SYS_getrandom, - libc::SYS_memfd_create, - libc::SYS_bpf, - libc::SYS_execveat, - libc::SYS_userfaultfd, - libc::SYS_membarrier, - libc::SYS_mlock2, - libc::SYS_copy_file_range, - libc::SYS_preadv2, - libc::SYS_pwritev2, - libc::SYS_pkey_mprotect, - libc::SYS_pkey_alloc, - libc::SYS_pkey_free, - libc::SYS_statx, - libc::SYS_rseq, - libc::SYS_pidfd_send_signal, - libc::SYS_open_tree, - libc::SYS_fsopen, - libc::SYS_fsconfig, - libc::SYS_fspick, - libc::SYS_pidfd_open, - libc::SYS_clone3, - libc::SYS_close_range, - libc::SYS_openat2, - libc::SYS_faccessat2, - libc::SYS_process_madvise, - libc::SYS_epoll_pwait2, - libc::SYS_mount_setattr, - libc::SYS_quotactl_fd, - libc::SYS_landlock_create_ruleset, - libc::SYS_landlock_add_rule, - libc::SYS_landlock_restrict_self, - libc::SYS_memfd_secret, - libc::SYS_process_mrelease, - libc::SYS_futex_waitv, - libc::SYS_set_mempolicy_home_node, -]; diff --git a/tests/canonicalize.rs b/tests/canonicalize.rs index 5675452..5b7772a 100644 --- a/tests/canonicalize.rs +++ b/tests/canonicalize.rs @@ -2,8 +2,7 @@ use std::fs; use birdcage::{Birdcage, Exception, Sandbox}; -#[test] -fn canonicalize() { +fn main() { let mut birdcage = Birdcage::new().unwrap(); birdcage.add_exception(Exception::Read("./".into())).unwrap(); birdcage.lock().unwrap(); diff --git a/tests/env.rs b/tests/env.rs index 4175a95..661785f 100644 --- a/tests/env.rs +++ b/tests/env.rs @@ -2,8 +2,7 @@ use std::env; use birdcage::{Birdcage, Exception, Sandbox}; -#[test] -fn partial_env() { +fn main() { // Setup our environment variables env::set_var("PUBLIC", "GOOD"); env::set_var("PRIVATE", "BAD"); diff --git a/tests/exec.rs b/tests/exec.rs index 0a809d5..f7f67ff 100644 --- a/tests/exec.rs +++ b/tests/exec.rs @@ -3,8 +3,7 @@ use std::process::Command; use birdcage::{Birdcage, Exception, Sandbox}; -#[test] -fn execution() { +fn main() { let mut birdcage = Birdcage::new().unwrap(); birdcage.add_exception(Exception::ExecuteAndRead("/usr/bin/true".into())).unwrap(); birdcage.add_exception(Exception::ExecuteAndRead("/usr/lib".into())).unwrap(); diff --git a/tests/fs.rs b/tests/fs.rs index 8f01fcb..dd8bd31 100644 --- a/tests/fs.rs +++ b/tests/fs.rs @@ -3,8 +3,7 @@ use std::fs; use birdcage::{Birdcage, Exception, Sandbox}; use tempfile::NamedTempFile; -#[test] -fn partial_fs() { +fn main() { const FILE_CONTENT: &str = "expected content"; // Setup our test files. diff --git a/tests/full_env.rs b/tests/full_env.rs index ab643e2..082ee15 100644 --- a/tests/full_env.rs +++ b/tests/full_env.rs @@ -2,8 +2,7 @@ use std::env; use birdcage::{Birdcage, Exception, Sandbox}; -#[test] -fn full_env() { +fn main() { // Setup our environment variables env::set_var("PUBLIC", "GOOD"); diff --git a/tests/full_sandbox.rs b/tests/full_sandbox.rs index 5c24804..db66e2f 100644 --- a/tests/full_sandbox.rs +++ b/tests/full_sandbox.rs @@ -1,12 +1,11 @@ -use std::net::{TcpListener, TcpStream}; +use std::net::TcpStream; use std::process::Command; use std::{env, fs}; use birdcage::{Birdcage, Sandbox}; use tempfile::NamedTempFile; -#[test] -fn full_sandbox() { +fn main() { const FILE_CONTENT: &str = "expected content"; // Create testfile. @@ -19,11 +18,6 @@ fn full_sandbox() { let content = fs::read_to_string(&path).unwrap(); assert_eq!(content, FILE_CONTENT); - // Ensure non-sandboxed socket bind works. - let listener = TcpListener::bind("127.0.0.1:31337"); - assert!(listener.is_ok()); - drop(listener); - // Ensure non-sandboxed socket connect works. let stream = TcpStream::connect("phylum.io:443"); assert!(stream.is_ok()); @@ -48,11 +42,6 @@ fn full_sandbox() { let result = fs::read_to_string(path); assert!(result.is_err()); - // Ensure sandboxed socket bind is blocked. - let listener = TcpListener::bind("127.0.0.1:31337"); - assert!(listener.is_err()); - drop(listener); - // Ensure sandboxed socket connect is blocked. let stream = TcpStream::connect("phylum.io:443"); assert!(stream.is_err()); diff --git a/tests/net.rs b/tests/net.rs index 8261228..9933b8f 100644 --- a/tests/net.rs +++ b/tests/net.rs @@ -1,94 +1,12 @@ -#[cfg(target_os = "linux")] -use std::io::Error as IoError; -use std::io::ErrorKind as IoErrorKind; -use std::net::{TcpListener, TcpStream}; +use std::net::TcpStream; use birdcage::{Birdcage, Exception, Sandbox}; -#[cfg(target_os = "linux")] -use libc; -#[test] -fn network() { +fn main() { let mut birdcage = Birdcage::new().unwrap(); birdcage.add_exception(Exception::Networking).unwrap(); birdcage.lock().unwrap(); - TcpStream::connect("8.8.8.8:443").unwrap(); - TcpListener::bind("127.0.0.1:31337").unwrap(); -} - -#[cfg(target_os = "linux")] -#[test] -fn allow_io_uring() { - let mut birdcage = Birdcage::new().unwrap(); - birdcage.add_exception(Exception::Networking).unwrap(); - birdcage.lock().unwrap(); - - let mut io_uring_params = - vec![IoUringParams { flags: 1, sq_entries: 32, cq_entries: 32, ..Default::default() }]; - - let result = unsafe { - libc::syscall(libc::SYS_io_uring_setup, io_uring_params.len(), io_uring_params.as_mut_ptr()) - }; - - assert!(result >= 0); -} - -#[cfg(target_os = "linux")] -#[test] -fn block_io_uring() { - let birdcage = Birdcage::new().unwrap(); - birdcage.lock().unwrap(); - - let mut io_uring_params = - vec![IoUringParams { flags: 1, sq_entries: 32, cq_entries: 32, ..Default::default() }]; - - let result = unsafe { - libc::syscall(libc::SYS_io_uring_setup, io_uring_params.len(), io_uring_params.as_mut_ptr()) - }; - - assert_eq!(result, -1); - assert_eq!(IoError::last_os_error().kind(), IoErrorKind::PermissionDenied); -} - -#[cfg(target_os = "linux")] -#[test] -fn allow_local_sockets() { - let birdcage = Birdcage::new().unwrap(); - birdcage.lock().unwrap(); - - let fd = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_STREAM, 0) }; - if fd < 0 { - panic!("AF_UNIX socket creation failed: {}", IoError::last_os_error()); - } - - unsafe { libc::close(fd) }; -} - -#[repr(C)] -#[derive(Default)] -struct IoUringParams { - sq_entries: u32, - cq_entries: u32, - flags: u32, - sq_thread_cpu: u32, - sq_thread_idle: u32, - features: u32, - wq_fd: u32, - resv: [u32; 3], - sq_off: IoSqringOffsets, - cq_off: IoSqringOffsets, -} - -#[repr(C)] -#[derive(Default)] -struct IoSqringOffsets { - head: u32, - tail: u32, - ring_mask: u32, - ring_entries: u32, - flags: u32, - dropped: u32, - array: u32, - resv: [u32; 3], + let result = TcpStream::connect("8.8.8.8:443"); + assert!(result.is_ok()); }