Compose protected runtime files before sandbox handoff
This commit is contained in:
parent
85f0db08d7
commit
af9b4de85a
3 changed files with 103 additions and 2 deletions
|
|
@ -180,6 +180,10 @@ impl<'a> ColdStart<'a> {
|
||||||
.map_err(|error| failure(format!("Git-routing staging: {error}")))?;
|
.map_err(|error| failure(format!("Git-routing staging: {error}")))?;
|
||||||
let expected_network_namespace = namespace_identity(Path::new(NETWORK_NAMESPACE_PATH))
|
let expected_network_namespace = namespace_identity(Path::new(NETWORK_NAMESPACE_PATH))
|
||||||
.map_err(|error| failure(format!("network namespace: {error}")))?;
|
.map_err(|error| failure(format!("network namespace: {error}")))?;
|
||||||
|
let network_policy = derive_ipv4_policy(inputs.configuration)
|
||||||
|
.map_err(|error| failure(format!("network policy: {error}")))?;
|
||||||
|
runtime_files::replace_protected_runtime_files(&state_anchor_layout, &network_policy)
|
||||||
|
.map_err(|error| failure(format!("protected runtime files: {error}")))?;
|
||||||
let request = SandboxBuildRequest {
|
let request = SandboxBuildRequest {
|
||||||
configuration: inputs.configuration,
|
configuration: inputs.configuration,
|
||||||
container_id: inputs.container_id,
|
container_id: inputs.container_id,
|
||||||
|
|
@ -198,8 +202,6 @@ impl<'a> ColdStart<'a> {
|
||||||
staging
|
staging
|
||||||
.write_bundle(sandbox_bundle.specification_json())
|
.write_bundle(sandbox_bundle.specification_json())
|
||||||
.map_err(|error| failure(format!("bundle staging: {error}")))?;
|
.map_err(|error| failure(format!("bundle staging: {error}")))?;
|
||||||
let network_policy = derive_ipv4_policy(inputs.configuration)
|
|
||||||
.map_err(|error| failure(format!("network policy: {error}")))?;
|
|
||||||
let ownership = ProcessOwnershipContext {
|
let ownership = ProcessOwnershipContext {
|
||||||
domain_id: inputs.generation.domain_id,
|
domain_id: inputs.generation.domain_id,
|
||||||
generation_id: inputs.generation.generation_id,
|
generation_id: inputs.generation.generation_id,
|
||||||
|
|
@ -414,6 +416,7 @@ fn require_inputs(inputs: &ColdStartInputs<'_>) -> Result<(), ColdStartError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod git_routing;
|
mod git_routing;
|
||||||
|
mod runtime_files;
|
||||||
mod runtime_staging;
|
mod runtime_staging;
|
||||||
|
|
||||||
use git_routing::GitRoutingLayout;
|
use git_routing::GitRoutingLayout;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
|
use lazaret_core::network::CanonicalDerivedNetworkPolicy;
|
||||||
|
use lazaret_core::records::IpAddress;
|
||||||
|
use lazaret_state_anchor::{ProtectedRuntimeFiles, StateAnchorError, StateAnchorLayout};
|
||||||
|
|
||||||
|
pub(super) fn replace_protected_runtime_files(
|
||||||
|
layout: &StateAnchorLayout<'_>,
|
||||||
|
policy: &CanonicalDerivedNetworkPolicy,
|
||||||
|
) -> Result<(), StateAnchorError> {
|
||||||
|
let resolv_conf = policy
|
||||||
|
.policy()
|
||||||
|
.resolvers
|
||||||
|
.iter()
|
||||||
|
.map(|resolver| match resolver.address {
|
||||||
|
IpAddress::V4(address) => format!("nameserver {}\n", Ipv4Addr::from(address)),
|
||||||
|
IpAddress::V6(address) => format!("nameserver {}\n", Ipv6Addr::from(address)),
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
layout.replace_protected_runtime_files(&ProtectedRuntimeFiles {
|
||||||
|
resolv_conf: resolv_conf.as_bytes(),
|
||||||
|
hosts: b"127.0.0.1 localhost\n",
|
||||||
|
hostname: b"lazaret\n",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ use lazaret_linux::user_namespace::{
|
||||||
REQUIRED_SUBORDINATE_IDS, UserNamespaceIdRange, UserNamespaceMap,
|
REQUIRED_SUBORDINATE_IDS, UserNamespaceIdRange, UserNamespaceMap,
|
||||||
};
|
};
|
||||||
use lazaret_runtime_supervisor::sandbox::{RootProcess, default_sandbox_layout};
|
use lazaret_runtime_supervisor::sandbox::{RootProcess, default_sandbox_layout};
|
||||||
|
use lazaret_state_anchor::StateAnchorLayout;
|
||||||
use lazaret_storage_catalog::Catalog;
|
use lazaret_storage_catalog::Catalog;
|
||||||
use rustix::process::{getgid, getuid};
|
use rustix::process::{getgid, getuid};
|
||||||
|
|
||||||
|
|
@ -48,6 +49,70 @@ fn non_git_workspace_stages_without_a_routing_runtime() {
|
||||||
drop(staged);
|
drop(staged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn production_stage_composes_derived_resolver_outside_the_prepared_lower() {
|
||||||
|
let fixture = ColdStartFixture::new();
|
||||||
|
let image_resolver = fixture.prepared_lower.join("etc/resolv.conf");
|
||||||
|
let upper_resolver = fixture
|
||||||
|
.persistent_domain
|
||||||
|
.join("state-anchor/upper/etc/resolv.conf");
|
||||||
|
let empty_runtime_resolver = StateAnchorLayout::create(&fixture.configuration).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
fs::read(empty_runtime_resolver.runtime_files().join("resolv.conf")).unwrap(),
|
||||||
|
b""
|
||||||
|
);
|
||||||
|
drop(empty_runtime_resolver);
|
||||||
|
|
||||||
|
let staged = ColdStart::stage(fixture.inputs(&fixture.escalation_client)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fs::read(&image_resolver).unwrap(), b"image resolver\n");
|
||||||
|
assert!(!upper_resolver.exists());
|
||||||
|
assert_eq!(
|
||||||
|
fs::read(
|
||||||
|
fixture
|
||||||
|
.runtime_domain
|
||||||
|
.join("state-anchor/runtime-files/resolv.conf")
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
b"nameserver 10.137.54.3\n"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fs::read(
|
||||||
|
fixture
|
||||||
|
.runtime_domain
|
||||||
|
.join("state-anchor/runtime-files/hosts")
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
b"127.0.0.1 localhost\n"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fs::read(
|
||||||
|
fixture
|
||||||
|
.runtime_domain
|
||||||
|
.join("state-anchor/runtime-files/hostname")
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
b"lazaret\n"
|
||||||
|
);
|
||||||
|
let specification: serde_json::Value =
|
||||||
|
serde_json::from_slice(&fs::read(fixture.bundle().join("config.json")).unwrap()).unwrap();
|
||||||
|
let resolver_mount = specification["mounts"]
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|mount| mount["destination"] == "/etc/resolv.conf")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
resolver_mount["source"],
|
||||||
|
fixture
|
||||||
|
.runtime_domain
|
||||||
|
.join("state-anchor/runtime-files/resolv.conf")
|
||||||
|
.display()
|
||||||
|
.to_string()
|
||||||
|
);
|
||||||
|
drop(staged);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cleanup_leaves_a_replacement_directory_untouched() {
|
fn cleanup_leaves_a_replacement_directory_untouched() {
|
||||||
let fixture = ColdStartFixture::new();
|
let fixture = ColdStartFixture::new();
|
||||||
|
|
@ -96,7 +161,9 @@ fn replaced_runtime_domain_authority_is_not_used_for_staging() {
|
||||||
struct ColdStartFixture {
|
struct ColdStartFixture {
|
||||||
tree: tempfile::TempDir,
|
tree: tempfile::TempDir,
|
||||||
configuration: lazaret_core::configuration::ResolvedDomainConfiguration,
|
configuration: lazaret_core::configuration::ResolvedDomainConfiguration,
|
||||||
|
persistent_domain: PathBuf,
|
||||||
runtime_domain: PathBuf,
|
runtime_domain: PathBuf,
|
||||||
|
prepared_lower: PathBuf,
|
||||||
catalog: Catalog,
|
catalog: Catalog,
|
||||||
store: Arc<HostCoordinatorDomainStore>,
|
store: Arc<HostCoordinatorDomainStore>,
|
||||||
artifact: PreparedArtifact,
|
artifact: PreparedArtifact,
|
||||||
|
|
@ -149,6 +216,10 @@ impl ColdStartFixture {
|
||||||
);
|
);
|
||||||
let lower = tree.path().join("prepared-lower");
|
let lower = tree.path().join("prepared-lower");
|
||||||
fs::create_dir(&lower).unwrap();
|
fs::create_dir(&lower).unwrap();
|
||||||
|
fs::create_dir(lower.join("etc")).unwrap();
|
||||||
|
fs::write(lower.join("etc/resolv.conf"), b"image resolver\n").unwrap();
|
||||||
|
fs::write(lower.join("etc/hosts"), b"image hosts\n").unwrap();
|
||||||
|
fs::write(lower.join("etc/hostname"), b"image hostname\n").unwrap();
|
||||||
let metadata = lower.metadata().unwrap();
|
let metadata = lower.metadata().unwrap();
|
||||||
let artifact_authority = File::open(&lower).unwrap();
|
let artifact_authority = File::open(&lower).unwrap();
|
||||||
let artifact = PreparedArtifact {
|
let artifact = PreparedArtifact {
|
||||||
|
|
@ -174,7 +245,9 @@ impl ColdStartFixture {
|
||||||
Self {
|
Self {
|
||||||
tree,
|
tree,
|
||||||
configuration,
|
configuration,
|
||||||
|
persistent_domain,
|
||||||
runtime_domain,
|
runtime_domain,
|
||||||
|
prepared_lower: lower,
|
||||||
catalog,
|
catalog,
|
||||||
store,
|
store,
|
||||||
artifact,
|
artifact,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue