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:
@@ -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]
|
||||
fn rw_missing_path_errors() {
|
||||
let output = sandbox(&["--rw", "/nonexistent/xyz"])
|
||||
|
||||
Reference in New Issue
Block a user