Expose the bwrap command line at /run/agent-sandbox inside every sandbox
This commit is contained in:
@@ -5,4 +5,5 @@ mod env;
|
||||
mod modes;
|
||||
mod mounts;
|
||||
mod namespaces;
|
||||
mod sandbox_info;
|
||||
mod seccomp;
|
||||
|
||||
@@ -373,6 +373,7 @@ fn blacklist_overlays_survive_absolute_var_run_symlink() {
|
||||
// layout inside the sandbox to reproduce on any host.
|
||||
let _guard = HostGlobsLock::for_scan();
|
||||
let mut bwrap_args = build_bwrap_command(&["--blacklist", "--no-seccomp", "--", "true"]);
|
||||
strip_command_line_bind(&mut bwrap_args);
|
||||
inject_absolute_var_run_symlink(&mut bwrap_args);
|
||||
|
||||
let output = Command::new(&bwrap_args[0])
|
||||
@@ -568,6 +569,15 @@ fn rand_suffix() -> String {
|
||||
format!("{nanos:08x}")
|
||||
}
|
||||
|
||||
// The bind reads from an fd that only exists in the agent-sandbox process.
|
||||
fn strip_command_line_bind(bwrap_args: &mut Vec<String>) {
|
||||
let start = bwrap_args
|
||||
.iter()
|
||||
.position(|a| a == "--ro-bind-data")
|
||||
.expect("dry-run output should bind the bwrap command line");
|
||||
bwrap_args.drain(start..start + 3);
|
||||
}
|
||||
|
||||
fn inject_absolute_var_run_symlink(bwrap_args: &mut Vec<String>) {
|
||||
assert_eq!(bwrap_args[1], "--ro-bind");
|
||||
assert_eq!(bwrap_args[2], "/");
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
use crate::common::*;
|
||||
|
||||
const BWRAP_COMMAND_LINE_PATH: &str = "/run/agent-sandbox/bwrap-args";
|
||||
|
||||
#[test]
|
||||
fn whitelist_exposes_exact_bwrap_invocation() {
|
||||
assert_exposes_exact_invocation(&[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_exposes_exact_bwrap_invocation() {
|
||||
assert_exposes_exact_invocation(&["--blacklist"]);
|
||||
}
|
||||
|
||||
fn assert_exposes_exact_invocation(mode_args: &[&str]) {
|
||||
let output = Sandbox::new(mode_args)
|
||||
.args(["--", "cat", BWRAP_COMMAND_LINE_PATH])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"reading {BWRAP_COMMAND_LINE_PATH} failed: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
let argv = shlex::split(stdout.trim()).expect("file content is not valid shell");
|
||||
assert_eq!(argv[0], "bwrap");
|
||||
assert!(
|
||||
argv.windows(3)
|
||||
.any(|w| w[0] == "--ro-bind-data" && w[2] == BWRAP_COMMAND_LINE_PATH),
|
||||
"invocation should include the bind of itself, got: {stdout}"
|
||||
);
|
||||
let [separator, command, argument] = &argv[argv.len() - 3..] else {
|
||||
unreachable!()
|
||||
};
|
||||
assert_eq!(separator, "--");
|
||||
assert!(
|
||||
command.ends_with("/cat"),
|
||||
"resolved inner command, got: {command}"
|
||||
);
|
||||
assert_eq!(argument, BWRAP_COMMAND_LINE_PATH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bwrap_command_line_is_read_only() {
|
||||
let output = Sandbox::new(&[])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
"-c",
|
||||
&format!("echo x >> {BWRAP_COMMAND_LINE_PATH} && echo WRITABLE || echo READ_ONLY"),
|
||||
])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert_eq!(stdout.trim(), "READ_ONLY");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_rw_bind_can_override_run_agent_sandbox() {
|
||||
let dir = tempfile::TempDir::new().unwrap();
|
||||
let dir_str = dir.path().to_str().unwrap();
|
||||
|
||||
let output = Sandbox::new(&["--rw", &format!("{dir_str}:/run/agent-sandbox")])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
"-c",
|
||||
&format!("test -e {BWRAP_COMMAND_LINE_PATH} || echo GONE"),
|
||||
])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(
|
||||
stdout.contains("GONE"),
|
||||
"user --rw must win over the info directory, got: {stdout}"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user