Confine package ownership changes to private mounts

This commit is contained in:
ms2k 2026-07-27 03:31:58 +09:00
commit 8334fd379f

View file

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs::File;
@ -40,17 +41,19 @@ use validation::{
};
pub const NETWORK_NAMESPACE_PATH: &str = "/proc/self/ns/net";
pub const RUNSC_BUILD_ID: &str = "904ab3c9bd4c99a27181076f2c9ea2fecc041db5a728c999540d6664174d2cd4";
pub const RUNSC_BUILD_ID: &str = "b9ad6f93a8e890b5935061582b1a128682f3fd41c8053c54d0e44ff45d809971";
pub const RUNSC_BUILD_ID_BYTES: [u8; 32] = [
144, 74, 179, 201, 189, 76, 153, 162, 113, 129, 7, 111, 44, 158, 162, 254, 204, 4, 29, 181,
167, 40, 201, 153, 84, 13, 102, 100, 23, 77, 44, 212,
185, 173, 111, 147, 168, 232, 144, 181, 147, 80, 97, 88, 43, 26, 18, 134, 130, 243, 253, 65,
200, 5, 60, 84, 208, 228, 79, 244, 93, 128, 153, 113,
];
pub const RUNSC_EXECUTABLE_NAME: &str =
"lazaret-runsc-904ab3c9bd4c99a27181076f2c9ea2fecc041db5a728c999540d6664174d2cd4";
"lazaret-runsc-b9ad6f93a8e890b5935061582b1a128682f3fd41c8053c54d0e44ff45d809971";
pub const DOMAIN_OPEN_FILE_LIMIT: u64 = 524_288;
const OCI_VERSION: &str = "1.2.1";
const MAX_TERMINFO_BYTES: usize = 1_048_576;
const GVISOR_MOUNT_ANNOTATION_PREFIX: &str = "dev.gvisor.spec.mount.";
const OWNER_CHANGE_MOUNTS_ANNOTATION: &str = "dev.lazaret.runsc.owner-change-mounts";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SandboxBuildErrorKind {
@ -691,8 +694,10 @@ fn build_sandbox_bundle_from_source(
.readonly(false)
.build()
.map_err(oci_error)?;
let annotations = build_mount_annotations(request.configuration, &mounts)?;
let specification = SpecBuilder::default()
.version(OCI_VERSION)
.annotations(annotations)
.root(root)
.mounts(mounts)
.process(process)
@ -723,6 +728,54 @@ fn build_sandbox_bundle_from_source(
})
}
fn build_mount_annotations(
configuration: &ResolvedDomainConfiguration,
mounts: &[Mount],
) -> Result<HashMap<String, String>, SandboxBuildError> {
let owner_change_mounts = std::iter::once("/".to_owned())
.chain(
configuration
.fingerprint_record()
.persistent_dirs
.iter()
.cloned(),
)
.collect::<Vec<_>>();
let mut annotations = HashMap::from([(
OWNER_CHANGE_MOUNTS_ANNOTATION.to_owned(),
serde_json::to_string(&owner_change_mounts).map_err(|error| {
SandboxBuildError::new(SandboxBuildErrorKind::JsonEncoding, error.to_string())
})?,
)]);
for (index, mount) in mounts.iter().enumerate() {
let destination = mount.destination().to_str().ok_or_else(|| {
SandboxBuildError::new(
SandboxBuildErrorKind::InvalidPath,
"mount destination is not UTF-8",
)
})?;
if mount.typ().as_deref() != Some("bind")
|| owner_change_mounts
.iter()
.any(|allowed| allowed == destination)
{
continue;
}
let Some(source) = mount.source().as_ref().and_then(|path| path.to_str()) else {
return Err(SandboxBuildError::new(
SandboxBuildErrorKind::InvalidPath,
"bind mount source is not UTF-8",
));
};
let prefix = format!("{GVISOR_MOUNT_ANNOTATION_PREFIX}lazaret{index}.");
annotations.insert(format!("{prefix}source"), source.to_owned());
annotations.insert(format!("{prefix}type"), "bind".to_owned());
annotations.insert(format!("{prefix}share"), "shared".to_owned());
annotations.insert(format!("{prefix}directfs"), "off".to_owned());
}
Ok(annotations)
}
fn canonical_specification_json(specification: &Spec) -> Result<Vec<u8>, SandboxBuildError> {
let mut value = serde_json::to_value(specification).map_err(|error| {
SandboxBuildError::new(SandboxBuildErrorKind::JsonEncoding, error.to_string())