From 6d7080fbdef601e7d9ced6bb5b600501bda97785 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sun, 16 Apr 2023 14:09:53 +0200 Subject: [PATCH] feat: added mounts to sandboxing --- src/config/schema_blanks/mod.rs | 2 +- .../{sandbox.rs => sandbox/mod.rs} | 55 ++++++++++++++++-- src/config/schema_blanks/sandbox/mounts.rs | 57 +++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) rename src/config/schema_blanks/{sandbox.rs => sandbox/mod.rs} (65%) create mode 100644 src/config/schema_blanks/sandbox/mounts.rs diff --git a/src/config/schema_blanks/mod.rs b/src/config/schema_blanks/mod.rs index 6e83ffa..3b70764 100644 --- a/src/config/schema_blanks/mod.rs +++ b/src/config/schema_blanks/mod.rs @@ -24,5 +24,5 @@ pub mod prelude { pub use super::gamescope::prelude::*; #[cfg(feature = "sandbox")] - pub use super::sandbox::Sandbox; + pub use super::sandbox::*; } diff --git a/src/config/schema_blanks/sandbox.rs b/src/config/schema_blanks/sandbox/mod.rs similarity index 65% rename from src/config/schema_blanks/sandbox.rs rename to src/config/schema_blanks/sandbox/mod.rs index 7023268..2045b60 100644 --- a/src/config/schema_blanks/sandbox.rs +++ b/src/config/schema_blanks/sandbox/mod.rs @@ -1,6 +1,10 @@ use serde::{Serialize, Deserialize}; use serde_json::Value as JsonValue; +mod mounts; + +pub use mounts::Mounts; + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Sandbox { /// Use `bwrap` to run the game. Default is `true` @@ -9,8 +13,14 @@ pub struct Sandbox { /// Mount tmpfs to `/home`, `/var/home/$USER` and `$HOME`. Default is `true` pub isolate_home: bool, + /// Spoof original hostname. Default is `None` + pub hostname: Option, + /// List of paths to which tmpfs will be mounted. Default is empty - pub private: Vec + pub private: Vec, + + /// Maps of directories mounts + pub mounts: Mounts } impl Default for Sandbox { @@ -19,7 +29,9 @@ impl Default for Sandbox { Self { enabled: false, isolate_home: true, - private: vec![] + hostname: None, + private: vec![], + mounts: Mounts::default() } } } @@ -39,6 +51,20 @@ impl From<&JsonValue> for Sandbox { None => default.isolate_home }, + hostname: match value.get("hostname") { + Some(value) => { + if value.is_null() { + None + } else { + match value.as_str() { + Some(value) => Some(value.to_string()), + None => default.hostname + } + } + }, + None => default.hostname + }, + private: match value.get("private") { Some(value) => match value.as_array() { Some(values) => { @@ -55,6 +81,11 @@ impl From<&JsonValue> for Sandbox { None => default.private }, None => default.private + }, + + mounts: match value.get("mounts") { + Some(value) => Mounts::from(value), + None => default.mounts } } } @@ -75,9 +106,15 @@ impl Sandbox { /// | `wine_dir` | `/tmp/sandbox/wine` | bind | false | /// | `prefix_dir` | `/tmp/sandbox/prefix` | bind | false | /// | `game_dir` | `/tmp/sandbox/game` | bind | false | + /// | | | read-only bind | true | + /// | | | bind | true | pub fn get_command(&self, wine_dir: impl AsRef, prefix_dir: impl AsRef, game_dir: impl AsRef) -> String { let mut command = String::from("bwrap --ro-bind / /"); + if let Some(hostname) = &self.hostname { + command += &format!(" --hostname '{hostname}'"); + } + if self.isolate_home { command.push_str(" --tmpfs /home"); command.push_str(" --tmpfs /var/home"); @@ -97,9 +134,17 @@ impl Sandbox { command.push_str(" --tmpfs /tmp"); - command.push_str(&format!(" --bind '{}' /tmp/sandbox/wine", wine_dir.as_ref())); - command.push_str(&format!(" --bind '{}' /tmp/sandbox/prefix", prefix_dir.as_ref())); - command.push_str(&format!(" --bind '{}' /tmp/sandbox/game", game_dir.as_ref())); + for (from, to) in &self.mounts.read_only { + command += &format!(" --ro-bind '{}' '{}'", from.trim(), to.trim()); + } + + for (from, to) in &self.mounts.bind { + command += &format!(" --bind '{}' '{}'", from.trim(), to.trim()); + } + + command += &format!(" --bind '{}' /tmp/sandbox/wine", wine_dir.as_ref()); + command += &format!(" --bind '{}' /tmp/sandbox/prefix", prefix_dir.as_ref()); + command += &format!(" --bind '{}' /tmp/sandbox/game", game_dir.as_ref()); command.push_str(" --chdir /"); command.push_str(" --die-with-parent"); diff --git a/src/config/schema_blanks/sandbox/mounts.rs b/src/config/schema_blanks/sandbox/mounts.rs new file mode 100644 index 0000000..b9986ce --- /dev/null +++ b/src/config/schema_blanks/sandbox/mounts.rs @@ -0,0 +1,57 @@ +use std::collections::HashMap; + +use serde::{Serialize, Deserialize}; +use serde_json::Value as JsonValue; + +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Mounts { + /// Bind original directory into the sandbox in read-only state + pub read_only: HashMap, + + /// Bind original directory into the sandbox with writing access + pub bind: HashMap +} + +impl From<&JsonValue> for Mounts { + fn from(value: &JsonValue) -> Self { + let default = Self::default(); + + Self { + read_only: match value.get("read_only") { + Some(value) => match value.as_object() { + Some(values) => { + let mut vars = HashMap::new(); + + for (name, value) in values { + if let Some(value) = value.as_str() { + vars.insert(name.clone(), value.to_string()); + } + } + + vars + }, + None => default.read_only + }, + None => default.read_only + }, + + bind: match value.get("bind") { + Some(value) => match value.as_object() { + Some(values) => { + let mut vars = HashMap::new(); + + for (name, value) in values { + if let Some(value) = value.as_str() { + vars.insert(name.clone(), value.to_string()); + } + } + + vars + }, + None => default.bind + }, + None => default.bind + } + } + } +}