expose network configuration as envs to containers

This commit is contained in:
elliptic
2024-02-13 02:43:11 -05:00
parent 4b7999fb04
commit fbbe26aad1
6 changed files with 145 additions and 34 deletions

View File

@@ -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<IpAssign>,
pub mount_req: Vec<Mount>,
pub vnet: bool,
pub init: Vec<Jexec>,
pub deinit: Vec<Jexec>,
pub main: Option<Jexec>,
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<String>,
pub origin_image: Option<JailImage>,
@@ -108,6 +121,8 @@ pub struct CreateContainer {
pub children_max: u32,
pub main_ip_selector: Option<MainAddressSelector>,
pub envs: HashMap<String, String>,
}
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
})
}
}

View File

@@ -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);

View File

@@ -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<PathBuf>,
pub main_ip_selector: Option<MainAddressSelector>,
pub envs: HashMap<String, String>,
}
pub struct ContainerNetworkIter<'a>(std::slice::Iter<'a, IpAssign>);
impl<'a> Iterator for ContainerNetworkIter<'a> {
type Item = &'a IpAssign;
fn next(&mut self) -> Option<Self::Item> {
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<IpAddr> {
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<AssignedAddress> {
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),
}
}
}

View File

@@ -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<Item = &'a IpAssign>>(selector: &Option<Self>, pool: I) -> Option<AssignedAddress> {
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 {

View File

@@ -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 = {

View File

@@ -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() {