Expose the bwrap command line at /run/agent-sandbox inside every sandbox
This commit is contained in:
@@ -4,9 +4,11 @@ pub mod cli;
|
||||
pub mod config;
|
||||
mod env;
|
||||
mod errors;
|
||||
mod memfd;
|
||||
mod persistent_tmp;
|
||||
mod preflight;
|
||||
mod sandbox;
|
||||
mod sandbox_info;
|
||||
mod seccomp;
|
||||
mod session_key;
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::os::fd::FromRawFd;
|
||||
|
||||
use crate::SandboxError;
|
||||
|
||||
pub fn create_inheritable(name: &CStr) -> Result<File, SandboxError> {
|
||||
// flags=0 omits MFD_CLOEXEC so the fd survives exec into bwrap
|
||||
let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), 0) };
|
||||
if raw_fd < 0 {
|
||||
return Err(SandboxError::Io(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(unsafe { File::from_raw_fd(raw_fd) })
|
||||
}
|
||||
+9
-4
@@ -7,6 +7,7 @@ use crate::agents;
|
||||
use crate::blacklist;
|
||||
use crate::env;
|
||||
use crate::persistent_tmp::PersistentTmpDirs;
|
||||
use crate::sandbox_info::BwrapCommandLine;
|
||||
use crate::seccomp;
|
||||
use crate::{BindSpec, EnvEntry, SandboxConfig, SandboxError, SandboxMode};
|
||||
|
||||
@@ -17,7 +18,9 @@ pub struct Bwrap {
|
||||
|
||||
impl Bwrap {
|
||||
pub fn build(config: &SandboxConfig) -> Result<Self, SandboxError> {
|
||||
build_command(config)
|
||||
let (bwrap, command_line) = build_command(config)?;
|
||||
command_line.fill(&bwrap.shell_quoted())?;
|
||||
Ok(bwrap)
|
||||
}
|
||||
|
||||
pub fn exec(mut self) -> io::Error {
|
||||
@@ -32,7 +35,7 @@ impl Bwrap {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_command(config: &SandboxConfig) -> Result<Bwrap, SandboxError> {
|
||||
fn build_command(config: &SandboxConfig) -> Result<(Bwrap, BwrapCommandLine), SandboxError> {
|
||||
let mut cmd = Command::new("bwrap");
|
||||
let hardened = config.hardened || matches!(config.mode, SandboxMode::Whitelist);
|
||||
|
||||
@@ -54,6 +57,7 @@ fn build_command(config: &SandboxConfig) -> Result<Bwrap, SandboxError> {
|
||||
persistent
|
||||
}
|
||||
};
|
||||
let command_line = BwrapCommandLine::add_to(&mut cmd)?;
|
||||
|
||||
for path in agents::agent_rw_paths() {
|
||||
cmd.arg("--bind-try").arg(&path).arg(&path);
|
||||
@@ -86,10 +90,11 @@ fn build_command(config: &SandboxConfig) -> Result<Bwrap, SandboxError> {
|
||||
.arg(&config.command)
|
||||
.args(&config.command_args);
|
||||
|
||||
Ok(Bwrap {
|
||||
let bwrap = Bwrap {
|
||||
command: cmd,
|
||||
_persistent: persistent,
|
||||
})
|
||||
};
|
||||
Ok((bwrap, command_line))
|
||||
}
|
||||
|
||||
fn add_env_policy(cmd: &mut Command, config: &SandboxConfig) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::os::fd::{AsRawFd, IntoRawFd};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use crate::SandboxError;
|
||||
use crate::memfd;
|
||||
|
||||
pub const DIR: &str = "/run/agent-sandbox";
|
||||
|
||||
pub fn bwrap_command_line_path() -> PathBuf {
|
||||
Path::new(DIR).join("bwrap-args")
|
||||
}
|
||||
|
||||
/// Created empty so its fd number can appear in the invocation it later holds.
|
||||
pub struct BwrapCommandLine {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl BwrapCommandLine {
|
||||
pub fn add_to(cmd: &mut Command) -> Result<Self, SandboxError> {
|
||||
let file = memfd::create_inheritable(c"agent-sandbox-bwrap-args")?;
|
||||
|
||||
cmd.arg("--dir").arg(DIR);
|
||||
cmd.arg("--ro-bind-data")
|
||||
.arg(file.as_raw_fd().to_string())
|
||||
.arg(bwrap_command_line_path());
|
||||
|
||||
Ok(Self { file })
|
||||
}
|
||||
|
||||
pub fn fill(mut self, invocation: &str) -> Result<(), SandboxError> {
|
||||
self.file.write_all(invocation.as_bytes())?;
|
||||
self.file.write_all(b"\n")?;
|
||||
self.file.seek(SeekFrom::Start(0))?;
|
||||
let _inherited_by_bwrap = self.file.into_raw_fd();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn binds_memfd_read_only_and_fills_it_with_invocation() {
|
||||
let mut cmd = Command::new("bwrap");
|
||||
let command_line = BwrapCommandLine::add_to(&mut cmd).unwrap();
|
||||
let args: Vec<String> = cmd
|
||||
.get_args()
|
||||
.map(|a| a.to_string_lossy().into_owned())
|
||||
.collect();
|
||||
|
||||
assert_eq!(args[0..2], ["--dir", DIR]);
|
||||
assert_eq!(args[2], "--ro-bind-data");
|
||||
assert_eq!(args[4], "/run/agent-sandbox/bwrap-args");
|
||||
let fd: i32 = args[3].parse().unwrap();
|
||||
|
||||
command_line.fill("bwrap --dir /run/agent-sandbox").unwrap();
|
||||
|
||||
let content = std::fs::read_to_string(format!("/proc/self/fd/{fd}")).unwrap();
|
||||
assert_eq!(content, "bwrap --dir /run/agent-sandbox\n");
|
||||
}
|
||||
}
|
||||
+3
-12
@@ -32,7 +32,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::fd::{IntoRawFd, RawFd};
|
||||
use std::str::FromStr;
|
||||
|
||||
use seccompiler::{
|
||||
@@ -42,6 +42,7 @@ use seccompiler::{
|
||||
use syscalls::Sysno;
|
||||
|
||||
use crate::SandboxError;
|
||||
use crate::memfd;
|
||||
|
||||
/// Syscall allowlist. Includes Podman's unconditional allow set (minus syscalls
|
||||
/// we deny on top, see module docs) plus arch-specific syscalls for the targets
|
||||
@@ -55,17 +56,7 @@ const ALLOWED_SYSCALLS: &[&str] = &include!("seccomp_allowlist.in");
|
||||
pub fn write_program_to_memfd() -> Result<RawFd, SandboxError> {
|
||||
let bytes = build_program_bytes()?;
|
||||
|
||||
// Safety: memfd_create is a normal Linux syscall. We pass a valid C string
|
||||
// and flags=0, so the fd is created without MFD_CLOEXEC and survives exec
|
||||
// into bwrap. The name is only a debugging label (shows up as the symlink
|
||||
// target in /proc/<pid>/fd/<n>).
|
||||
let raw_fd = unsafe { libc::memfd_create(c"agent-sandbox-seccomp".as_ptr(), 0) };
|
||||
if raw_fd < 0 {
|
||||
return Err(SandboxError::Io(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
// Safety: raw_fd is owned by us and currently uniquely held.
|
||||
let mut file = unsafe { std::fs::File::from_raw_fd(raw_fd) };
|
||||
let mut file = memfd::create_inheritable(c"agent-sandbox-seccomp")?;
|
||||
file.write_all(&bytes)?;
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
// into_raw_fd consumes the File without closing the underlying fd.
|
||||
|
||||
Reference in New Issue
Block a user