### Run an Extrasafe Isolate with Bind Mounts Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md This example demonstrates setting up and running an Isolate. It configures a bind mount for a directory and sets a root filesystem size limit. Ensure the name used in `Isolate::run` and `Isolate::main_hook` matches. ```rust use extrasafe::isolate::Isolate; use std::collections::HashMap; use std::path::PathBuf; const EXAMPLE_ISOLATE: &str = "user guide isolate"; /// The function that ultimately runs inside the Isolate fn do_cool_thing() { println!("I'm going to read some files from /cooldir and do cool stuff with it!"); // TODO: do cool stuff with the files in /cooldir } /// Isolate configuration that happens when the program is re-executed after `Isolate::run` fn setup_isolate(name: &'static str) -> Isolate { let path = std::env::var("COOL_DIRECTORY").unwrap(); let path = PathBuf::from(path); Isolate::new(name, do_cool_thing) // This will mount /a/b/c from the parent into /cooldir in the child, // but not until after entering the namespace. .add_bind_mount(path, "/cooldir") // Limit the amount of data that can be written to the filesystem the // Isolate lives in. .set_rootfs_size(1) } fn main() { // Once the Isolate::run call is made, this will run the setup function, // enter the namespace and call the the function provided. The first time the program runs, // this code will be ignored. Isolate::main_hook(EXAMPLE_ISOLATE, setup_isolate); // ... somewhere later in the program let env_vars = HashMap::from([("COOL_DIRECTORY".to_string(), "/".to_string())]); // `Isolate::run` returns a normal `std::process::Output` let output = Isolate::run(EXAMPLE_ISOLATE, &env_vars).unwrap(); assert!(output.status.success()); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); } ``` -------------------------------- ### Disable System Calls with extrasafe Source: https://github.com/boustrophedon/extrasafe/blob/master/README.md This example demonstrates how to disable most system calls while allowing standard output and error streams. It shows that file creation fails after applying the safety context. ```rust fn main() { println!("disabling syscalls..."); extrasafe::SafetyContext::new() .enable( extrasafe::builtins::SystemIO::nothing() .allow_stdout() .allow_stderr() ).unwrap() .apply_to_all_threads().unwrap(); // Opening files now fails! let res = File::create("should_fail.txt"); assert!(res.is_err()); println!("but printing to stdout still works!"); eprintln!("and so does stderr!"); } ``` -------------------------------- ### Enable SystemIO and Networking Rulesets Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Enable predefined RuleSets for system I/O and networking operations. SystemIO allows opening files read-only, while Networking allows starting and running TCP clients. Ensure that the enabled rules align with the program's intended operations. ```rust use extrasafe::builtins::{SystemIO, Networking}; let ctx = ctx .enable( SystemIO::nothing() .allow_open_readonly() ).expect("Failed to add systemio ruleset to context") // The Networking RuleSet includes both read and write, but our files // will be opened readonly so we can't actually write to them. // We can still write to stdout and stderr though. .enable( Networking::nothing() .allow_start_tcp_clients() .allow_running_tcp_clients() ).expect("Failed to add networking ruleset to context"); ``` -------------------------------- ### Allow Specific File Operations with extrasafe and Landlock Source: https://github.com/boustrophedon/extrasafe/blob/master/README.md This example uses extrasafe with Landlock to allow create and write operations within a specific temporary directory. It verifies that operations outside this directory fail, while operations within it succeed. It also shows that network-related syscalls remain disallowed. ```rust fn main() { let tmp_dir = tempfile::tempdir().unwrap().into_path(); extrasafe::SafetyContext::new() .enable( extrasafe::builtins::SystemIO::nothing() .allow_create_in_dir(&tmp_dir) .allow_write_file(&tmp_dir) ).unwrap() .apply_to_current_thread().unwrap(); // Opening arbitrary files now fails! assert!(File::open("/etc/passwd") .is_err()); // But the directory we allowed works assert!(File::create(tmp_dir.join("my_output.txt")) .is_ok()); // And other syscalls are still disallowed assert!(std::net::UdpSocket::bind("127.0.0.1:0") .is_err()); } ``` -------------------------------- ### Configure SystemIO ruleset for file operations Source: https://context7.com/boustrophedon/extrasafe/llms.txt Configures syscalls for file operations like open, read, write, close, and stat. Start with `SystemIO::nothing()` and chain opt-in methods. Supports Landlock integration for path-level restrictions if the `landlock` feature is enabled. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; use std::fs::File; fn main() { // Open files before locking down let log_file = File::create("/tmp/app.log").unwrap(); SafetyContext::new() .enable( SystemIO::nothing() // Allow stdout/stderr .allow_stdout() .allow_stderr() // Allow reading from stdin .allow_stdin() // Allow writing only to our specific log file fd .allow_file_write(&log_file) // Allow reading any already-open file .allow_read() .allow_close() ).unwrap() .apply_to_current_thread() .unwrap(); // Opening new files is blocked: assert!(File::open("/etc/passwd").is_err()); // Writing to our pre-opened log file works: use std::io::Write; writeln!(&log_file, "this works").unwrap(); } ``` -------------------------------- ### Apply Landlock for Filesystem Access Control Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Use Landlock via Extrasefe's SystemIO ruleset to grant specific filesystem access rights, such as allowing file creation and writing within a designated directory. This example demonstrates restricting access to other directories and disallowed syscalls. ```rust fn with_landlock() { let tmp_dir_allow = tempfile::tempdir().unwrap().into_path(); let tmp_dir_deny = tempfile::tempdir().unwrap().into_path(); extrasafe::SafetyContext::new() .enable( extrasafe::builtins::SystemIO::nothing() .allow_create_in_dir(&tmp_dir_allow) .allow_write_file(&tmp_dir_allow) ).unwrap() .apply_to_current_thread().unwrap(); // Opening arbitrary files now fails! let res = File::create(tmp_dir_deny.join("evil.txt")); assert!(res.is_err()); // But the directory we allowed works let res = File::create(tmp_dir_allow.join("my_output.txt")); assert!(res.is_ok()); println!("printing to stdout is also allowed"); eprintln!("because read/write syscalls are unrestricted"); // And other syscalls are still disallowed let res = std::net::UdpSocket::bind("127.0.0.1:0"); assert!(res.is_err()); } ``` -------------------------------- ### Network helper shortcuts for SSL and DNS files with SystemIO Source: https://context7.com/boustrophedon/extrasafe/llms.txt These convenience Landlock methods grant read access to system SSL certificate directories and DNS configuration files. `allow_ssl_files()` covers `/etc/ssl/certs` and `/etc/ca-certificates`, while `allow_dns_files()` includes `/etc/resolv.conf`, `/etc/hosts`, and similar files. These are necessary for TLS and DNS resolvers. Ensure `Networking` rules for UDP and TCP clients are also configured. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::{SystemIO, Networking, danger_zone::Threads}; fn run_https_client() { let client = reqwest::Client::new(); SafetyContext::new() .enable( Networking::nothing() .allow_start_udp_servers().yes_really() // DNS queries use UDP .allow_start_tcp_clients() ).unwrap() .enable(Threads::nothing().allow_create()).unwrap() .enable( SystemIO::nothing() .allow_dns_files() // /etc/resolv.conf, /etc/hosts, etc. .allow_ssl_files() // /etc/ssl/certs, /etc/ca-certificates ).unwrap() .apply_to_current_thread() .unwrap(); let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(); rt.block_on(async { let resp = client.get("https://example.org").send().await.unwrap(); println!("status: {}", resp.status()); }); } ``` -------------------------------- ### Create and Apply SystemIO Rules with SafetyContext Source: https://context7.com/boustrophedon/extrasafe/llms.txt Demonstrates creating a `SafetyContext` that only allows writing to stdout and stderr, then applying it to all threads. Opening files will be blocked after application. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn main() { // Build a context that only allows writing to stdout and stderr SafetyContext::new() .enable( SystemIO::nothing() .allow_stdout() .allow_stderr() ).expect("Failed to add SystemIO rules") .apply_to_all_threads() .expect("Failed to apply seccomp filter"); // From this point on, opening files is blocked: let res = std::fs::File::create("/tmp/blocked.txt"); assert!(res.is_err()); // EPERM println!("stdout still works"); eprintln!("so does stderr"); } ``` -------------------------------- ### Path-level filesystem access control with SystemIO Landlock methods Source: https://context7.com/boustrophedon/extrasafe/llms.txt When the `landlock` feature is enabled, `SystemIO` provides methods for path-level filesystem access control. These methods allow specifying exact files and directories for access, independent of the file descriptor. This requires the Landlock LSM and a kernel version of 5.19 or later. Ensure the necessary permissions like `allow_list_dir`, `allow_create_in_dir`, `allow_write_file`, and `allow_read_path` are configured for the desired directories. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn main() { let allowed_dir = tempfile::tempdir().unwrap().into_path(); let denied_dir = tempfile::tempdir().unwrap().into_path(); SafetyContext::new() .enable( SystemIO::nothing() // Allow listing a directory .allow_list_dir(&allowed_dir) // Allow creating files inside allowed_dir .allow_create_in_dir(&allowed_dir) // Allow writing to files in allowed_dir .allow_write_file(&allowed_dir) // Allow reading from allowed_dir .allow_read_path(&allowed_dir) ).unwrap() .apply_to_current_thread() .unwrap(); // Writing inside the allowed directory works: let out = allowed_dir.join("output.txt"); assert!(std::fs::write(&out, "hello").is_ok()); // Writing anywhere else is blocked by Landlock: assert!(std::fs::write(denied_dir.join("evil.txt"), "x").is_err()); // Reading allowed path works: assert!(std::fs::read_to_string(&out).is_ok()); } ``` -------------------------------- ### Enable Process Spawning with ForkAndExec and SystemIO Source: https://context7.com/boustrophedon/extrasafe/llms.txt Allows `fork`, `vfork`, `execve`, `execveat`, `wait4`, `waitid`, and `clone` syscalls. The child process inherits the seccomp filter. Also allows standard output and error streams. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::danger_zone::ForkAndExec; use extrasafe::builtins::SystemIO; fn main() { SafetyContext::new() .enable(ForkAndExec) .unwrap() .enable( SystemIO::nothing() .allow_stdout() .allow_stderr() ).unwrap() .apply_to_current_thread() .unwrap(); // Spawning a child process still works: let output = std::process::Command::new("/bin/echo") .arg("hello from child") .output() .unwrap(); assert!(output.status.success()); println!("{}", String::from_utf8_lossy(&output.stdout)); } ``` -------------------------------- ### SystemIO Ruleset Source: https://context7.com/boustrophedon/extrasafe/llms.txt Configures syscalls related to file operations: open, read, write, close, stat, ioctl. Includes Landlock integration for path-level restrictions. ```APIDOC ## `SystemIO` — File and IO syscall ruleset Configures syscalls related to file operations: open, read, write, close, stat, ioctl. Start with `SystemIO::nothing()` and chain opt-in methods. Includes Landlock integration for path-level restrictions (requires the `landlock` feature). ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; use std::fs::File; fn main() { // Open files before locking down let log_file = File::create("/tmp/app.log").unwrap(); SafetyContext::new() .enable( SystemIO::nothing() // Allow stdout/stderr .allow_stdout() .allow_stderr() // Allow reading from stdin .allow_stdin() // Allow writing only to our specific log file fd .allow_file_write(&log_file) // Allow reading any already-open file .allow_read() .allow_close() ).unwrap() .apply_to_current_thread() .unwrap(); // Opening new files is blocked: assert!(File::open("/etc/passwd").is_err()); // Writing to our pre-opened log file works: use std::io::Write; writeln!(&log_file, "this works").unwrap(); } ``` ``` -------------------------------- ### Create a SafetyContext Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Instantiate a new SafetyContext to begin configuring seccomp and Landlock filters. ```rust let ctx = extrasafe::SafetyContext::new(); ``` -------------------------------- ### Apply Security Context with ExtraSafe Source: https://context7.com/boustrophedon/extrasafe/llms.txt Demonstrates how to apply a security context to the current thread using extrasafe. This involves enabling specific system I/O and networking rules and handling potential errors like ConditionalNoEffectError or NoRulesEnabled. ```rust use extrasafe::{SafetyContext, ExtraSafeError}; use extrasafe::builtins::{SystemIO, Networking}; fn apply_context() -> Result<(), ExtraSafeError> { SafetyContext::new() // allow_open_readonly uses conditional rules on openat/open .enable(SystemIO::nothing().allow_open_readonly())? // Networking's allow_running_tcp_servers includes unrestricted `read` and `write`, // which would conflict with conditional read/write rules — careful with combinations! .enable(Networking::nothing().allow_running_tcp_servers())? .apply_to_current_thread() } fn main() { match apply_context() { Ok(()) => println!("Security context applied"), Err(ExtraSafeError::ConditionalNoEffectError(sysno, rs_a, rs_b)) => { eprintln!("Rule conflict on syscall `{}`: '{}' vs '{}'", sysno, rs_a, rs_b); } Err(ExtraSafeError::NoRulesEnabled) => { eprintln!("No rules were enabled — call .enable() at least once"); } Err(e) => eprintln!("Other error: {}", e), } } ``` -------------------------------- ### Enable Anonymous Pipe Creation with SystemIO Source: https://context7.com/boustrophedon/extrasafe/llms.txt Allows `pipe` and `pipe2` syscalls for creating anonymous pipes. Requires read, write, close, and stdout permissions for basic pipe operations. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::{Pipes, SystemIO}; fn main() { SafetyContext::new() .enable(Pipes) .unwrap() .enable( SystemIO::nothing() .allow_read() .allow_write() .allow_close() .allow_stdout() ).unwrap() .apply_to_current_thread() .unwrap(); // Create an anonymous pipe and communicate through it: let (reader, writer) = os_pipe::pipe().unwrap(); drop(writer); // close the write end drop(reader); // close the read end println!("pipe syscall succeeded"); } ``` -------------------------------- ### Verify File Creation Restriction Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Assert that creating a file for writing fails after seccomp filters have been applied, demonstrating the restriction on file system operations. ```rust assert!(std::fs::File::create("/tmp/a_file").is_err()); ``` -------------------------------- ### Enable Networking and File Access Rules with SafetyContext Source: https://context7.com/boustrophedon/extrasafe/llms.txt Shows how to configure a `SafetyContext` to allow read-only file access and permit already running TCP servers, while blocking the creation of new sockets. This is useful for services that need to bind to ports before security is applied. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::{SystemIO, Networking}; fn main() { // Bind a TCP listener before locking down syscalls let listener = std::net::TcpListener::bind("127.0.0.1:8080").unwrap(); SafetyContext::new() // Allow readonly file access (for config, certs, etc.) .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_close() ).expect("SystemIO rules failed") // Allow the already-bound server to accept connections .enable( Networking::nothing() .allow_running_tcp_servers() ).expect("Networking rules failed") .apply_to_current_thread() .expect("Failed to apply seccomp filter"); // Creating new sockets is now blocked: let res = std::net::TcpListener::bind("127.0.0.1:9090"); assert!(res.is_err()); // Cannot create new sockets drop(listener); } ``` -------------------------------- ### Control network syscalls with Networking ruleset Source: https://context7.com/boustrophedon/extrasafe/llms.txt The `Networking` ruleset controls socket creation, binding, and connection operations. Use `allow_start_*` methods for new servers/clients, which require `socket`/`bind`/`connect` permissions. These methods return `YesReally`, requiring a `.yes_really()` call to confirm intent. `allow_running_*` methods are for existing sockets without creating new ones. Unix domain sockets are also supported. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::Networking; fn main() { // Bind before locking down let _listener = std::net::TcpListener::bind("127.0.0.1:7878").unwrap(); let _udp = std::net::UdpSocket::bind("127.0.0.1:5353").unwrap(); SafetyContext::new() .enable( Networking::nothing() // Keep existing TCP server running (no new socket/bind) .allow_running_tcp_servers() // Allow TCP clients to connect outward .allow_start_tcp_clients() // Keep existing UDP socket running .allow_running_udp_sockets() ).unwrap() .apply_to_current_thread() .unwrap(); // Creating a new TCP server is now blocked: assert!(std::net::TcpListener::bind("127.0.0.1:9999").is_err()); // Unix domain sockets are also available: // Networking::nothing().allow_start_unix_servers().yes_really() // Networking::nothing().allow_running_unix_servers() } ``` -------------------------------- ### Enable Specific Syscalls with Extrasefe Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Use this method to enable one or two specific syscalls directly within a SafetyContext. Ensure the syscalls are available on your target architecture. ```rust extrasafe::SafetyContext::new() .enable(syscalls::Sysno::reboot).unwrap() .enable(syscalls::Sysno::sysinfo).unwrap() .apply_to_current_thread().unwrap(); ``` -------------------------------- ### Enable Thread Creation and Sleep with Danger Zone Source: https://context7.com/boustrophedon/extrasafe/llms.txt Allows `clone`/`clone3` syscalls to create new threads. Use `allow_sleep().yes_really()` to acknowledge timing-attack risk when using nanosleep/clock_nanosleep. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::danger_zone::Threads; fn main() { SafetyContext::new() .enable( Threads::nothing() .allow_create() .allow_sleep().yes_really() // nanosleep/clock_nanosleep ).unwrap() .apply_to_current_thread() .unwrap(); // Can still create threads: let h = std::thread::spawn(|| { std::thread::sleep(std::time::Duration::from_millis(10)); 42u32 }); assert_eq!(h.join().unwrap(), 42); } ``` -------------------------------- ### SafetyContext::new() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Creates a new `SafetyContext` builder, which is the entry point for configuring security rules. No restrictions are active until an `apply_*` method is called. ```APIDOC ## SafetyContext::new() - Create a security context The entry point for all extrasafe configuration. Returns a builder that accumulates `RuleSet`s before the filter is applied. No syscall restrictions are in effect until one of the `apply_*` methods is called. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn main() { // Build a context that only allows writing to stdout and stderr SafetyContext::new() .enable( SystemIO::nothing() .allow_stdout() .allow_stderr() ).expect("Failed to add SystemIO rules") .apply_to_all_threads() .expect("Failed to apply seccomp filter"); // From this point on, opening files is blocked: let res = std::fs::File::create("/tmp/blocked.txt"); assert!(res.is_err()); // EPERM println!("stdout still works"); eprintln!("so does stderr"); } ``` ``` -------------------------------- ### Implement RuleSet for Custom Syscall Policies Source: https://context7.com/boustrophedon/extrasafe/llms.txt Implement `RuleSet` on a struct to define a reusable, named policy for syscalls. `simple_rules` allows unconditional syscalls, `conditional_rules` filters syscall arguments, and `landlock_rules` (feature-gated) handles Landlock path rules. ```rust use extrasafe::{RuleSet, SeccompRule, SafetyContext}; use extrasafe::seccomp_arg_filter; use syscalls::Sysno; use std::collections::HashMap; /// A policy that only allows creating TCP stream sockets (no UDP, no Unix) struct TcpOnlyPolicy; impl RuleSet for TcpOnlyPolicy { fn simple_rules(&self) -> Vec { vec![] // no unconditional allows } fn conditional_rules(&self) -> HashMap> { const AF_INET: u64 = libc::AF_INET as u64; const AF_INET6: u64 = libc::AF_INET6 as u64; const SOCK_STREAM:u64 = libc::SOCK_STREAM as u64; let ipv4_tcp = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); let ipv6_tcp = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); HashMap::from([(Sysno::socket, vec![ipv4_tcp, ipv6_tcp])]) } fn name(&self) -> &'static str { "TcpOnlyPolicy" } } fn main() { SafetyContext::new() .enable(TcpOnlyPolicy) .unwrap() .apply_to_current_thread() .unwrap(); // TCP socket creation works: let tcp = std::net::TcpStream::connect("93.184.216.34:80"); // UDP creation is blocked: assert!(std::net::UdpSocket::bind("127.0.0.1:0").is_err()); } ``` -------------------------------- ### Enable Clock Gettime with Time Ruleset Source: https://context7.com/boustrophedon/extrasafe/llms.txt Allows `clock_gettime` syscall. This ruleset is rarely needed on 64-bit Linux systems as these calls are often handled via vDSO. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::Time; fn main() { SafetyContext::new() .enable(Time::nothing().allow_gettime()) .unwrap() .apply_to_current_thread() .unwrap(); let now = std::time::Instant::now(); std::thread::sleep(std::time::Duration::from_millis(1)); println!("elapsed: {:?}", now.elapsed()); } ``` -------------------------------- ### Restrict open to read-only with SystemIO::allow_open_readonly() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Use `allow_open_readonly()` to permit `open`/`openat` syscalls only without write, create, or append flags. This method relies on seccomp argument filtering and does not permit `openat2`. Ensure `allow_read()` and `allow_close()` are also enabled if needed. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn main() { SafetyContext::new() .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_close() .allow_metadata() ).unwrap() .apply_to_current_thread() .unwrap(); // Read-only open succeeds: assert!(std::fs::File::open("/etc/hostname").is_ok()); // Write/create is blocked: assert!(std::fs::File::create("/tmp/blocked.txt").is_err()); assert!(std::fs::OpenOptions::new().write(true).open("/tmp/x").is_err()); } ``` -------------------------------- ### Isolate Subprocesses with User Namespaces Source: https://context7.com/boustrophedon/extrasafe/llms.txt Launch a subprocess that re-execs the program into a private tmpfs root using Linux user namespaces. Filesystem and optionally network are isolated from the parent. Requires the `isolate` crate feature. ```rust use extrasafe::isolate::Isolate; use std::collections::HashMap; use std::path::PathBuf; const MY_ISOLATE: &str = "my-data-processor"; /// This function runs inside the isolated namespace fn process_data() { // Only /input (bind-mounted from parent) is accessible let data = std::fs::read_to_string("/input/data.txt") .expect("data.txt not found"); println!("Processing: {}", data.trim()); // The rest of the host filesystem is invisible assert!(std::fs::read_to_string("/etc/passwd").is_err()); } /// Configure the isolate when the process is re-executed fn setup_isolate(name: &'static str) -> Isolate { let src_dir = std::env::var("DATA_DIR").unwrap(); Isolate::new(name, process_data) .add_bind_mount(PathBuf::from(src_dir), "/input") // mount host dir into isolate .set_rootfs_size(5) // 5 MiB tmpfs .new_network(true) // create isolated network namespace (no external network) } fn main() { // Must be called at the very start of main to intercept the re-exec Isolate::main_hook(MY_ISOLATE, setup_isolate); // --- rest of normal application startup --- let envs = HashMap::from([ ("DATA_DIR".to_string(), "/tmp/mydata".to_string()), ]); let output = Isolate::run(MY_ISOLATE, &envs) .expect("Failed to launch isolate"); assert!(output.status.success()); println!("Isolate stdout: {}", String::from_utf8_lossy(&output.stdout)); } ``` -------------------------------- ### SafetyContext::enable() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Adds a `RuleSet` to the security context, merging its syscall rules. This method returns an error if a conflicting rule is already present. Multiple `enable()` calls can be chained. ```APIDOC ## SafetyContext::enable() - Add a RuleSet Merges the syscall rules provided by a `RuleSet` implementor into the context. Returns an error if a simple (unrestricted) rule for a syscall conflicts with a conditional (argument-filtered) rule for the same syscall already present in the context. Multiple `enable()` calls can be chained before applying. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::{SystemIO, Networking}; fn main() { // Bind a TCP listener before locking down syscalls let listener = std::net::TcpListener::bind("127.0.0.1:8080").unwrap(); SafetyContext::new() // Allow readonly file access (for config, certs, etc.) .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_close() ).expect("SystemIO rules failed") // Allow the already-bound server to accept connections .enable( Networking::nothing() .allow_running_tcp_servers() ).expect("Networking rules failed") .apply_to_current_thread() .expect("Failed to apply seccomp filter"); // Creating new sockets is now blocked: let res = std::net::TcpListener::bind("127.0.0.1:9090"); assert!(res.is_err()); // Cannot create new sockets drop(listener); } ``` ``` -------------------------------- ### Apply seccomp filter to all threads with SafetyContext Source: https://context7.com/boustrophedon/extrasafe/llms.txt Applies seccomp rules to all threads in the process, including those already running, using SECCOMP_FILTER_FLAG_TSYNC. This cannot be used with Landlock rules. Ensure necessary imports are present. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::{Networking, danger_zone::Threads}; use std::thread; fn main() { // Start a background server thread before locking down let _server = thread::spawn(|| { // server loop ... }); thread::sleep(std::time::Duration::from_millis(50)); // Apply to ALL threads (including the server thread already running) SafetyContext::new() .enable( Networking::nothing() .allow_running_tcp_servers() .allow_start_tcp_clients() ).unwrap() .enable( Threads::nothing() .allow_create() ).unwrap() .apply_to_all_threads() // synced to every thread via SECCOMP_FILTER_FLAG_TSYNC .unwrap(); // Creating new bindings is now blocked on all threads: assert!(std::net::TcpListener::bind("127.0.0.1:9999").is_err()); } ``` -------------------------------- ### Apply Seccomp Filters to Current Thread Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Apply the configured seccomp filters to the current thread. This action is permanent for the lifetime of the thread and any new threads it creates. Errors during application will be returned. ```rust ctx.apply_to_current_thread() .expect("Failed to apply seccomp filters"); ``` -------------------------------- ### Use seccomp_arg_filter! for Syscall Argument Conditions Source: https://context7.com/boustrophedon/extrasafe/llms.txt Construct `SeccompArgumentFilter`s for use in `conditional_rules()`. Supports various comparison operators and masked equality. Arguments are referenced as `arg0` through `arg5`. ```rust use extrasafe::*; // Allow read() only on stdin (fd == 0) let stdin_read = SeccompRule::new(syscalls::Sysno::read) .and_condition(seccomp_arg_filter!(arg0 == 0)); // Allow write() only on stdout (fd == 1) or stderr (fd == 2) let stdout_write = SeccompRule::new(syscalls::Sysno::write) .and_condition(seccomp_arg_filter!(arg0 == 1)); let stderr_write = SeccompRule::new(syscalls::Sysno::write) .and_condition(seccomp_arg_filter!(arg0 == 2)); // Allow socket() only when AF_INET flag is set in arg0 const AF_INET: u64 = libc::AF_INET as u64; let inet_socket = SeccompRule::new(syscalls::Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)); // Allow open() only when fd count is below a threshold let limited_open = SeccompRule::new(syscalls::Sysno::openat) .and_condition(seccomp_arg_filter!(arg0 < 100)); println!("Filters created successfully"); ``` -------------------------------- ### SafetyContext::apply_to_current_thread() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Compiles and loads the accumulated rules as a seccomp BPF filter on the calling thread only. Once applied, the filter is permanent for the thread's lifetime. Automatically includes BasicCapabilities. ```APIDOC ## `SafetyContext::apply_to_current_thread()` — Apply filter to current thread Compiles and loads the accumulated rules as a seccomp BPF filter on the calling thread only. Once applied, the filter is permanent for the thread's lifetime. Automatically includes `BasicCapabilities` (memory allocation, signal handling, exit, etc.). ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn worker_thread() { // Lock down this worker thread to only file reads SafetyContext::new() .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_close() .allow_metadata() ).unwrap() .apply_to_current_thread() // only this thread is affected .unwrap(); let contents = std::fs::read_to_string("/etc/hostname").unwrap(); println!("hostname: {}", contents.trim()); // Writing is blocked: assert!(std::fs::write("/tmp/out.txt", "data").is_err()); } fn main() { let handle = std::thread::spawn(worker_thread); handle.join().unwrap(); // Main thread is NOT restricted — filter only applied inside worker_thread() } ``` ``` -------------------------------- ### SafetyContext::apply_to_all_threads() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Applies a seccomp BPF filter to all threads in the process, including those already running, by setting SECCOMP_FILTER_FLAG_TSYNC. This cannot be used when Landlock rules are present. ```APIDOC ## `SafetyContext::apply_to_all_threads()` — Apply filter to all threads Same as `apply_to_current_thread()` but sets `SECCOMP_FILTER_FLAG_TSYNC`, which synchronizes the filter to all threads in the process, including those already running. Cannot be used when Landlock rules are present (use per-thread application instead). ```rust use extrasafe::SafetyContext; use extrasafe::builtins::{Networking, danger_zone::Threads}; use std::thread; fn main() { // Start a background server thread before locking down let _server = thread::spawn(|| { // server loop ... }); thread::sleep(std::time::Duration::from_millis(50)); // Apply to ALL threads (including the server thread already running) SafetyContext::new() .enable( Networking::nothing() .allow_running_tcp_servers() .allow_start_tcp_clients() ).unwrap() .enable( Threads::nothing() .allow_create() ).unwrap() .apply_to_all_threads() // synced to every thread via SECCOMP_FILTER_FLAG_TSYNC .unwrap(); // Creating new bindings is now blocked on all threads: assert!(std::net::TcpListener::bind("127.0.0.1:9999").is_err()); } ``` ``` -------------------------------- ### Customize blocked syscall errno with SafetyContext::with_errno() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Changes the error code returned when a syscall is denied from the default EPERM to a specified value. Use this to provide more specific error feedback. Ensure libc is available for error codes. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn main() { SafetyContext::new() .with_errno(libc::ENOSYS as u32) // return ENOSYS instead of EPERM .enable( SystemIO::nothing() .allow_stdout() ).unwrap() .apply_to_current_thread() .unwrap(); // Blocked calls now fail with ENOSYS let res = std::fs::File::open("/etc/passwd"); assert!(res.is_err()); assert_eq!(res.unwrap_err().raw_os_error(), Some(libc::ENOSYS)); } ``` -------------------------------- ### Define a Custom Seccomp RuleSet in Rust Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Implement the `RuleSet` trait to create a custom seccomp ruleset. This allows for defining both simple and conditional syscall rules, including complex argument filtering for syscalls like `socket`. ```rust use extrasafe::* use syscalls::Sysno; use std::collections::HashMap; struct MyRuleSet; impl RuleSet for MyRuleSet { fn simple_rules(&self) -> Vec { // literally reboot the computer vec![Sysno::reboot] } fn conditional_rules(&self) -> HashMap> { // Only allow the creation of stream (tcp) sockets const SOCK_STREAM: u64 = libc::SOCK_STREAM as u64; let rule = SeccompRule::new(Sysno::socket) .and_condition( seccomp_arg_filter!(arg0 & SOCK_STREAM == SOCK_STREAM)); HashMap::from([ (Sysno::socket, vec![rule,]) ]) } fn name(&self) -> &'static str { "MyRuleSet" } } // And it can be enabled just like the builtin ones: extrasafe::SafetyContext::new() .enable(MyRuleSet).unwrap() .apply_to_current_thread().unwrap(); ``` -------------------------------- ### SafetyContext::with_errno() Source: https://context7.com/boustrophedon/extrasafe/llms.txt Customizes the errno value returned when a syscall is denied. By default, blocked syscalls return EPERM (errno 1). ```APIDOC ## `SafetyContext::with_errno()` — Customize blocked-syscall errno By default, blocked syscalls return `EPERM` (errno 1). Use `with_errno()` to change the error code returned when a syscall is denied. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn main() { SafetyContext::new() .with_errno(libc::ENOSYS as u32) // return ENOSYS instead of EPERM .enable( SystemIO::nothing() .allow_stdout() ).unwrap() .apply_to_current_thread() .unwrap(); // Blocked calls now fail with ENOSYS let res = std::fs::File::open("/etc/passwd"); assert!(res.is_err()); assert_eq!(res.unwrap_err().raw_os_error(), Some(libc::ENOSYS)); } ``` ``` -------------------------------- ### Apply seccomp filter to current thread with SafetyContext Source: https://context7.com/boustrophedon/extrasafe/llms.txt Applies accumulated seccomp rules to the calling thread only. The filter is permanent for the thread's lifetime and automatically includes basic capabilities. Ensure necessary imports are present. ```rust use extrasafe::SafetyContext; use extrasafe::builtins::SystemIO; fn worker_thread() { // Lock down this worker thread to only file reads SafetyContext::new() .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_close() .allow_metadata() ).unwrap() .apply_to_current_thread() // only this thread is affected .unwrap(); let contents = std::fs::read_to_string("/etc/hostname").unwrap(); println!("hostname: {}", contents.trim()); // Writing is blocked: assert!(std::fs::write("/tmp/out.txt", "data").is_err()); } fn main() { let handle = std::thread::spawn(worker_thread); handle.join().unwrap(); // Main thread is NOT restricted — filter only applied inside worker_thread() } ``` -------------------------------- ### Define RuleSet Trait in Rust Source: https://github.com/boustrophedon/extrasafe/blob/master/user-guide.md Defines the RuleSet trait with methods for simple rules, conditional rules, name, and landlock rules. Conditional rules default to an empty HashMap, and landlock rules default to an empty Vec. ```rust /// A [`RuleSet`] is a collection of [`SeccompRule`] and [`LandlockRule`] s that enable a /// functionality, such as opening files or starting threads. pub trait RuleSet { /// A simple rule is a seccomp rule that just allows the syscall without restriction. fn simple_rules(&self) -> Vec; /// A conditional rule is a seccomp rule that uses a condition to restrict the syscall, e.g. only /// specific flags as parameters. fn conditional_rules(&self) -> HashMap> { HashMap::new() } /// The name of the profile. fn name(&self) -> &'static str; #[cfg(feature = "landlock")] /// A landlock rule is a pair of an access control (e.g. read/write access, directory creation /// access) and a directory or path. fn landlock_rules(&self) -> Vec { Vec::new() } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.