Ensure root filesystem is always read-only inside sandbox
Whitelist mode's implicit bwrap root was a writable tmpfs, letting the sandboxed process create files and directories anywhere not covered by an explicit ro mount. This was not an issue in blacklist mode due to --ro-bind / / covering that case. This patch adds --remount-ro / before any other mount to make the base layer read-only in both modes.
This commit is contained in:
@@ -34,6 +34,7 @@ pub fn build_command(config: &SandboxConfig) -> Result<Command, SandboxError> {
|
|||||||
add_ro_bind(&mut cmd, path)?;
|
add_ro_bind(&mut cmd, path)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.args(["--remount-ro", "/"]);
|
||||||
cmd.arg("--new-session");
|
cmd.arg("--new-session");
|
||||||
cmd.arg("--die-with-parent");
|
cmd.arg("--die-with-parent");
|
||||||
cmd.arg("--chdir").arg(&config.chdir);
|
cmd.arg("--chdir").arg(&config.chdir);
|
||||||
|
|||||||
@@ -420,6 +420,107 @@ fn blacklist_dev_input_hidden() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blacklist_root_is_readonly() {
|
||||||
|
let output = sandbox(&[])
|
||||||
|
.args([
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"touch /rootfile 2>&1 && echo WRITABLE || echo READONLY; \
|
||||||
|
mkdir /newdir 2>&1 && echo MKDIR_OK || echo MKDIR_FAIL",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.expect("agent-sandbox binary failed to execute");
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("READONLY"),
|
||||||
|
"expected root to be read-only in blacklist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("MKDIR_FAIL"),
|
||||||
|
"expected mkdir at root to fail in blacklist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn whitelist_root_is_readonly() {
|
||||||
|
let output = sandbox(&["--whitelist"])
|
||||||
|
.args([
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"touch /rootfile 2>&1 && echo WRITABLE || echo READONLY; \
|
||||||
|
mkdir /newdir 2>&1 && echo MKDIR_OK || echo MKDIR_FAIL",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.expect("agent-sandbox binary failed to execute");
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("READONLY"),
|
||||||
|
"expected root to be read-only in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("MKDIR_FAIL"),
|
||||||
|
"expected mkdir at root to fail in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn whitelist_mountpoint_parents_are_readonly() {
|
||||||
|
let output = sandbox(&["--whitelist"])
|
||||||
|
.args([
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"echo pwned > /home/testfile 2>&1 && echo HOME_WRITABLE || echo HOME_READONLY; \
|
||||||
|
touch /etc/newfile 2>&1 && echo ETC_WRITABLE || echo ETC_READONLY; \
|
||||||
|
touch /var/newfile 2>&1 && echo VAR_WRITABLE || echo VAR_READONLY",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.expect("agent-sandbox binary failed to execute");
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("HOME_READONLY"),
|
||||||
|
"expected /home to be read-only in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("ETC_READONLY"),
|
||||||
|
"expected /etc to be read-only in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("VAR_READONLY"),
|
||||||
|
"expected /var to be read-only in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn whitelist_tmp_still_writable() {
|
||||||
|
let output = sandbox(&["--whitelist"])
|
||||||
|
.args([
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"touch /tmp/ok 2>&1 && echo TMP_WRITABLE || echo TMP_READONLY; \
|
||||||
|
touch /var/tmp/ok 2>&1 && echo VARTMP_WRITABLE || echo VARTMP_READONLY",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.expect("agent-sandbox binary failed to execute");
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("TMP_WRITABLE"),
|
||||||
|
"expected /tmp to remain writable in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("VARTMP_WRITABLE"),
|
||||||
|
"expected /var/tmp to remain writable in whitelist mode, got: {stdout}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rw_missing_path_errors() {
|
fn rw_missing_path_errors() {
|
||||||
let output = sandbox(&["--rw", "/nonexistent/xyz"])
|
let output = sandbox(&["--rw", "/nonexistent/xyz"])
|
||||||
|
|||||||
Reference in New Issue
Block a user