Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions crates/nibrunnerd/tests/guest/isolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

use std::net::{Ipv4Addr, SocketAddr, TcpListener};

use nibrunnerd::adapters::vm::process::VmProcesses;
use nibrunnerd::test_support::egress::EgressEndpoint;
use nibrunnerd::test_support::machine::{RunningHost, Tenant};
use protocol::InstanceState;
use std::os::unix::fs::MetadataExt;

/// The tenant gives up after two seconds, so anything under this came back because a rule said
/// no rather than because nothing was there.
Expand Down Expand Up @@ -149,3 +151,97 @@ async fn an_address_the_configuration_denies_cannot_be_reached() {

closed.stop().await;
}

const JAILER_IDLE_TIMEOUT_MS: u64 = 60_000;

#[tokio::test(flavor = "multi_thread")]
async fn jailed_vmm_processes_have_distinct_non_root_identities_and_resume_their_memory_and_volume() {
let Some(host) = crate::host().await else {
return;
};
let one = host.tenant(1).on_request(JAILER_IDLE_TIMEOUT_MS);
let another = host.tenant(2);
host.deploy(&[one.clone(), another.clone()]).await;
host.until_state(&one.app_id, InstanceState::Running).await;
host.until_state(&another.app_id, InstanceState::Running).await;
let processes = VmProcesses::new(host.host.config.runtime_dir.clone());
let first = processes.read_record(&one.app_id).expect("the first VMM record");
let second = processes
.read_record(&another.app_id)
.expect("the second VMM record");
for record in [&first, &second] {
let root = std::fs::metadata(record.jail_root.as_ref().expect("the recorded jail root"))
.expect("the jail root's owner");
assert_eq!(record.jail_uid, Some(root.uid()));
let status = std::fs::read_to_string(format!("/proc/{}/status", record.pid))
.expect("the VMM's process status");
for (label, identity) in [("Uid:", root.uid()), ("Gid:", root.gid())] {
assert_ne!(identity, 0);
let line = status
.lines()
.find(|line| line.starts_with(label))
.expect("the process identity line");
assert!(
line.split_whitespace()
.skip(1)
.all(|value| value.parse::<u32>() == Ok(identity)),
"{line}"
);
}
}
let root = first.jail_root.as_ref().expect("the recorded jail root");
let process_root = std::path::PathBuf::from(format!("/proc/{}/root", first.pid));
let actual = std::fs::metadata(&process_root).expect("the VMM root");
let recorded = std::fs::metadata(root).expect("the recorded root inode");
assert_eq!((actual.dev(), actual.ino()), (recorded.dev(), recorded.ino()));
assert!(
std::fs::metadata(process_root.join("assets/kernel"))
.unwrap()
.len()
> 0
);
assert_ne!(first.jail_root, second.jail_root);
let other_root = second.jail_root.as_ref().expect("the second jail");
assert_ne!(
std::fs::metadata(root).unwrap().uid(),
std::fs::metadata(other_root).unwrap().uid()
);
assert_ne!(
std::fs::metadata(root).unwrap().gid(),
std::fs::metadata(other_root).unwrap().gid()
);
assert!(!process_root.join("etc/nibrunner/config.toml").exists());
assert_eq!(
host.get(&one, "/remember")
.await
.expect("memory before sleep")
.body,
"1"
);
assert_eq!(
host.get(&one, "/write?path=kept&body=jailed")
.await
.expect("a volume write")
.status,
200
);
host.let_sleep(&one).await;
assert_eq!(
host.get(&one, "/remember").await.expect("memory after wake").body,
"2"
);
assert_eq!(
host.get(&one, "/read?path=kept")
.await
.expect("the volume after wake")
.body,
"jailed"
);
host.host
.vms
.readopt(&one.app_id)
.await
.expect("reattach jailed host channels");
assert_eq!(host.get(&one, "/").await.expect("the readopted app").status, 200);
host.stop().await;
}
Loading