Compose selected State Anchor paths over prepared roots
This commit is contained in:
parent
71b0266cf2
commit
4dd2b150d2
1 changed files with 1575 additions and 163 deletions
|
|
@ -219,6 +219,8 @@ pub struct SandboxHandoffAuthority<'configuration> {
|
|||
configuration_authority: ConfigurationAuthorityIdentity,
|
||||
command: RunscCommand,
|
||||
derived: Vec<DerivedHandoffAuthority>,
|
||||
prepared_root: PathBuf,
|
||||
prepared_root_identity: FileIdentity,
|
||||
}
|
||||
|
||||
impl SandboxHandoffAuthority<'_> {
|
||||
|
|
@ -241,17 +243,30 @@ impl SandboxHandoffAuthority<'_> {
|
|||
self.configuration
|
||||
.workspace()
|
||||
.open_authority()
|
||||
.map_err(changed_configuration_authority)?;
|
||||
.map_err(|error| changed_authority_at("workspace", error))?;
|
||||
for volume in self.configuration.volumes() {
|
||||
volume
|
||||
.open_source()
|
||||
.map_err(changed_configuration_authority)?;
|
||||
.map_err(|error| changed_authority_at("volume", error))?;
|
||||
}
|
||||
let prepared_root = open_absolute_directory(&self.prepared_root)?;
|
||||
if descriptor_identity(&prepared_root).map_err(|error| {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!("prepared root identity: {error}"),
|
||||
)
|
||||
})? != self.prepared_root_identity
|
||||
{
|
||||
return Err(SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
"prepared root changed identity after bundle construction",
|
||||
));
|
||||
}
|
||||
let runtime_root = self
|
||||
.configuration
|
||||
.runtime_domain_root()
|
||||
.open_authority()
|
||||
.map_err(changed_configuration_authority)?;
|
||||
.map_err(|error| changed_authority_at("Runtime Domain", error))?;
|
||||
for authority in &self.derived {
|
||||
let descriptor = open_runtime_source(
|
||||
&runtime_root,
|
||||
|
|
@ -259,7 +274,16 @@ impl SandboxHandoffAuthority<'_> {
|
|||
authority.expected_type,
|
||||
authority.expected_bytes.is_some(),
|
||||
)?;
|
||||
if descriptor_identity(&descriptor)? != authority.identity {
|
||||
if descriptor_identity(&descriptor).map_err(|error| {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!(
|
||||
"runtime handoff source {} identity: {error}",
|
||||
authority.relative_path.display()
|
||||
),
|
||||
)
|
||||
})? != authority.identity
|
||||
{
|
||||
return Err(SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
"runtime handoff source changed identity",
|
||||
|
|
@ -416,6 +440,7 @@ pub struct SandboxBuildRequest<'a> {
|
|||
pub expected_network_namespace: NamespaceIdentity,
|
||||
pub root_process: RootProcess,
|
||||
pub terminal: Option<StagedHostTerminal>,
|
||||
pub prepared_root: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -458,6 +483,8 @@ pub struct SandboxBundle {
|
|||
configuration_fingerprint: ConfigurationFingerprint,
|
||||
configuration_authority: ConfigurationAuthorityIdentity,
|
||||
runtime_sources: Vec<RuntimeHandoffSource>,
|
||||
prepared_root: PathBuf,
|
||||
prepared_root_identity: FileIdentity,
|
||||
}
|
||||
|
||||
impl SandboxBundle {
|
||||
|
|
@ -497,16 +524,29 @@ impl SandboxBundle {
|
|||
configuration
|
||||
.workspace()
|
||||
.open_authority()
|
||||
.map_err(changed_configuration_authority)?;
|
||||
.map_err(|error| changed_authority_at("workspace", error))?;
|
||||
for volume in configuration.volumes() {
|
||||
volume
|
||||
.open_source()
|
||||
.map_err(changed_configuration_authority)?;
|
||||
.map_err(|error| changed_authority_at("volume", error))?;
|
||||
}
|
||||
let runtime_root = configuration
|
||||
.runtime_domain_root()
|
||||
.open_authority()
|
||||
.map_err(changed_configuration_authority)?;
|
||||
.map_err(|error| changed_authority_at("Runtime Domain", error))?;
|
||||
let prepared_root = open_absolute_directory(&self.prepared_root)?;
|
||||
if descriptor_identity(&prepared_root).map_err(|error| {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!("prepared root identity: {error}"),
|
||||
)
|
||||
})? != self.prepared_root_identity
|
||||
{
|
||||
return Err(SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
"prepared root changed identity after bundle construction",
|
||||
));
|
||||
}
|
||||
let derived = self
|
||||
.runtime_sources
|
||||
.iter()
|
||||
|
|
@ -517,7 +557,15 @@ impl SandboxBundle {
|
|||
source.expected_type,
|
||||
source.expected_bytes.is_some(),
|
||||
)?;
|
||||
let identity = descriptor_identity(&descriptor)?;
|
||||
let identity = descriptor_identity(&descriptor).map_err(|error| {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!(
|
||||
"runtime handoff source {} identity: {error}",
|
||||
source.relative_path.display()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
if source
|
||||
.build_identity
|
||||
.is_some_and(|expected| expected != identity)
|
||||
|
|
@ -545,6 +593,8 @@ impl SandboxBundle {
|
|||
configuration_authority: self.configuration_authority.clone(),
|
||||
command: self.command.clone(),
|
||||
derived,
|
||||
prepared_root: self.prepared_root.clone(),
|
||||
prepared_root_identity: self.prepared_root_identity,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -590,6 +640,8 @@ pub fn build_sandbox_bundle(
|
|||
if let Some(terminal) = &request.terminal {
|
||||
terminal.revalidate(request.configuration)?;
|
||||
}
|
||||
let prepared_root = open_absolute_directory(&request.prepared_root)?;
|
||||
let prepared_root_identity = descriptor_identity(&prepared_root)?;
|
||||
|
||||
let paths = runtime_paths(request.configuration, request.container_id)?;
|
||||
let mounts = build_mounts(
|
||||
|
|
@ -604,7 +656,7 @@ pub fn build_sandbox_bundle(
|
|||
)?;
|
||||
let linux = build_linux()?;
|
||||
let root = RootBuilder::default()
|
||||
.path(paths.rootfs.clone())
|
||||
.path(request.prepared_root.clone())
|
||||
.readonly(false)
|
||||
.build()
|
||||
.map_err(oci_error)?;
|
||||
|
|
@ -632,6 +684,8 @@ pub fn build_sandbox_bundle(
|
|||
configuration_fingerprint: request.configuration.configuration_fingerprint(),
|
||||
configuration_authority: ConfigurationAuthorityIdentity::capture(request.configuration),
|
||||
runtime_sources,
|
||||
prepared_root: request.prepared_root,
|
||||
prepared_root_identity,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -639,7 +693,7 @@ struct RuntimePaths {
|
|||
container_name: String,
|
||||
bundle: PathBuf,
|
||||
runsc_root: PathBuf,
|
||||
rootfs: PathBuf,
|
||||
overlay: PathBuf,
|
||||
control_sources: PathBuf,
|
||||
}
|
||||
|
||||
|
|
@ -662,7 +716,7 @@ fn handoff_runtime_sources(
|
|||
),
|
||||
(PathBuf::from("runsc"), HandoffPathType::Directory),
|
||||
(
|
||||
PathBuf::from("state-anchor/merged"),
|
||||
PathBuf::from("runsc-overlay").join(&paths.container_name),
|
||||
HandoffPathType::Directory,
|
||||
),
|
||||
(container.clone(), HandoffPathType::Directory),
|
||||
|
|
@ -699,6 +753,14 @@ fn handoff_runtime_sources(
|
|||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for name in ["resolv.conf", "hosts", "hostname"] {
|
||||
sources.push(RuntimeHandoffSource {
|
||||
relative_path: Path::new("state-anchor/runtime-files").join(name),
|
||||
expected_type: HandoffPathType::RegularFile,
|
||||
build_identity: None,
|
||||
expected_bytes: None,
|
||||
});
|
||||
}
|
||||
if let Some(terminal) = terminal {
|
||||
sources.push(RuntimeHandoffSource {
|
||||
relative_path: terminal.relative_source.clone(),
|
||||
|
|
@ -727,7 +789,15 @@ fn open_runtime_source(
|
|||
Mode::empty(),
|
||||
ResolveFlags::BENEATH | ResolveFlags::NO_MAGICLINKS | ResolveFlags::NO_SYMLINKS,
|
||||
)
|
||||
.map_err(staging_error)?;
|
||||
.map_err(|error| {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!(
|
||||
"runtime handoff source {}: {error}",
|
||||
relative_path.display()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
let stat = rustix::fs::fstat(&descriptor).map_err(staging_error)?;
|
||||
let actual = FileType::from_raw_mode(stat.st_mode);
|
||||
let matches = match expected_type {
|
||||
|
|
@ -793,7 +863,7 @@ fn runtime_paths(
|
|||
Ok(RuntimePaths {
|
||||
bundle: runtime_domain.join("bundles").join(&container_name),
|
||||
runsc_root: runtime_domain.join("runsc"),
|
||||
rootfs: runtime_domain.join("state-anchor").join("merged"),
|
||||
overlay: runtime_domain.join("runsc-overlay").join(&container_name),
|
||||
control_sources: runtime_domain.join("sandbox-control").join(&container_name),
|
||||
container_name,
|
||||
})
|
||||
|
|
@ -811,6 +881,17 @@ fn build_mounts(
|
|||
false,
|
||||
false,
|
||||
)?);
|
||||
let runtime_domain = host_path(configuration.runtime_domain_root().canonical_label())?;
|
||||
for destination in &configuration.fingerprint_record().persistent_dirs {
|
||||
mounts.push(bind_mount(
|
||||
runtime_domain
|
||||
.join("state-anchor/merged")
|
||||
.join(destination.strip_prefix('/').unwrap_or(destination)),
|
||||
PathBuf::from(destination),
|
||||
false,
|
||||
false,
|
||||
)?);
|
||||
}
|
||||
for volume in configuration.volumes() {
|
||||
let record = volume.record();
|
||||
mounts.push(bind_mount(
|
||||
|
|
@ -821,6 +902,14 @@ fn build_mounts(
|
|||
)?);
|
||||
}
|
||||
mounts.extend(control_mounts(control_sources)?);
|
||||
for name in ["resolv.conf", "hosts", "hostname"] {
|
||||
mounts.push(bind_mount(
|
||||
runtime_domain.join("state-anchor/runtime-files").join(name),
|
||||
Path::new("/etc").join(name),
|
||||
true,
|
||||
true,
|
||||
)?);
|
||||
}
|
||||
if let Some(terminal) = terminal {
|
||||
mounts.push(bind_mount(
|
||||
host_path(configuration.runtime_domain_root().canonical_label())?
|
||||
|
|
@ -858,13 +947,13 @@ fn build_linux() -> Result<oci_spec::runtime::Linux, SandboxBuildError> {
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let uid_mapping = LinuxIdMappingBuilder::default()
|
||||
.container_id(0_u32)
|
||||
.host_id(rustix::process::getuid().as_raw())
|
||||
.host_id(0_u32)
|
||||
.size(1_u32)
|
||||
.build()
|
||||
.map_err(oci_error)?;
|
||||
let gid_mapping = LinuxIdMappingBuilder::default()
|
||||
.container_id(0_u32)
|
||||
.host_id(rustix::process::getgid().as_raw())
|
||||
.host_id(0_u32)
|
||||
.size(1_u32)
|
||||
.build()
|
||||
.map_err(oci_error)?;
|
||||
|
|
@ -891,6 +980,13 @@ fn build_runsc_command(paths: &RuntimePaths, bundle_authorization: [u8; 32]) ->
|
|||
OsString::from("-rootless"),
|
||||
OsString::from("-network=host"),
|
||||
OsString::from("-ignore-cgroups"),
|
||||
OsString::from(format!(
|
||||
"-overlay2=root:dir={}",
|
||||
paths
|
||||
.overlay
|
||||
.to_str()
|
||||
.expect("validated runtime path remains UTF-8")
|
||||
)),
|
||||
OsString::from("run"),
|
||||
OsString::from(format!(
|
||||
"-bundle={}",
|
||||
|
|
@ -905,6 +1001,23 @@ fn build_runsc_command(paths: &RuntimePaths, bundle_authorization: [u8; 32]) ->
|
|||
)
|
||||
}
|
||||
|
||||
fn open_absolute_directory(path: &Path) -> Result<OwnedFd, SandboxBuildError> {
|
||||
validate_absolute_normalized(path)?;
|
||||
openat2(
|
||||
rustix::fs::CWD,
|
||||
path,
|
||||
OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOFOLLOW,
|
||||
Mode::empty(),
|
||||
ResolveFlags::NO_MAGICLINKS | ResolveFlags::NO_SYMLINKS,
|
||||
)
|
||||
.map_err(|error| {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!("prepared root {}: {error}", path.display()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn build_process(
|
||||
configuration: &ResolvedDomainConfiguration,
|
||||
root_process: RootProcess,
|
||||
|
|
@ -1094,6 +1207,13 @@ fn changed_configuration_authority(error: impl fmt::Display) -> SandboxBuildErro
|
|||
SandboxBuildError::new(SandboxBuildErrorKind::ChangedAuthority, error.to_string())
|
||||
}
|
||||
|
||||
fn changed_authority_at(label: &str, error: impl fmt::Display) -> SandboxBuildError {
|
||||
SandboxBuildError::new(
|
||||
SandboxBuildErrorKind::ChangedAuthority,
|
||||
format!("{label}: {error}"),
|
||||
)
|
||||
}
|
||||
|
||||
fn oci_error(error: impl fmt::Display) -> SandboxBuildError {
|
||||
SandboxBuildError::new(SandboxBuildErrorKind::OciSpecification, error.to_string())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue