Fix rootless launches with subordinate ID maps

Resolve and verify canonical caller subordinate ranges, launch preparation and runtime workers through mapped causal acquisition, and keep runsc in the existing user namespace. Repair descriptor-relative OCI cleanup and absent-mount recovery so failed launches can progress to the corrected path.
This commit is contained in:
ms2k 2026-07-19 19:51:47 +09:00
commit 73e3e9fdee
2 changed files with 1424 additions and 172 deletions

View file

@ -20,8 +20,10 @@ use lazaret_core::records::{
};
use lazaret_linux::filesystem::{atomic_replace, require_write_sealed, sealed_memfd};
use lazaret_linux::process::{
ProcessOwnershipContext, TargetCommand, child_broker, current_process_identity, recover_process,
AcknowledgedChildAcquisition, ChildStandardIo, ProcessOwnershipContext, TargetCommand,
child_broker, current_process_identity, recover_process,
};
use lazaret_linux::user_namespace::UserNamespaceMap;
use lazaret_runtime_supervisor::network::{
DnsProbeTarget, NetworkAttestationContext, NetworkProbeRequest, TcpProbeTarget,
enter_generation_filesystem_namespaces, prepare_network, probe_current_network,
@ -77,15 +79,20 @@ fn run_parent() -> std::io::Result<()> {
.argument(policy_file.as_raw_fd().to_string())
.argument(hex(policy.digest().as_bytes()));
let parent = current_process_identity()?;
let runtime = child_broker().acquire(
parent,
ProcessOwnershipContext {
domain_id: DomainId::new([1; 32]),
generation_id: GenerationId::new(7),
coordinator_id: CoordinatorId::new([2; 32]),
slot: HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
let map = UserNamespaceMap::resolve_current()?;
let runtime = child_broker().acquire_with_stdio_in_user_namespace(
AcknowledgedChildAcquisition {
expected_parent: parent,
ownership: ProcessOwnershipContext {
domain_id: DomainId::new([1; 32]),
generation_id: GenerationId::new(7),
coordinator_id: CoordinatorId::new([2; 32]),
slot: HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
},
target: &target,
standard_io: ChildStandardIo::inherit(),
},
&target,
&map,
|_| Ok(()),
|staged| Ok(ProcessOwnershipCommitAck::v1(staged)),
)?;

View file

@ -295,24 +295,14 @@ fn deterministic_snapshot_has_only_private_namespaces_and_fixed_runtime_shape()
.iter()
.map(|namespace| namespace["type"].as_str().unwrap())
.collect::<Vec<_>>(),
["pid", "ipc", "uts", "mount", "user", "network"]
["pid", "ipc", "uts", "mount", "network"]
);
for namespace in &namespaces[..5] {
for namespace in &namespaces[..4] {
assert!(namespace.get("path").is_none());
}
assert_eq!(namespaces[5]["path"], NETWORK_NAMESPACE_PATH);
assert_eq!(
specification["linux"]["uidMappings"],
serde_json::json!([{
"hostID": 0, "containerID": 0, "size": 1
}])
);
assert_eq!(
specification["linux"]["gidMappings"],
serde_json::json!([{
"hostID": 0, "containerID": 0, "size": 1
}])
);
assert_eq!(namespaces[4]["path"], NETWORK_NAMESPACE_PATH);
assert!(specification["linux"].get("uidMappings").is_none());
assert!(specification["linux"].get("gidMappings").is_none());
assert_runtime_command(&first, &fixture);
assert_eq!(
@ -360,7 +350,6 @@ fn assert_runtime_command(bundle: &SandboxBundle, fixture: &Fixture) {
.collect::<Vec<_>>(),
[
format!("-root={runtime_domain}/runsc"),
"-rootless".to_owned(),
"-network=host".to_owned(),
"-ignore-cgroups".to_owned(),
format!(
@ -411,12 +400,8 @@ fn product_init_role_has_exact_terminal_neutral_rootless_bundle_shape() {
);
assert_eq!(specification["process"]["user"]["uid"], 0);
assert_eq!(specification["process"]["user"]["gid"], 0);
assert_eq!(
specification["linux"]["uidMappings"],
serde_json::json!([{
"hostID": 0, "containerID": 0, "size": 1
}])
);
assert!(specification["linux"].get("uidMappings").is_none());
assert!(specification["linux"].get("gidMappings").is_none());
assert_eq!(
mounts(&specification)[SANDBOX_INIT_DESTINATION]["source"],
host_path(