Confine runtime staging to retained authority

This commit is contained in:
ms2k 2026-07-16 18:53:20 +09:00
commit fc5b2b6ca0

View file

@ -23,108 +23,198 @@ use rustix::process::getuid;
#[test]
fn validated_inputs_stage_one_complete_plan_and_drop_exact_runtime_artifacts() {
let tree = tempfile::tempdir().unwrap();
let configuration = configuration(tree.path());
let persistent_domain = host_path(
configuration
.persistent_domain_root()
.canonical_label()
.as_bytes(),
);
let runtime_domain = host_path(
configuration
.runtime_domain_root()
.canonical_label()
.as_bytes(),
);
let catalog = Catalog::new(
tree.path().join("catalog.redb"),
tree.path().join("catalog.lock"),
);
catalog.initialize().unwrap();
let domain = Domain {
domain_id: configuration.domain_id(),
profile_name: configuration.selected_profile().to_owned(),
canonical_workspace: configuration.workspace().canonical_label().clone(),
};
let lock = OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(persistent_domain.join("domain.lock"))
.unwrap();
let store = Arc::new(
HostCoordinatorDomainStore::open(
try_exclusive_flock(lock).unwrap(),
&persistent_domain.join("domain.redb"),
&domain,
)
.unwrap(),
);
let lower = tree.path().join("prepared-lower");
fs::create_dir(&lower).unwrap();
let metadata = lower.metadata().unwrap();
let artifact = PreparedArtifact {
prepared_artifact_id: PreparedArtifactId::new([8; 32]),
source_identity: SourceIdentity::new(b"synthetic-spike".to_vec()),
selected_manifest_digest: SelectedManifestDigest::new([9; 32]),
policy_id: PolicyId::new([10; 32]),
id_map_digest: IdMapDigest::new([11; 32]),
path: HostPath::new(lower.as_os_str().as_bytes().to_vec()),
device: DeviceId::new(metadata.dev()),
inode: InodeId::new(metadata.ino()),
state: ArtifactState::Available,
};
let generation = Generation {
domain_id: configuration.domain_id(),
generation_id: GenerationId::new(1),
configuration_fingerprint: configuration.configuration_fingerprint(),
coordinator_id: CoordinatorId::new([2; 32]),
network_sidecar_id: lazaret_core::identity::NetworkSidecarId::new([3; 32]),
sandbox_id: SandboxId::new([4; 32]),
durable_decision: DurableDecision::None,
};
let container_id = ContainerId::new([5; 32]);
let escalation_client = File::open("/bin/true").unwrap();
let invalid_escalation_client = File::open("/dev/null").unwrap();
let inputs = |escalation_client| ProductionColdStartInputs {
store: Arc::clone(&store),
catalog: &catalog,
configuration: &configuration,
generation: generation.clone(),
prepared_artifact: &artifact,
root_process: RootProcess::new("/bin/sh", ["-c".into(), "sleep infinity".into()]).unwrap(),
resolver_addresses: vec![IpAddress::V4([10, 0, 2, 3])],
network_probe: NetworkProbeRequest {
domain_id: configuration.domain_id(),
generation_id: GenerationId::new(1),
enforcement_namespace: namespace_identity(Path::new("/proc/self/ns/net")).unwrap(),
denied: Vec::new(),
permitted: Vec::new(),
dns: Vec::new(),
},
reachability: lazaret_core::network::ReachabilityExpectation {
denied_destinations: Vec::new(),
permitted_destinations: Vec::new(),
dns_queries: Vec::new(),
},
container_id,
invoking_uid: getuid().as_raw(),
escalation_client,
};
let staged = ProductionColdStart::stage(inputs(&escalation_client)).unwrap();
let name = hex(container_id.as_bytes());
let bundle = runtime_domain.join("bundles").join(&name);
let control = runtime_domain.join("sandbox-control").join(&name);
let fixture = ProductionFixture::new();
let staged = ProductionColdStart::stage(fixture.inputs(&fixture.escalation_client)).unwrap();
let bundle = fixture.bundle();
let control = fixture.control();
assert_private_artifacts(&bundle, &control);
drop(staged);
assert!(!bundle.exists());
assert!(!control.exists());
assert!(ProductionColdStart::stage(inputs(&invalid_escalation_client)).is_err());
assert!(
ProductionColdStart::stage(fixture.inputs(&fixture.invalid_escalation_client)).is_err()
);
assert!(!bundle.exists());
assert!(!control.exists());
}
#[test]
fn cleanup_leaves_a_replacement_directory_untouched() {
let fixture = ProductionFixture::new();
let staged = ProductionColdStart::stage(fixture.inputs(&fixture.escalation_client)).unwrap();
let bundle = fixture.bundle();
let moved = bundle.with_extension("moved");
fs::rename(&bundle, &moved).unwrap();
fs::create_dir(&bundle).unwrap();
fs::write(bundle.join("replacement"), b"trusted replacement").unwrap();
drop(staged);
assert_eq!(
fs::read(bundle.join("replacement")).unwrap(),
b"trusted replacement"
);
assert!(moved.is_dir());
}
#[test]
fn fixed_runtime_parents_refuse_symlinks_and_wrong_modes() {
let fixture = ProductionFixture::new();
let bundles = fixture.runtime_domain.join("bundles");
fs::create_dir(&bundles).unwrap();
fs::set_permissions(&bundles, fs::Permissions::from_mode(0o755)).unwrap();
assert!(ProductionColdStart::stage(fixture.inputs(&fixture.escalation_client)).is_err());
fs::remove_dir(&bundles).unwrap();
let controls = fixture.runtime_domain.join("sandbox-control");
let redirect = fixture.tree.path().join("redirect");
fs::create_dir(&redirect).unwrap();
std::os::unix::fs::symlink(&redirect, &controls).unwrap();
assert!(ProductionColdStart::stage(fixture.inputs(&fixture.escalation_client)).is_err());
assert!(redirect.read_dir().unwrap().next().is_none());
}
#[test]
fn replaced_runtime_domain_authority_is_not_used_for_staging() {
let fixture = ProductionFixture::new();
let moved = fixture.runtime_domain.with_extension("moved");
fs::rename(&fixture.runtime_domain, &moved).unwrap();
fs::create_dir(&fixture.runtime_domain).unwrap();
fs::set_permissions(&fixture.runtime_domain, fs::Permissions::from_mode(0o700)).unwrap();
assert!(ProductionColdStart::stage(fixture.inputs(&fixture.escalation_client)).is_err());
assert!(fixture.runtime_domain.read_dir().unwrap().next().is_none());
}
struct ProductionFixture {
tree: tempfile::TempDir,
configuration: lazaret_core::configuration::ResolvedDomainConfiguration,
runtime_domain: PathBuf,
catalog: Catalog,
store: Arc<HostCoordinatorDomainStore>,
artifact: PreparedArtifact,
generation: Generation,
container_id: ContainerId,
escalation_client: File,
invalid_escalation_client: File,
}
impl ProductionFixture {
fn new() -> Self {
let tree = tempfile::tempdir().unwrap();
let configuration = configuration(tree.path());
let persistent_domain = host_path(
configuration
.persistent_domain_root()
.canonical_label()
.as_bytes(),
);
let runtime_domain = host_path(
configuration
.runtime_domain_root()
.canonical_label()
.as_bytes(),
);
let catalog = Catalog::new(
tree.path().join("catalog.redb"),
tree.path().join("catalog.lock"),
);
catalog.initialize().unwrap();
let domain = Domain {
domain_id: configuration.domain_id(),
profile_name: configuration.selected_profile().to_owned(),
canonical_workspace: configuration.workspace().canonical_label().clone(),
};
let lock = OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(persistent_domain.join("domain.lock"))
.unwrap();
let store = Arc::new(
HostCoordinatorDomainStore::open(
try_exclusive_flock(lock).unwrap(),
&persistent_domain.join("domain.redb"),
&domain,
)
.unwrap(),
);
let lower = tree.path().join("prepared-lower");
fs::create_dir(&lower).unwrap();
let metadata = lower.metadata().unwrap();
let artifact = PreparedArtifact {
prepared_artifact_id: PreparedArtifactId::new([8; 32]),
source_identity: SourceIdentity::new(b"synthetic-spike".to_vec()),
selected_manifest_digest: SelectedManifestDigest::new([9; 32]),
policy_id: PolicyId::new([10; 32]),
id_map_digest: IdMapDigest::new([11; 32]),
path: HostPath::new(lower.as_os_str().as_bytes().to_vec()),
device: DeviceId::new(metadata.dev()),
inode: InodeId::new(metadata.ino()),
state: ArtifactState::Available,
};
let generation = Generation {
domain_id: configuration.domain_id(),
generation_id: GenerationId::new(1),
configuration_fingerprint: configuration.configuration_fingerprint(),
coordinator_id: CoordinatorId::new([2; 32]),
network_sidecar_id: lazaret_core::identity::NetworkSidecarId::new([3; 32]),
sandbox_id: SandboxId::new([4; 32]),
durable_decision: DurableDecision::None,
};
Self {
tree,
configuration,
runtime_domain,
catalog,
store,
artifact,
generation,
container_id: ContainerId::new([5; 32]),
escalation_client: File::open("/bin/true").unwrap(),
invalid_escalation_client: File::open("/dev/null").unwrap(),
}
}
fn inputs<'a>(&'a self, escalation_client: &'a File) -> ProductionColdStartInputs<'a> {
ProductionColdStartInputs {
store: Arc::clone(&self.store),
catalog: &self.catalog,
configuration: &self.configuration,
generation: self.generation.clone(),
prepared_artifact: &self.artifact,
root_process: RootProcess::new("/bin/sh", ["-c".into(), "sleep infinity".into()])
.unwrap(),
resolver_addresses: vec![IpAddress::V4([10, 0, 2, 3])],
network_probe: NetworkProbeRequest {
domain_id: self.configuration.domain_id(),
generation_id: GenerationId::new(1),
enforcement_namespace: namespace_identity(Path::new("/proc/self/ns/net")).unwrap(),
denied: Vec::new(),
permitted: Vec::new(),
dns: Vec::new(),
},
reachability: lazaret_core::network::ReachabilityExpectation {
denied_destinations: Vec::new(),
permitted_destinations: Vec::new(),
dns_queries: Vec::new(),
},
container_id: self.container_id,
invoking_uid: getuid().as_raw(),
escalation_client,
}
}
fn bundle(&self) -> PathBuf {
self.runtime_domain
.join("bundles")
.join(hex(self.container_id.as_bytes()))
}
fn control(&self) -> PathBuf {
self.runtime_domain
.join("sandbox-control")
.join(hex(self.container_id.as_bytes()))
}
}
fn assert_private_artifacts(bundle: &Path, control: &Path) {
let config = bundle.join("config.json");
let escalation_client = control.join("bin/lazaret-escalate");