Skip to content

Commit 8719fc9

Browse files
authored
fix(sandbox): restrict provider file mode (#4093)
* fix(sandbox): restrict provider file mode (fixes #4091) Signed-off-by: Drew Newberry <anewberry@nvidia.com> * refactor(sandbox): set provider file mode with safe API Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent 76cfd0e commit 8719fc9

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

‎crates/openshell-sandbox/src/provider_files.rs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
use std::collections::HashMap;
99
use std::ffi::CString;
10-
use std::fs::File;
10+
use std::fs::{File, Permissions};
1111
use std::io::{self, Seek as _, SeekFrom, Write as _};
1212
use std::os::fd::{AsRawFd as _, FromRawFd as _};
13+
use std::os::unix::fs::PermissionsExt as _;
1314
use std::sync::{Arc, RwLock};
1415

1516
use openshell_isolation_interface::linux::seccomp_notify::{Notification, NotificationListener};
@@ -211,6 +212,9 @@ fn sealed_memfd(content: &[u8]) -> io::Result<File> {
211212
return Err(io::Error::last_os_error());
212213
}
213214
let mut file = unsafe { File::from_raw_fd(fd) };
215+
// memfd_create defaults to 0777. Keep the metadata private as well as the
216+
// returned descriptor read-only, since workloads may inspect it with fstat.
217+
file.set_permissions(Permissions::from_mode(0o600))?;
214218
file.write_all(content)?;
215219
file.seek(SeekFrom::Start(0))?;
216220
let seals = libc::F_SEAL_SEAL | libc::F_SEAL_WRITE | libc::F_SEAL_GROW | libc::F_SEAL_SHRINK;
@@ -228,6 +232,7 @@ mod tests {
228232
use std::collections::HashMap;
229233
use std::io::Read as _;
230234
use std::os::fd::AsRawFd as _;
235+
use std::os::unix::fs::PermissionsExt as _;
231236

232237
#[test]
233238
fn paths_cannot_escape_the_managed_tree() {
@@ -249,6 +254,7 @@ mod tests {
249254
#[test]
250255
fn memfd_is_read_only_and_positioned_at_start() {
251256
let mut file = sealed_memfd(b"version = 1\n").unwrap();
257+
assert_eq!(file.metadata().unwrap().permissions().mode() & 0o777, 0o600);
252258
let flags = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL) };
253259
assert_eq!(flags & libc::O_ACCMODE, libc::O_RDONLY);
254260
let mut read = String::new();

‎e2e/rust/tests/provider_files.rs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ async fn provider_file_open_update_and_detach() -> Result<(), String> {
107107
if !before.contains("project = \"production\"") {
108108
return Err(format!("initial provider file content missing:\n{before}"));
109109
}
110+
let permissions = sandbox
111+
.exec(&[
112+
"sh",
113+
"-c",
114+
&format!(
115+
"set -eu; exec 3<{path}; test \"$(stat -Lc %a /proc/self/fd/3)\" = 600; \
116+
test \"$(stat -Lc %F /proc/self/fd/3)\" = 'regular file'; \
117+
if (printf x >&3) 2>/dev/null; then exit 1; fi; \
118+
echo provider-file-permissions-ok"
119+
),
120+
])
121+
.await?;
122+
if !permissions.contains("provider-file-permissions-ok") {
123+
return Err(format!(
124+
"provider file permission assertion did not complete:\n{permissions}"
125+
));
126+
}
110127

111128
cli_ok(&[
112129
"provider",

0 commit comments

Comments
 (0)