Bind production cold start to installed Lazaret authorities

This commit is contained in:
ms2k 2026-07-16 20:41:40 +09:00
commit bc04112c77
9 changed files with 573 additions and 25 deletions

View file

@ -19,6 +19,7 @@ ed25519-dalek.workspace = true
getrandom.workspace = true
lazaret-core = { version = "=0.0.0", path = "../lazaret-core" }
lazaret-linux = { version = "=0.0.0", path = "../lazaret-linux" }
lazaret-prepared-lower = { version = "=0.0.0", path = "../lazaret-prepared-lower" }
lazaret-runtime-supervisor = { version = "=0.0.0", path = "../lazaret-runtime-supervisor" }
lazaret-state-anchor = { version = "=0.0.0", path = "../lazaret-state-anchor" }
lazaret-storage-catalog = { version = "=0.0.0", path = "../lazaret-storage-catalog" }

View file

@ -7,5 +7,7 @@ pub mod escalation;
pub mod lifecycle;
pub mod network;
pub mod production;
pub mod production_selector;
pub mod runtime;
pub mod sandbox_init;
pub mod state_anchor;

View file

@ -1,5 +1,8 @@
fn main() {
let arguments = std::env::args_os().skip(1).collect::<Vec<_>>();
if lazaret_host_coordinator::sandbox_init::dispatch_private_sandbox_init(&arguments) {
return;
}
if let Some(result) = lazaret_linux::process::dispatch_private_process_role(&arguments) {
if let Err(error) = result {
eprintln!("private process role failed: {error}");

View file

@ -66,6 +66,7 @@ pub struct ProductionColdStartInputs<'a> {
pub container_id: ContainerId,
pub invoking_uid: u32,
pub escalation_client: &'a File,
pub sandbox_init: &'a File,
}
pub struct BootstrapOwnerGuard {
@ -138,6 +139,7 @@ impl<'a> ProductionColdStart<'a> {
inputs.configuration.runtime_domain_root(),
inputs.container_id,
inputs.escalation_client,
inputs.sandbox_init,
inputs.invoking_uid,
)
.map_err(|error| failure(format!("runtime staging: {error}")))?;
@ -293,6 +295,7 @@ impl RuntimeStaging {
runtime_domain: &ConfinedPathPlan,
container_id: ContainerId,
escalation_client: &File,
sandbox_init: &File,
invoking_uid: u32,
) -> Result<Self, ProductionColdStartError> {
let root = runtime_domain.open_authority().map_err(failure)?;
@ -325,11 +328,15 @@ impl RuntimeStaging {
bundle_identity,
control_identity,
};
staging.populate_control(escalation_client)?;
staging.populate_control(escalation_client, sandbox_init)?;
Ok(staging)
}
fn populate_control(&self, escalation_client: &File) -> Result<(), ProductionColdStartError> {
fn populate_control(
&self,
escalation_client: &File,
sandbox_init: &File,
) -> Result<(), ProductionColdStartError> {
let invoking_uid = fstat(&self.control).map_err(failure)?.st_uid;
drop(open_or_create_private_directory(
&self.control,
@ -352,15 +359,10 @@ impl RuntimeStaging {
true,
)?);
let bin = open_or_create_private_directory(&self.control, "bin", invoking_uid, true)?;
let source = escalation_client
.metadata()
.map_err(|error| failure(format!("inspecting escalation client: {error}")))?;
if !source.file_type().is_file() || source.permissions().mode() & 0o111 == 0 {
return Err(ProductionColdStartError(
"escalation client is not an executable regular file".into(),
));
}
require_executable_regular_file(escalation_client, "escalation client")?;
copy_descriptor(escalation_client, &bin, "lazaret-escalate", invoking_uid)?;
require_executable_regular_file(sandbox_init, "sandbox init")?;
copy_descriptor(sandbox_init, &bin, "lazaret-sandbox-init", invoking_uid)?;
Ok(())
}
@ -414,6 +416,21 @@ fn copy_descriptor(
require_private_file(&destination, invoking_uid, 0o700)
}
fn require_executable_regular_file(
source: &File,
label: &str,
) -> Result<(), ProductionColdStartError> {
let metadata = source
.metadata()
.map_err(|error| failure(format!("inspecting {label}: {error}")))?;
if !metadata.file_type().is_file() || metadata.permissions().mode() & 0o111 == 0 {
return Err(ProductionColdStartError(format!(
"{label} is not an executable regular file"
)));
}
Ok(())
}
#[derive(Clone, Copy)]
struct FileIdentity {
device: u64,

View file

@ -0,0 +1,405 @@
use std::collections::BTreeSet;
use std::fmt;
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::sync::Arc;
use lazaret_core::configuration::ResolvedDomainConfiguration;
use lazaret_core::identity::{
ConfigurationFingerprint, ContainerId, CoordinatorId, DomainId, GenerationId, NetworkSidecarId,
PrepareOperationId, SandboxId,
};
use lazaret_core::network::{DnsQueryExpectation, ReachabilityExpectation};
use lazaret_core::records::{DurableDecision, Generation, IpAddress, PreparedArtifact};
use lazaret_linux::filesystem::namespace_identity;
use lazaret_prepared_lower::{
IdMap, IdMapRange, OciRegistryTransport, Platform, PreparedLowerStore, RegistryCredentials,
};
use lazaret_runtime_supervisor::network::{DnsProbeTarget, NetworkProbeRequest, TcpProbeTarget};
use lazaret_runtime_supervisor::sandbox::{RootProcess, SANDBOX_INIT_DESTINATION};
use lazaret_storage_catalog::{Catalog, CatalogError};
use rustix::fs::{Mode, OFlags, ResolveFlags, openat2};
use crate::domain_store::HostCoordinatorDomainStore;
use crate::production::ProductionColdStartInputs;
use crate::sandbox_init::PRIVATE_SANDBOX_INIT_ROLE;
const NETWORK_NAMESPACE_PATH: &str = "/proc/self/ns/net";
#[derive(Debug)]
pub struct ProductionSelectionError(String);
impl fmt::Display for ProductionSelectionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl std::error::Error for ProductionSelectionError {}
fn failure(error: impl fmt::Display) -> ProductionSelectionError {
ProductionSelectionError(error.to_string())
}
pub struct InstalledProductPaths<'a> {
pub escalation_client: &'a Path,
pub sandbox_init: &'a Path,
}
pub struct ProductionSelection {
domain_id: DomainId,
configuration_fingerprint: ConfigurationFingerprint,
catalog: Catalog,
artifact: PreparedArtifact,
generation: Generation,
resolver_addresses: Vec<IpAddress>,
network_probe: NetworkProbeRequest,
reachability: ReachabilityExpectation,
container_id: ContainerId,
escalation_client: File,
sandbox_init: File,
}
impl ProductionSelection {
/// Borrows the selected resources as the existing production cold-start input.
///
/// # Errors
/// Refuses a configuration other than the exact one used for selection.
pub fn cold_start_inputs<'a>(
&'a self,
store: Arc<HostCoordinatorDomainStore>,
configuration: &'a ResolvedDomainConfiguration,
) -> Result<ProductionColdStartInputs<'a>, ProductionSelectionError> {
if configuration.domain_id() != self.domain_id
|| configuration.configuration_fingerprint() != self.configuration_fingerprint
{
return Err(failure("selected cold start does not match configuration"));
}
Ok(ProductionColdStartInputs {
store,
catalog: &self.catalog,
configuration,
generation: self.generation.clone(),
prepared_artifact: &self.artifact,
root_process: RootProcess::new(
SANDBOX_INIT_DESTINATION,
[PRIVATE_SANDBOX_INIT_ROLE.to_owned()],
)
.map_err(failure)?,
resolver_addresses: self.resolver_addresses.clone(),
network_probe: self.network_probe.clone(),
reachability: self.reachability.clone(),
container_id: self.container_id,
invoking_uid: rustix::process::getuid().as_raw(),
escalation_client: &self.escalation_client,
sandbox_init: &self.sandbox_init,
})
}
}
/// Selects and prepares every Host Coordinator-owned cold-start input.
///
/// # Errors
/// Refuses invalid installed paths, unsupported architectures, registry or image failures,
/// changed Storage Root authority, exhausted identities, and configuration-policy mismatch.
pub async fn select_production_cold_start(
configuration: &ResolvedDomainConfiguration,
store: &HostCoordinatorDomainStore,
product_paths: InstalledProductPaths<'_>,
) -> Result<ProductionSelection, ProductionSelectionError> {
let escalation_client = open_installed_executable(product_paths.escalation_client)?;
let sandbox_init = open_installed_executable(product_paths.sandbox_init)?;
let catalog = Catalog::under_storage_root(configuration.storage_root()).map_err(failure)?;
match catalog.initialize() {
Ok(()) | Err(CatalogError::AlreadyInitialized) => {}
Err(error) => return Err(failure(error)),
}
let prepared =
PreparedLowerStore::under_storage_root(configuration.storage_root(), catalog.clone())
.map_err(failure)?;
let image = OciRegistryTransport
.pull_confined(
&configuration.fingerprint_record().image,
&RegistryCredentials::Anonymous,
&host_platform()?,
configuration.storage_root(),
random_id()?,
)
.await
.map_err(|error| failure(format!("pulling configured anonymous OCI image: {error}")))?;
let artifact = prepared
.prepare_transported(
PrepareOperationId::new(random_id()?),
&image,
rootless_id_map(),
)
.map_err(failure)?;
image.cleanup().map_err(failure)?;
let next_generation = store
.snapshot()
.map_err(failure)?
.generations
.iter()
.map(|generation| generation.generation_id.get())
.max()
.unwrap_or(0)
.checked_add(1)
.ok_or_else(|| failure("generation identity exhausted"))?;
let (coordinator_id, network_sidecar_id, sandbox_id) = distinct_generation_ids()?;
let generation = Generation {
domain_id: configuration.domain_id(),
generation_id: GenerationId::new(next_generation),
configuration_fingerprint: configuration.configuration_fingerprint(),
coordinator_id,
network_sidecar_id,
sandbox_id,
durable_decision: DurableDecision::None,
};
let namespace = namespace_identity(Path::new(NETWORK_NAMESPACE_PATH)).map_err(failure)?;
let (resolver_addresses, network_probe, reachability) =
select_network_readiness(configuration, &generation, namespace);
Ok(ProductionSelection {
domain_id: configuration.domain_id(),
configuration_fingerprint: configuration.configuration_fingerprint(),
catalog,
artifact,
generation,
resolver_addresses,
network_probe,
reachability,
container_id: ContainerId::new(random_id()?),
escalation_client,
sandbox_init,
})
}
fn select_network_readiness(
configuration: &ResolvedDomainConfiguration,
generation: &Generation,
enforcement_namespace: lazaret_core::identity::NamespaceIdentity,
) -> (Vec<IpAddress>, NetworkProbeRequest, ReachabilityExpectation) {
let readiness = configuration.network_readiness();
let denied = readiness
.denied
.iter()
.map(|target| TcpProbeTarget {
address: IpAddress::V4(target.address),
port: target.port,
})
.collect::<Vec<_>>();
let permitted = readiness
.permitted
.iter()
.map(|target| TcpProbeTarget {
address: IpAddress::V4(target.address),
port: target.port,
})
.collect::<Vec<_>>();
let dns = readiness
.dns
.iter()
.map(|target| DnsProbeTarget {
resolver: IpAddress::V4(target.resolver),
query_name: target.query.clone(),
})
.collect::<Vec<_>>();
let reachability = ReachabilityExpectation {
denied_destinations: denied.iter().map(|target| target.address).collect(),
permitted_destinations: permitted.iter().map(|target| target.address).collect(),
dns_queries: dns
.iter()
.map(|target| DnsQueryExpectation {
resolver: target.resolver,
query_name: target.query_name.clone(),
})
.collect(),
};
let resolvers = dns
.iter()
.map(|target| target.resolver)
.collect::<BTreeSet<_>>()
.into_iter()
.collect();
(
resolvers,
NetworkProbeRequest {
domain_id: generation.domain_id,
generation_id: generation.generation_id,
enforcement_namespace,
denied,
permitted,
dns,
},
reachability,
)
}
fn open_installed_executable(path: &Path) -> Result<File, ProductionSelectionError> {
let descriptor = openat2(
rustix::fs::CWD,
path,
OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::NO_MAGICLINKS | ResolveFlags::NO_SYMLINKS,
)
.map_err(failure)?;
let file = File::from(descriptor);
let metadata = file.metadata().map_err(failure)?;
if !metadata.is_file() || metadata.permissions().mode() & 0o111 == 0 {
return Err(failure(
"installed product path is not an executable regular file",
));
}
Ok(file)
}
fn rootless_id_map() -> IdMap {
IdMap {
uid: vec![IdMapRange {
container_id: 0,
namespace_id: rustix::process::getuid().as_raw(),
size: 1,
}],
gid: vec![IdMapRange {
container_id: 0,
namespace_id: rustix::process::getgid().as_raw(),
size: 1,
}],
}
}
fn host_platform() -> Result<Platform, ProductionSelectionError> {
match std::env::consts::ARCH {
"x86_64" => Ok(Platform::linux("amd64")),
"aarch64" => Ok(Platform::linux("arm64")),
architecture => Err(failure(format!(
"host architecture {architecture} has no selected OCI platform mapping"
))),
}
}
fn distinct_generation_ids()
-> Result<(CoordinatorId, NetworkSidecarId, SandboxId), ProductionSelectionError> {
for _ in 0..8 {
let coordinator = CoordinatorId::new(random_id()?);
let network = NetworkSidecarId::new(random_id()?);
let sandbox = SandboxId::new(random_id()?);
if coordinator.as_bytes() != network.as_bytes()
&& coordinator.as_bytes() != sandbox.as_bytes()
&& network.as_bytes() != sandbox.as_bytes()
{
return Ok((coordinator, network, sandbox));
}
}
Err(failure(
"fresh generation role identities repeatedly collided",
))
}
fn random_id() -> Result<[u8; 32], ProductionSelectionError> {
let mut bytes = [0; 32];
getrandom::fill(&mut bytes).map_err(failure)?;
Ok(bytes)
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use std::fs;
use lazaret_core::configuration::{ConfigurationLoadRequest, load_trusted_configuration};
use lazaret_core::identity::{DeviceId, InodeId, NamespaceIdentity};
use lazaret_runtime_supervisor::sandbox::production_sandbox_layout;
use super::*;
#[test]
fn resolved_readiness_maps_exactly_and_rootless_id_map_matches_builder_policy() {
let tree = tempfile::tempdir().unwrap();
let configuration = configuration(tree.path());
let generation = Generation {
domain_id: configuration.domain_id(),
generation_id: GenerationId::new(9),
configuration_fingerprint: configuration.configuration_fingerprint(),
coordinator_id: CoordinatorId::new([1; 32]),
network_sidecar_id: NetworkSidecarId::new([2; 32]),
sandbox_id: SandboxId::new([3; 32]),
durable_decision: DurableDecision::None,
};
let namespace = NamespaceIdentity {
device: DeviceId::new(4),
inode: InodeId::new(5),
};
let (resolvers, request, expectation) =
select_network_readiness(&configuration, &generation, namespace);
assert_eq!(resolvers, [IpAddress::V4([198, 51, 100, 53])]);
assert_eq!(request.denied[0].port, 8443);
assert_eq!(request.permitted[0].port, 443);
assert_eq!(request.dns[0].query_name, "example.invalid");
assert_eq!(expectation.denied_destinations, [request.denied[0].address]);
assert_eq!(
expectation.permitted_destinations,
[request.permitted[0].address]
);
assert_eq!(expectation.dns_queries[0].resolver, request.dns[0].resolver);
let id_map = rootless_id_map();
assert_eq!(id_map.uid[0].container_id, 0);
assert_eq!(
id_map.uid[0].namespace_id,
rustix::process::getuid().as_raw()
);
assert_eq!(
id_map.gid[0].namespace_id,
rustix::process::getgid().as_raw()
);
}
#[test]
fn installed_product_selection_requires_exact_executable_and_fresh_roles() {
let tree = tempfile::tempdir().unwrap();
let executable = tree.path().join("product");
fs::copy("/bin/true", &executable).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
assert!(open_installed_executable(&executable).is_ok());
fs::set_permissions(&executable, fs::Permissions::from_mode(0o600)).unwrap();
assert!(open_installed_executable(&executable).is_err());
let (coordinator, network, sandbox) = distinct_generation_ids().unwrap();
assert_ne!(coordinator.as_bytes(), network.as_bytes());
assert_ne!(coordinator.as_bytes(), sandbox.as_bytes());
assert_ne!(network.as_bytes(), sandbox.as_bytes());
assert!(host_platform().is_ok());
}
fn configuration(root: &Path) -> ResolvedDomainConfiguration {
let configuration_directory = root.join("configuration");
let workspace = root.join("workspace");
let runtime = root.join("runtime");
fs::create_dir(&configuration_directory).unwrap();
fs::create_dir(&workspace).unwrap();
fs::create_dir(&runtime).unwrap();
let configuration_path = configuration_directory.join("lazaret.toml");
fs::write(
&configuration_path,
format!(
"schema = \"lazaret-trusted-host-config-v1\"\n[storage]\ndata_root = \"{}\"\n[network]\ndefault_allow = [\"192.0.2.10/32\"]\n[profiles.default]\nimage = \"example.invalid/factory:v1\"\n[profiles.default.readiness]\ndenied = [{{ address = \"10.20.30.40\", port = 8443 }}]\npermitted = [{{ address = \"192.0.2.10\", port = 443 }}]\ndns = [{{ resolver = \"198.51.100.53\", query = \"Example.Invalid.\" }}]\n",
root.join("data").display()
),
)
.unwrap();
load_trusted_configuration(ConfigurationLoadRequest {
explicit_configuration_path: Some(configuration_path),
selected_profile: "default".into(),
workspace,
home: root.join("home"),
xdg_config_home: None,
xdg_data_home: None,
xdg_runtime_dir: runtime,
environment: BTreeMap::new(),
sandbox_layout: production_sandbox_layout().unwrap(),
})
.unwrap()
}
}

View file

@ -0,0 +1,38 @@
use std::ffi::{OsStr, OsString};
pub const PRIVATE_SANDBOX_INIT_ROLE: &str = "--lazaret-private-sandbox-init-v1";
#[must_use]
pub fn is_private_sandbox_init_role(arguments: &[OsString]) -> bool {
arguments.len() == 1 && arguments[0] == OsStr::new(PRIVATE_SANDBOX_INIT_ROLE)
}
#[must_use]
pub fn dispatch_private_sandbox_init(arguments: &[OsString]) -> bool {
if !is_private_sandbox_init_role(arguments) {
return false;
}
loop {
std::thread::park();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn private_role_dispatch_is_exact_and_closed() {
assert!(is_private_sandbox_init_role(&[OsString::from(
PRIVATE_SANDBOX_INIT_ROLE
)]));
assert!(!is_private_sandbox_init_role(&[]));
assert!(!is_private_sandbox_init_role(&[
OsString::from(PRIVATE_SANDBOX_INIT_ROLE),
OsString::from("extra"),
]));
assert!(!is_private_sandbox_init_role(&[OsString::from(
"--private-sandbox-init"
)]));
}
}

View file

@ -577,7 +577,7 @@ fn configuration(tree: &TestTree) -> lazaret_core::configuration::ResolvedDomain
fs::write(
&configuration_path,
format!(
"schema = \"lazaret-trusted-host-config-v1\"\n[storage]\ndata_root = \"{}\"\n[profiles.default]\nimage = \"example.invalid/fixture:v1\"\npersistent_dirs = [\"/usr\"]\n",
"schema = \"lazaret-trusted-host-config-v1\"\n[storage]\ndata_root = \"{}\"\n[network]\ndefault_allow = [\"192.0.2.10/32\"]\n[profiles.default]\nimage = \"example.invalid/fixture:v1\"\npersistent_dirs = [\"/usr\"]\n[profiles.default.readiness]\ndenied = [{{ address = \"10.20.30.40\", port = 443 }}]\npermitted = [{{ address = \"192.0.2.10\", port = 443 }}]\ndns = [{{ resolver = \"198.51.100.53\", query = \"example.invalid\" }}]\n",
tree.path.join("data").display()
),
)

View file

@ -199,6 +199,7 @@ impl ProductionFixture {
container_id: self.container_id,
invoking_uid: getuid().as_raw(),
escalation_client,
sandbox_init: escalation_client,
}
}
@ -242,7 +243,7 @@ fn configuration(root: &Path) -> lazaret_core::configuration::ResolvedDomainConf
fs::write(
&configuration_path,
format!(
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[profiles.default]\nimage = \"example.invalid/factory:v1\"\npersistent_dirs = []\n",
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[network]\ndefault_allow = [\"192.0.2.10/32\"]\n\n[profiles.default]\nimage = \"example.invalid/factory:v1\"\npersistent_dirs = []\n\n[profiles.default.readiness]\ndenied = [{{ address = \"10.20.30.40\", port = 443 }}]\npermitted = [{{ address = \"192.0.2.10\", port = 443 }}]\ndns = [{{ resolver = \"198.51.100.53\", query = \"example.invalid\" }}]\n",
root.join("data").display()
),
)

View file

@ -629,7 +629,7 @@ impl Fixture {
fs::write(
&configuration_path,
format!(
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[profiles.default]\nimage = \"example.invalid/runtime-fixture:v1\"\npersistent_dirs = [\"/tooling\", \"/fixtures\"]\nenv = {{ PATH = \"env:BENCHMARK_PATH\", CARGO_HOME = \"env:BENCHMARK_CARGO_HOME\", RUSTUP_HOME = \"env:BENCHMARK_RUSTUP_HOME\", CARGO_BUILD_JOBS = \"env:BENCHMARK_CARGO_BUILD_JOBS\" }}\nvolumes = [\n {{ host = \"{}\", container = \"/tooling/usr\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/fixtures/host\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/fixtures/output\", kind = \"directory\", access = \"read-write\" }},\n {{ host = \"{}\", container = \"/fixtures/repository\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/tooling/cargo\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/tooling/rustup\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/tooling/uv-bin\", kind = \"directory\", access = \"read-only\" }},\n]\n",
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[network]\ndefault_allow = [\"192.0.2.10/32\"]\n\n[profiles.default]\nimage = \"example.invalid/runtime-fixture:v1\"\npersistent_dirs = [\"/tooling\", \"/fixtures\"]\nenv = {{ PATH = \"env:BENCHMARK_PATH\", CARGO_HOME = \"env:BENCHMARK_CARGO_HOME\", RUSTUP_HOME = \"env:BENCHMARK_RUSTUP_HOME\", CARGO_BUILD_JOBS = \"env:BENCHMARK_CARGO_BUILD_JOBS\" }}\nvolumes = [\n {{ host = \"{}\", container = \"/tooling/usr\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/fixtures/host\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/fixtures/output\", kind = \"directory\", access = \"read-write\" }},\n {{ host = \"{}\", container = \"/fixtures/repository\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/tooling/cargo\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/tooling/rustup\", kind = \"directory\", access = \"read-only\" }},\n {{ host = \"{}\", container = \"/tooling/uv-bin\", kind = \"directory\", access = \"read-only\" }},\n]\n\n[profiles.default.readiness]\ndenied = [{{ address = \"10.20.30.40\", port = 443 }}]\npermitted = [{{ address = \"192.0.2.10\", port = 443 }}]\ndns = [{{ resolver = \"198.51.100.53\", query = \"example.invalid\" }}]\n",
root.join("data").display(),
Path::new("/usr").display(),
host_root.display(),
@ -708,7 +708,7 @@ impl Fixture {
fs::write(
&configuration_path,
format!(
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[profiles.default]\nimage = \"example.invalid/runtime-fixture:v1\"\npersistent_dirs = []\n",
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[network]\ndefault_allow = [\"192.0.2.10/32\"]\n\n[profiles.default]\nimage = \"example.invalid/runtime-fixture:v1\"\npersistent_dirs = []\n\n[profiles.default.readiness]\ndenied = [{{ address = \"10.20.30.40\", port = 443 }}]\npermitted = [{{ address = \"192.0.2.10\", port = 443 }}]\ndns = [{{ resolver = \"198.51.100.53\", query = \"example.invalid\" }}]\n",
root.join("data").display()
),
)?;
@ -1026,7 +1026,7 @@ fn network_configuration(
fs::write(
&configuration_path,
format!(
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[profiles.default]\nimage = \"example.invalid/network-fixture:v1\"\npersistent_dirs = []\n",
"schema = \"lazaret-trusted-host-config-v1\"\n\n[storage]\ndata_root = \"{}\"\n\n[network]\ndefault_allow = [\"192.0.2.10/32\"]\n\n[profiles.default]\nimage = \"example.invalid/network-fixture:v1\"\npersistent_dirs = []\n\n[profiles.default.readiness]\ndenied = [{{ address = \"10.20.30.40\", port = 443 }}]\npermitted = [{{ address = \"192.0.2.10\", port = 443 }}]\ndns = [{{ resolver = \"198.51.100.53\", query = \"example.invalid\" }}]\n",
root.join("data").display()
),
)?;