Restore persistent system path composition
This commit is contained in:
parent
84bb22a7db
commit
7aff8e542e
3 changed files with 338 additions and 30 deletions
|
|
@ -113,6 +113,7 @@ impl Ipv4Prefix {
|
|||
|
||||
pub struct SandboxLayoutV1 {
|
||||
protected_destinations: BTreeSet<String>,
|
||||
nested_transient_overrides: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SandboxLayoutV1 {
|
||||
|
|
@ -120,6 +121,10 @@ impl fmt::Debug for SandboxLayoutV1 {
|
|||
formatter
|
||||
.debug_struct("SandboxLayoutV1")
|
||||
.field("protected_destinations", &self.protected_destinations)
|
||||
.field(
|
||||
"nested_transient_overrides",
|
||||
&self.nested_transient_overrides,
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -132,9 +137,21 @@ impl SandboxLayoutV1 {
|
|||
/// include `/workspace`.
|
||||
pub fn from_protected_destinations(
|
||||
destinations: impl IntoIterator<Item = String>,
|
||||
) -> Result<Self, ConfigurationError> {
|
||||
Self::from_destination_policy(destinations, [])
|
||||
}
|
||||
|
||||
/// Builds protected destination classes supplied by sandbox-layout construction.
|
||||
///
|
||||
/// # Errors
|
||||
/// Destinations must be normalized, non-root, unique across classes, and include
|
||||
/// `/workspace`; nested overrides must be exact product-owned transient mounts.
|
||||
pub fn from_destination_policy(
|
||||
exclusive: impl IntoIterator<Item = String>,
|
||||
nested_transient_overrides: impl IntoIterator<Item = String>,
|
||||
) -> Result<Self, ConfigurationError> {
|
||||
let mut protected_destinations = BTreeSet::new();
|
||||
for destination in destinations {
|
||||
for destination in exclusive {
|
||||
validate_sandbox_path(&destination)?;
|
||||
if !protected_destinations.insert(destination) {
|
||||
return Err(ConfigurationError::new(
|
||||
|
|
@ -143,6 +160,16 @@ impl SandboxLayoutV1 {
|
|||
));
|
||||
}
|
||||
}
|
||||
let mut nested = BTreeSet::new();
|
||||
for destination in nested_transient_overrides {
|
||||
validate_sandbox_path(&destination)?;
|
||||
if !nested.insert(destination.clone()) || !protected_destinations.insert(destination) {
|
||||
return Err(ConfigurationError::new(
|
||||
ConfigurationErrorKind::InvalidPath,
|
||||
"duplicate protected sandbox destination",
|
||||
));
|
||||
}
|
||||
}
|
||||
if !protected_destinations.contains("/workspace") {
|
||||
return Err(ConfigurationError::new(
|
||||
ConfigurationErrorKind::InvalidPath,
|
||||
|
|
@ -151,6 +178,7 @@ impl SandboxLayoutV1 {
|
|||
}
|
||||
Ok(Self {
|
||||
protected_destinations,
|
||||
nested_transient_overrides: nested,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +186,11 @@ impl SandboxLayoutV1 {
|
|||
pub fn protected_destinations(&self) -> &BTreeSet<String> {
|
||||
&self.protected_destinations
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn nested_transient_overrides(&self) -> &BTreeSet<String> {
|
||||
&self.nested_transient_overrides
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfinedPathPlan {
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ fn validate_owned_directory(descriptor: &OwnedFd, uid: u32) -> Result<(), Config
|
|||
pub(super) fn validate_persistent_destinations(
|
||||
destinations: &[String],
|
||||
protected: &BTreeSet<String>,
|
||||
nested_transient_overrides: &BTreeSet<String>,
|
||||
) -> Result<Vec<String>, ConfigurationError> {
|
||||
require_pairwise_non_ancestral(
|
||||
destinations.iter().map(String::as_str),
|
||||
|
|
@ -272,11 +273,32 @@ pub(super) fn validate_persistent_destinations(
|
|||
)?;
|
||||
for destination in destinations {
|
||||
validate_sandbox_path(destination)?;
|
||||
require_clear_of_protected(destination, protected)?;
|
||||
require_persistent_clear_of_protected(destination, protected, nested_transient_overrides)?;
|
||||
}
|
||||
Ok(destinations.to_vec())
|
||||
}
|
||||
|
||||
fn require_persistent_clear_of_protected(
|
||||
destination: &str,
|
||||
protected: &BTreeSet<String>,
|
||||
nested_transient_overrides: &BTreeSet<String>,
|
||||
) -> Result<(), ConfigurationError> {
|
||||
let conflict = protected.iter().any(|protected| {
|
||||
destination == protected
|
||||
|| sandbox_is_strict_descendant(destination, protected)
|
||||
|| (sandbox_is_strict_descendant(protected, destination)
|
||||
&& !nested_transient_overrides.contains(protected))
|
||||
});
|
||||
if conflict {
|
||||
Err(ConfigurationError::new(
|
||||
ConfigurationErrorKind::PathConflict,
|
||||
"configured sandbox destination intersects a protected destination",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn require_clear_of_protected(
|
||||
destination: &str,
|
||||
protected: &BTreeSet<String>,
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ pub fn load_trusted_configuration(
|
|||
let persistent_dirs = validate_persistent_destinations(
|
||||
&selected.persistent_dirs,
|
||||
request.sandbox_layout.protected_destinations(),
|
||||
request.sandbox_layout.nested_transient_overrides(),
|
||||
)?;
|
||||
let (effective_allow_prefixes, effective_deny_prefixes) =
|
||||
resolve_network(&configuration, selected)?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue