fix seccomp and add tracing-mode project feature
This commit is contained in:
parent
51064a503b
commit
3305f70f77
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -20,7 +20,9 @@
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"args": [
|
||||
"/usr/bin/ls"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"RUST_LOG": "DEBUG"
|
||||
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"rust-analyzer.cargo.features": [
|
||||
"tracing-mode"
|
||||
]
|
||||
}
|
124
Cargo.lock
generated
124
Cargo.lock
generated
@ -11,6 +11,54 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
@ -35,6 +83,52 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
@ -58,6 +152,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
@ -200,6 +300,23 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.3.0"
|
||||
@ -215,6 +332,12 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -317,6 +440,7 @@ name = "x2t-sandbox"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"libseccomp",
|
||||
"log",
|
||||
|
@ -12,12 +12,16 @@ members = [
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
env_logger = "0.10.0"
|
||||
libseccomp = "0.3.0"
|
||||
log = "0.4.20"
|
||||
nix = { version = "0.27.1", features = [ "process" ] }
|
||||
nix = { version = "0.27.1", features = [ "process", "ptrace", "signal" ] }
|
||||
x2t-sandbox-rulegen = { path = "./x2t-sandbox-rulegen" }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.75"
|
||||
pkg-config = "0.3.27"
|
||||
|
||||
[features]
|
||||
tracing-mode = ["x2t-sandbox-rulegen/no-fail"]
|
||||
|
14
README.md
14
README.md
@ -22,6 +22,20 @@ cargo build
|
||||
cargo run
|
||||
```
|
||||
|
||||
### Generate syscalls with strace
|
||||
|
||||
```
|
||||
strace -f --output x2t-syscalls.txt /path/to/x2t some.xml
|
||||
```
|
||||
|
||||
### Generate syscalls with tracing mode
|
||||
|
||||
cargo 开启 tracing-mode 后,宏找不到环境变量和文件不会失败,可以直接生成一个。
|
||||
|
||||
```
|
||||
cargo run --features tracing-mode -- -l x2t-syscalls.txt /path/to/x2t some.xml
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
|
||||
- [项目](/)
|
||||
|
4
build.rs
4
build.rs
@ -1,5 +1,9 @@
|
||||
fn main() -> anyhow::Result<()> {
|
||||
pkg_config::probe_library("libseccomp")?;
|
||||
|
||||
println!("cargo:rerun-if-changed=x2t-syscalls.txt");
|
||||
println!("cargo:rerun-if-env-changed=X2T_SYSCALLS_FILE");
|
||||
println!("cargo:rerun-if-env-changed=X2T_SYSCALLS");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
};
|
||||
|
||||
buildWithPackages = pkgs: pkgsStatic: (buildRustPlatform pkgsStatic).buildRustPackage rec {
|
||||
pname = "hello";
|
||||
pname = "x2t-sandbox";
|
||||
version = "1.0.0";
|
||||
|
||||
nativeBuildInputs = buildTools pkgs;
|
||||
@ -72,9 +72,10 @@
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
rec {
|
||||
hello = buildWithPackages pkgs pkgs.pkgsStatic;
|
||||
x2t-sandbox-musl = buildWithPackages pkgs pkgs.pkgsStatic;
|
||||
x2t-sandbox-glibc = buildWithPackages pkgs pkgs;
|
||||
|
||||
default = hello;
|
||||
default = x2t-sandbox-musl;
|
||||
});
|
||||
devShells = foreachSystem
|
||||
(system:
|
||||
|
145
src/main.rs
145
src/main.rs
@ -1,38 +1,149 @@
|
||||
use std::{ffi::CString, ptr::null};
|
||||
use nix::{
|
||||
libc::{c_long, EPERM, ORIG_RAX},
|
||||
sys::signal::Signal,
|
||||
unistd::{getpid, getppid},
|
||||
};
|
||||
use std::{
|
||||
ffi::{c_void, CString},
|
||||
mem::size_of,
|
||||
};
|
||||
use std::io::Write;
|
||||
|
||||
use clap::*;
|
||||
|
||||
use libseccomp::*;
|
||||
use nix::libc::EPERM;
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
struct Args {
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
#[clap(short, long)]
|
||||
log_failed_to: Option<String>,
|
||||
|
||||
command: Vec<String>,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
log::info!("restrict myself by set_no_new_privs...");
|
||||
nix::sys::prctl::set_no_new_privs()?;
|
||||
let args = Args::parse();
|
||||
|
||||
log::info!("create filter context...");
|
||||
log::trace!("args parsed: {args:?}");
|
||||
|
||||
let mut filter = ScmpFilterContext::new_filter(ScmpAction::Errno(EPERM))?;
|
||||
|
||||
log::info!("add architecture to filter context...");
|
||||
filter.add_arch(ScmpArch::X8664)?;
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
let tracing = args.log_failed_to != None;
|
||||
|
||||
let mut default_action = ScmpAction::Errno(EPERM);
|
||||
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
if let Some(log_fail_to) = args.log_failed_to {
|
||||
use nix::sys::{
|
||||
ptrace::Options,
|
||||
wait::{waitpid, WaitStatus},
|
||||
};
|
||||
log::trace!("tracing mode. forking into a tracer...");
|
||||
|
||||
let fork_result = match unsafe { nix::unistd::fork() } {
|
||||
Err(err) => panic!("failed to fork a child: {}", err),
|
||||
Ok(pid) => pid,
|
||||
};
|
||||
|
||||
log::trace!("fork result: {fork_result:?}");
|
||||
|
||||
if let nix::unistd::ForkResult::Parent { child } = fork_result {
|
||||
// i'm parent, setting up ptrace
|
||||
|
||||
log::trace!("waiting for child to be ready...");
|
||||
waitpid(child, None)?;
|
||||
|
||||
let mut output = match std::fs::OpenOptions::new().append(true).write(true).create(true).open(log_fail_to) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
nix::sys::ptrace::kill(child)?;
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
nix::sys::ptrace::setoptions(child, Options::PTRACE_O_TRACESECCOMP)?;
|
||||
nix::sys::ptrace::cont(child, None)?;
|
||||
log::trace!("child is ready");
|
||||
|
||||
loop {
|
||||
log::debug!("parent: waitpid...");
|
||||
match waitpid(child, None)? {
|
||||
WaitStatus::Exited(pid, ret) => {
|
||||
log::info!("child {pid} exited with return code {ret}");
|
||||
break;
|
||||
}
|
||||
WaitStatus::PtraceEvent(pid, sig,_) => {
|
||||
let syscall_nr = nix::sys::ptrace::read_user(
|
||||
pid,
|
||||
(size_of::<c_long>() * ORIG_RAX as usize) as *mut c_void,
|
||||
)? as i32;
|
||||
let syscall = ScmpSyscall::from(syscall_nr);
|
||||
let syscall_name = syscall.get_name().unwrap_or(format!("syscall({syscall_nr})"));
|
||||
log::info!("parent: child {pid} received signal {sig:?} syscall: {syscall_name}({syscall_nr})");
|
||||
|
||||
writeln!(output, "{} {}", pid.as_raw(), syscall_name);
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
nix::sys::ptrace::cont(child, None);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let command: Vec<_> = args
|
||||
.command
|
||||
.iter()
|
||||
.map(|s| CString::new(s.as_str()).unwrap())
|
||||
.collect();
|
||||
log::trace!("command: {command:?}");
|
||||
let executable = CString::new(args.command[0].clone()).unwrap();
|
||||
log::trace!("executable: {executable:?}");
|
||||
let env: Vec<CString> = std::env::vars()
|
||||
.into_iter()
|
||||
.map(|(k, v)| format!("{k}={v}"))
|
||||
.map(|s| CString::new(s).unwrap())
|
||||
.collect();
|
||||
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
if tracing {
|
||||
// i'm child. trace me
|
||||
log::debug!("child: traceme");
|
||||
nix::sys::ptrace::traceme()?;
|
||||
|
||||
let me = getpid();
|
||||
log::info!("waiting for parent to be ready...");
|
||||
nix::sys::signal::kill(me, nix::sys::signal::Signal::SIGSTOP)?;
|
||||
log::info!("parent is ready");
|
||||
|
||||
let parent = getppid();
|
||||
default_action = ScmpAction::Trace(parent.as_raw() as u16);
|
||||
log::info!("set default action to {default_action:?}");
|
||||
}
|
||||
|
||||
log::trace!("create filter context...");
|
||||
let mut filter = ScmpFilterContext::new_filter(default_action)?;
|
||||
|
||||
filter.add_rule(ScmpAction::Allow, ScmpSyscall::from(nix::libc::SYS_execve as i32))?;
|
||||
|
||||
x2t_sandbox_rulegen::generate! {
|
||||
log::info!("accepting {}", syscall_name);
|
||||
log::trace!("accepting {}({})", syscall_name, syscall_nr);
|
||||
};
|
||||
|
||||
log::info!("load filter into kernel...");
|
||||
log::debug!("restrict myself by set_no_new_privs...");
|
||||
nix::sys::prctl::set_no_new_privs()?;
|
||||
|
||||
log::info!("loading filter into kernel...");
|
||||
if let Err(err) = filter.load() {
|
||||
log::error!("failed to load filter into kernel: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
log::trace!("loaded");
|
||||
|
||||
let args: Vec<_> = std::env::args().map(|s| CString::new(s).unwrap()).collect();
|
||||
let command = std::env::args().next().unwrap();
|
||||
let command = CString::new(command).unwrap();
|
||||
let env: Vec<CString> = Vec::new();
|
||||
|
||||
log::info!("executing {:?}", args);
|
||||
if let Err(err) = nix::unistd::execve(&command, args.as_slice(), env.as_slice()) {
|
||||
log::debug!("executing {:?}", args.command);
|
||||
if let Err(err) = nix::unistd::execve(&executable, command.as_slice(), env.as_slice()) {
|
||||
panic!("failed to execve for {err}");
|
||||
}
|
||||
|
||||
|
@ -14,3 +14,5 @@ quote = "1"
|
||||
nix = "0.27.1"
|
||||
regex = "1.10.2"
|
||||
|
||||
[features]
|
||||
no-fail = []
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use proc_macro::*;
|
||||
use quote::{quote, format_ident, TokenStreamExt};
|
||||
use quote::{quote, format_ident};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn generate(input: TokenStream) -> TokenStream {
|
||||
@ -15,7 +15,14 @@ pub fn generate(input: TokenStream) -> TokenStream {
|
||||
} else if let Ok(syscalls) = std::fs::read_to_string(&syscall_filepath) {
|
||||
generate_from(input.clone(), syscalls, "\n").into()
|
||||
} else {
|
||||
#[cfg(not(feature = "no-fail"))]
|
||||
panic!("either specify a X2T_SYSCALLS environment variable with values seperated by colon or write the allowed syscalls line by line into {}", syscall_filepath.to_string_lossy());
|
||||
|
||||
#[cfg(feature = "no-fail")]
|
||||
{
|
||||
eprintln!("x2t syscalls not found. macro is not failing. but you program may fail. turn on tracing-mode to find out what happened");
|
||||
TokenStream::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +57,7 @@ fn generate_from(input: proc_macro::TokenStream, buf: String, sep: &str) -> proc
|
||||
{
|
||||
let syscall_nr = nix::libc::#libc_name;
|
||||
let syscall_name = #syscall_name;
|
||||
filter.add_rule_conditional(ScmpAction::Allow, syscall_nr as i32, &[])?;
|
||||
filter.add_rule(ScmpAction::Allow, syscall_nr as i32)?;
|
||||
#hook
|
||||
}
|
||||
}
|
||||
|
1045
x2t-syscalls.txt
1045
x2t-syscalls.txt
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user