From fbbe26aad19f84e3588bbcde07b185b723fa93b4 Mon Sep 17 00:00:00 2001 From: elliptic Date: Tue, 13 Feb 2024 02:43:11 -0500 Subject: [PATCH] expose network configuration as envs to containers --- xc/src/container/mod.rs | 18 +++++++++- xc/src/container/runner/mod.rs | 44 +++++++++++++++++++++++-- xc/src/container/running.rs | 60 +++++++++++++++++----------------- xc/src/models/network.rs | 54 ++++++++++++++++++++++++++++++ xcd/src/instantiate.rs | 2 ++ xcd/src/site.rs | 1 + 6 files changed, 145 insertions(+), 34 deletions(-) diff --git a/xc/src/container/mod.rs b/xc/src/container/mod.rs index 2381b2b..8899c3f 100644 --- a/xc/src/container/mod.rs +++ b/xc/src/container/mod.rs @@ -67,17 +67,29 @@ pub struct CreateContainer { pub root: String, /// The devfs ruleset id assigned to this container pub devfs_ruleset_id: u16, + pub ip_alloc: Vec, + pub mount_req: Vec, + pub vnet: bool, + pub init: Vec, + pub deinit: Vec, + pub main: Option, + pub linux: bool, + pub main_norun: bool, + pub init_norun: bool, + pub deinit_norun: bool, + pub persist: bool, + pub no_clean: bool, /// Do not create /proc automatically and abort mounting procfs if the directory is missing. pub linux_no_create_proc_dir: bool, @@ -87,6 +99,7 @@ pub struct CreateContainer { pub linux_no_mount_sys: bool, /// Do not mount linux procfs pub linux_no_mount_proc: bool, + pub zfs_origin: Option, pub origin_image: Option, @@ -108,6 +121,8 @@ pub struct CreateContainer { pub children_max: u32, pub main_ip_selector: Option, + + pub envs: HashMap, } impl CreateContainer { @@ -413,7 +428,8 @@ impl CreateContainer { started: None, finished_at: None, jailed_datasets: self.jailed_datasets, - main_ip_selector: None, + main_ip_selector: self.main_ip_selector, + envs: self.envs }) } } diff --git a/xc/src/container/runner/mod.rs b/xc/src/container/runner/mod.rs index f7dadeb..8ed69b7 100644 --- a/xc/src/container/runner/mod.rs +++ b/xc/src/container/runner/mod.rs @@ -194,9 +194,10 @@ impl ProcessRunner { info!("spawn: {exec:#?}"); container_runner::spawn_process!(|| (self.container.jid, id, exec)); + let mut envs = self.container.envs.clone(); + let jail = freebsd::jail::RunningJail::from_jid_unchecked(self.container.jid); - let paths = exec - .envs + let paths = envs .get("PATH") .cloned() .unwrap_or_else(|| "/bin:/usr/bin:/sbin:/usr/sbin".to_string()); @@ -247,11 +248,48 @@ impl ProcessRunner { }, }; + + for (key, value) in exec.envs.iter() { + envs.insert(key.to_string(), value.to_string()); + } + + if let Some(address) = self.container.main_address() { + // allow faking the environ to make debugging easier + if !envs.contains_key("XC_MAIN_IP") { + envs.insert("XC_MAIN_IP".to_string(), address.address.to_string()); + } + if !envs.contains_key("XC_MAIN_IFACE") { + envs.insert("XC_MAIN_IFACE".to_string(), address.interface); + } + } + + let mut networks_count = 0; + for network in self.container.networks() { + networks_count += 1; + let network_name = network.network.as_ref().unwrap(); + envs.insert( + format!("XC_NETWORK_{network_name}_ADDR_COUNT"), + network.addresses.len().to_string() + ); + envs.insert( + format!("XC_NETWORK_{network_name}_IFACE"), + network.interface.to_string(), + ); + for (i, addr) in network.addresses.iter().enumerate() { + envs.insert(format!("XC_NETWORK_{network_name}_ADDR_{i}"), addr.to_string()); + } + } + + envs.insert("XC_NETWORKS_COUNT".to_string(), networks_count.to_string()); + + envs.insert("XC_ID".to_string(), self.container.id.to_string()); + + let mut cmd = std::process::Command::new(&exec.arg0); cmd.env_clear() .args(&exec.args) - .envs(&exec.envs) + .envs(envs) .jail(&jail) .juid(uid) .jgid(gid); diff --git a/xc/src/container/running.rs b/xc/src/container/running.rs index 44b9fad..cda5501 100644 --- a/xc/src/container/running.rs +++ b/xc/src/container/running.rs @@ -27,7 +27,7 @@ use crate::container::request::Mount; use crate::container::ContainerManifest; use crate::models::exec::Jexec; use crate::models::jail_image::JailImage; -use crate::models::network::{DnsSetting, IpAssign, MainAddressSelector}; +use crate::models::network::{DnsSetting, IpAssign, MainAddressSelector, AssignedAddress}; use crate::util::realpath; use anyhow::Context; @@ -93,6 +93,28 @@ pub struct RunningContainer { pub jailed_datasets: Vec, pub main_ip_selector: Option, + + pub envs: HashMap, +} + +pub struct ContainerNetworkIter<'a>(std::slice::Iter<'a, IpAssign>); + +impl<'a> Iterator for ContainerNetworkIter<'a> { + type Item = &'a IpAssign; + fn next(&mut self) -> Option { + loop { + match self.0.next() { + None => return None, + x@Some(assign) => { + if assign.network.is_none() { + continue + } else { + return x + } + } + } + } + } } impl RunningContainer { @@ -158,34 +180,12 @@ impl RunningContainer { Ok(()) } - pub fn main_address(&self) -> Option { - match &self.main_ip_selector { - Some(MainAddressSelector::Ip(address)) => Some(*address), - Some(MainAddressSelector::Network(network)) => { - for alloc in self.ip_alloc.iter() { - match alloc.network.as_ref() { - Some(_network) if network == _network => { - match alloc.addresses.first() { - None => continue, - Some(addr) => return Some(addr.addr()) - } - }, - _ => continue - } - } - None - }, - None => { - for alloc in self.ip_alloc.iter() { - if alloc.network.is_some() { - if let Some(address) = alloc.addresses.first() { - return Some(address.addr()) - } - } - } - None - }, - } + pub fn main_address(&self) -> Option { + MainAddressSelector::select(&self.main_ip_selector, self.ip_alloc.iter()) + } + + pub fn networks(&self) -> ContainerNetworkIter<'_> { + ContainerNetworkIter(self.ip_alloc.iter()) } pub fn serialized(&self) -> ContainerManifest { @@ -224,7 +224,7 @@ impl RunningContainer { started: self.started, finished_at: self.finished_at, created: self.created, - main_address: self.main_address(), + main_address: self.main_address().map(|a| a.address), } } } diff --git a/xc/src/models/network.rs b/xc/src/models/network.rs index ac9a527..bd0126f 100644 --- a/xc/src/models/network.rs +++ b/xc/src/models/network.rs @@ -163,6 +163,60 @@ pub enum MainAddressSelector { Ip(IpAddr) } +pub struct AssignedAddress { + pub interface: String, + pub address: IpAddr, +} + +impl AssignedAddress { + pub fn new(interface: String, address: IpAddr) -> AssignedAddress { + AssignedAddress { + interface, + address, + } + } +} + +impl MainAddressSelector { + pub fn select<'a, I: Iterator>(selector: &Option, pool: I) -> Option { + match selector { + None => { + for alloc in pool { + if alloc.network.is_some() { + if let Some(address) = alloc.addresses.first() { + return Some(AssignedAddress::new(alloc.interface.to_string(), address.addr())) + } + } + } + None + }, + Some(MainAddressSelector::Ip(address)) => /*Some(*address)*/ { + for alloc in pool { + if alloc.addresses.iter().any(|addr| addr.addr() == *address) { + return Some(AssignedAddress::new(alloc.interface.to_string(), *address)) + } + } + None + }, + Some(MainAddressSelector::Network(network)) => { + for alloc in pool { + match alloc.network.as_ref() { + Some(_network) if network == _network => { + match alloc.addresses.first() { + None => continue, + Some(addr) => + return Some(AssignedAddress::new(alloc.interface.to_string(), addr.addr())) + } + }, + _ => continue + } + } + None + } + } + } +} + impl std::fmt::Display for MainAddressSelector { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/xcd/src/instantiate.rs b/xcd/src/instantiate.rs index df3a5f8..de2d080 100644 --- a/xcd/src/instantiate.rs +++ b/xcd/src/instantiate.rs @@ -460,6 +460,8 @@ impl InstantiateBlueprint { let devfs_ruleset_id = devfs_store.get_ruleset_id(&devfs_rules); + envs.insert("XC_DEVFS_RULESET".to_string(), devfs_ruleset_id.to_string()); + let main = match &request.request.entry_point { Some(spec) => { let args = { diff --git a/xcd/src/site.rs b/xcd/src/site.rs index 2cc5f3c..d6c7667 100644 --- a/xcd/src/site.rs +++ b/xcd/src/site.rs @@ -448,6 +448,7 @@ impl Site { jailed_datasets: blueprint.jailed_datasets, children_max: blueprint.children_max, main_ip_selector: blueprint.main_ip_selector, + envs: blueprint.envs, }; for iface in blueprint.created_interfaces.iter() {