mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Feature/callbacks (#2678)
* wip * initialize callbacks * wip * smtp * list_service_interfaces * wip * wip * fix domains * fix hostname handling in NetService * misc fixes * getInstalledPackages * misc fixes * publish v6 lib * refactor service effects * fix import * fix container runtime * fix tests * apply suggestions from review
This commit is contained in:
@@ -4,6 +4,7 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::InternedString;
|
||||
use models::{HostId, OptionExt, PackageId};
|
||||
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
||||
use tracing::instrument;
|
||||
@@ -67,6 +68,14 @@ impl PreInitNetController {
|
||||
alpn.clone(),
|
||||
)
|
||||
.await?;
|
||||
self.vhost
|
||||
.add(
|
||||
Some("startos".into()),
|
||||
443,
|
||||
([127, 0, 0, 1], 80).into(),
|
||||
alpn.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// LAN IP
|
||||
self.os_bindings.push(
|
||||
@@ -113,7 +122,9 @@ impl PreInitNetController {
|
||||
self.os_bindings.push(
|
||||
self.vhost
|
||||
.add(
|
||||
Some(tor_key.public().get_onion_address().to_string()),
|
||||
Some(InternedString::from_display(
|
||||
&tor_key.public().get_onion_address(),
|
||||
)),
|
||||
443,
|
||||
([127, 0, 0, 1], 80).into(),
|
||||
alpn.clone(),
|
||||
@@ -189,7 +200,15 @@ impl NetController {
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct HostBinds {
|
||||
lan: BTreeMap<u16, (LanInfo, Option<AddSslOptions>, Vec<Arc<()>>)>,
|
||||
lan: BTreeMap<
|
||||
u16,
|
||||
(
|
||||
LanInfo,
|
||||
Option<AddSslOptions>,
|
||||
BTreeSet<InternedString>,
|
||||
Vec<Arc<()>>,
|
||||
),
|
||||
>,
|
||||
tor: BTreeMap<OnionAddressV3, (OrdMap<u16, SocketAddr>, Vec<Arc<()>>)>,
|
||||
}
|
||||
|
||||
@@ -234,20 +253,35 @@ impl NetService {
|
||||
.await?;
|
||||
self.update(id, host).await
|
||||
}
|
||||
|
||||
pub async fn clear_bindings(&mut self) -> Result<(), Error> {
|
||||
// TODO BLUJ
|
||||
Ok(())
|
||||
let ctrl = self.net_controller()?;
|
||||
let mut errors = ErrorCollection::new();
|
||||
for (_, binds) in std::mem::take(&mut self.binds) {
|
||||
for (_, (lan, _, _, rc)) in binds.lan {
|
||||
drop(rc);
|
||||
if let Some(external) = lan.assigned_ssl_port {
|
||||
ctrl.vhost.gc(None, external).await?;
|
||||
}
|
||||
if let Some(external) = lan.assigned_port {
|
||||
ctrl.forward.gc(external).await?;
|
||||
}
|
||||
}
|
||||
for (addr, (_, rcs)) in binds.tor {
|
||||
drop(rcs);
|
||||
errors.handle(ctrl.tor.gc(Some(addr), None).await);
|
||||
}
|
||||
}
|
||||
std::mem::take(&mut self.dns);
|
||||
errors.handle(ctrl.dns.gc(Some(self.id.clone()), self.ip).await);
|
||||
errors.into_result()
|
||||
}
|
||||
|
||||
async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> {
|
||||
let ctrl = self.net_controller()?;
|
||||
let mut hostname_info = BTreeMap::new();
|
||||
let binds = {
|
||||
if !self.binds.contains_key(&id) {
|
||||
self.binds.insert(id.clone(), Default::default());
|
||||
}
|
||||
self.binds.get_mut(&id).unwrap()
|
||||
};
|
||||
let binds = self.binds.entry(id.clone()).or_default();
|
||||
|
||||
let peek = ctrl.db.peek().await;
|
||||
|
||||
// LAN
|
||||
@@ -256,37 +290,71 @@ impl NetService {
|
||||
let hostname = server_info.as_hostname().de()?;
|
||||
for (port, bind) in &host.bindings {
|
||||
let old_lan_bind = binds.lan.remove(port);
|
||||
let old_lan_port = old_lan_bind.as_ref().map(|(external, _, _)| *external);
|
||||
let lan_bind = old_lan_bind
|
||||
.filter(|(external, ssl, _)| ssl == &bind.options.add_ssl && bind.lan == *external); // only keep existing binding if relevant details match
|
||||
.as_ref()
|
||||
.filter(|(external, ssl, _, _)| {
|
||||
ssl == &bind.options.add_ssl && bind.lan == *external
|
||||
})
|
||||
.cloned(); // only keep existing binding if relevant details match
|
||||
if bind.lan.assigned_port.is_some() || bind.lan.assigned_ssl_port.is_some() {
|
||||
let new_lan_bind = if let Some(b) = lan_bind {
|
||||
b
|
||||
} else {
|
||||
let mut rcs = Vec::with_capacity(2);
|
||||
let mut rcs = Vec::with_capacity(2 + host.addresses.len());
|
||||
let mut hostnames = BTreeSet::new();
|
||||
if let Some(ssl) = &bind.options.add_ssl {
|
||||
let external = bind
|
||||
.lan
|
||||
.assigned_ssl_port
|
||||
.or_not_found("assigned ssl port")?;
|
||||
let target = (self.ip, *port).into();
|
||||
let connect_ssl = if let Some(alpn) = ssl.alpn.clone() {
|
||||
Err(alpn)
|
||||
} else {
|
||||
if bind.options.secure.as_ref().map_or(false, |s| s.ssl) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AlpnInfo::Reflect)
|
||||
}
|
||||
};
|
||||
rcs.push(
|
||||
ctrl.vhost
|
||||
.add(
|
||||
None,
|
||||
external,
|
||||
(self.ip, *port).into(),
|
||||
if let Some(alpn) = ssl.alpn.clone() {
|
||||
Err(alpn)
|
||||
} else {
|
||||
if bind.options.secure.as_ref().map_or(false, |s| s.ssl) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AlpnInfo::Reflect)
|
||||
}
|
||||
},
|
||||
)
|
||||
.add(None, external, target, connect_ssl.clone())
|
||||
.await?,
|
||||
);
|
||||
for address in host.addresses() {
|
||||
match address {
|
||||
HostAddress::Onion { address } => {
|
||||
let hostname = InternedString::from_display(address);
|
||||
if hostnames.insert(hostname.clone()) {
|
||||
rcs.push(
|
||||
ctrl.vhost
|
||||
.add(
|
||||
Some(hostname),
|
||||
external,
|
||||
target,
|
||||
connect_ssl.clone(),
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
HostAddress::Domain { address } => {
|
||||
if hostnames.insert(address.clone()) {
|
||||
rcs.push(
|
||||
ctrl.vhost
|
||||
.add(
|
||||
Some(address.clone()),
|
||||
external,
|
||||
target,
|
||||
connect_ssl.clone(),
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(security) = bind.options.secure {
|
||||
if bind.options.add_ssl.is_some() && security.ssl {
|
||||
@@ -297,7 +365,7 @@ impl NetService {
|
||||
rcs.push(ctrl.forward.add(external, (self.ip, *port).into()).await?);
|
||||
}
|
||||
}
|
||||
(bind.lan, bind.options.add_ssl.clone(), rcs)
|
||||
(bind.lan, bind.options.add_ssl.clone(), hostnames, rcs)
|
||||
};
|
||||
let mut bind_hostname_info: Vec<HostnameInfo> =
|
||||
hostname_info.remove(port).unwrap_or_default();
|
||||
@@ -337,9 +405,12 @@ impl NetService {
|
||||
hostname_info.insert(*port, bind_hostname_info);
|
||||
binds.lan.insert(*port, new_lan_bind);
|
||||
}
|
||||
if let Some(lan) = old_lan_port {
|
||||
if let Some((lan, _, hostnames, _)) = old_lan_bind {
|
||||
if let Some(external) = lan.assigned_ssl_port {
|
||||
ctrl.vhost.gc(None, external).await?;
|
||||
for hostname in hostnames {
|
||||
ctrl.vhost.gc(Some(hostname), external).await?;
|
||||
}
|
||||
}
|
||||
if let Some(external) = lan.assigned_port {
|
||||
ctrl.forward.gc(external).await?;
|
||||
@@ -347,18 +418,21 @@ impl NetService {
|
||||
}
|
||||
}
|
||||
let mut removed = BTreeSet::new();
|
||||
binds.lan.retain(|internal, (external, _, _)| {
|
||||
binds.lan.retain(|internal, (external, _, hostnames, _)| {
|
||||
if host.bindings.contains_key(internal) {
|
||||
true
|
||||
} else {
|
||||
removed.insert(*external);
|
||||
removed.insert((*external, std::mem::take(hostnames)));
|
||||
|
||||
false
|
||||
}
|
||||
});
|
||||
for lan in removed {
|
||||
for (lan, hostnames) in removed {
|
||||
if let Some(external) = lan.assigned_ssl_port {
|
||||
ctrl.vhost.gc(None, external).await?;
|
||||
for hostname in hostnames {
|
||||
ctrl.vhost.gc(Some(hostname), external).await?;
|
||||
}
|
||||
}
|
||||
if let Some(external) = lan.assigned_port {
|
||||
ctrl.forward.gc(external).await?;
|
||||
@@ -401,48 +475,44 @@ impl NetService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut keep_tor_addrs = BTreeSet::new();
|
||||
for addr in match host.kind {
|
||||
HostKind::Multi => {
|
||||
// itertools::Either::Left(
|
||||
host.addresses.iter()
|
||||
// )
|
||||
} // HostKind::Single | HostKind::Static => itertools::Either::Right(&host.primary),
|
||||
} {
|
||||
match addr {
|
||||
HostAddress::Onion { address } => {
|
||||
keep_tor_addrs.insert(address);
|
||||
let old_tor_bind = binds.tor.remove(address);
|
||||
let tor_bind = old_tor_bind.filter(|(ports, _)| ports == &tor_binds);
|
||||
let new_tor_bind = if let Some(tor_bind) = tor_bind {
|
||||
tor_bind
|
||||
} else {
|
||||
let key = peek
|
||||
.as_private()
|
||||
.as_key_store()
|
||||
.as_onion()
|
||||
.get_key(address)?;
|
||||
let rcs = ctrl
|
||||
.tor
|
||||
.add(key, tor_binds.clone().into_iter().collect())
|
||||
.await?;
|
||||
(tor_binds.clone(), rcs)
|
||||
};
|
||||
for (internal, ports) in &tor_hostname_ports {
|
||||
let mut bind_hostname_info =
|
||||
hostname_info.remove(internal).unwrap_or_default();
|
||||
bind_hostname_info.push(HostnameInfo::Onion {
|
||||
hostname: OnionHostname {
|
||||
value: address.to_string(),
|
||||
port: ports.non_ssl,
|
||||
ssl_port: ports.ssl,
|
||||
},
|
||||
});
|
||||
hostname_info.insert(*internal, bind_hostname_info);
|
||||
}
|
||||
binds.tor.insert(address.clone(), new_tor_bind);
|
||||
}
|
||||
for tor_addr in host.addresses().filter_map(|a| {
|
||||
if let HostAddress::Onion { address } = a {
|
||||
Some(address)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
keep_tor_addrs.insert(tor_addr);
|
||||
let old_tor_bind = binds.tor.remove(tor_addr);
|
||||
let tor_bind = old_tor_bind.filter(|(ports, _)| ports == &tor_binds);
|
||||
let new_tor_bind = if let Some(tor_bind) = tor_bind {
|
||||
tor_bind
|
||||
} else {
|
||||
let key = peek
|
||||
.as_private()
|
||||
.as_key_store()
|
||||
.as_onion()
|
||||
.get_key(tor_addr)?;
|
||||
let rcs = ctrl
|
||||
.tor
|
||||
.add(key, tor_binds.clone().into_iter().collect())
|
||||
.await?;
|
||||
(tor_binds.clone(), rcs)
|
||||
};
|
||||
for (internal, ports) in &tor_hostname_ports {
|
||||
let mut bind_hostname_info = hostname_info.remove(internal).unwrap_or_default();
|
||||
bind_hostname_info.push(HostnameInfo::Onion {
|
||||
hostname: OnionHostname {
|
||||
value: tor_addr.to_string(),
|
||||
port: ports.non_ssl,
|
||||
ssl_port: ports.ssl,
|
||||
},
|
||||
});
|
||||
hostname_info.insert(*internal, bind_hostname_info);
|
||||
}
|
||||
binds.tor.insert(tor_addr.clone(), new_tor_bind);
|
||||
}
|
||||
for addr in binds.tor.keys() {
|
||||
if !keep_tor_addrs.contains(addr) {
|
||||
@@ -462,26 +532,8 @@ impl NetService {
|
||||
|
||||
pub async fn remove_all(mut self) -> Result<(), Error> {
|
||||
self.shutdown = true;
|
||||
let mut errors = ErrorCollection::new();
|
||||
if let Some(ctrl) = Weak::upgrade(&self.controller) {
|
||||
for (_, binds) in std::mem::take(&mut self.binds) {
|
||||
for (_, (lan, _, rc)) in binds.lan {
|
||||
drop(rc);
|
||||
if let Some(external) = lan.assigned_ssl_port {
|
||||
ctrl.vhost.gc(None, external).await?;
|
||||
}
|
||||
if let Some(external) = lan.assigned_port {
|
||||
ctrl.forward.gc(external).await?;
|
||||
}
|
||||
}
|
||||
for (addr, (_, rcs)) in binds.tor {
|
||||
drop(rcs);
|
||||
errors.handle(ctrl.tor.gc(Some(addr), None).await);
|
||||
}
|
||||
}
|
||||
std::mem::take(&mut self.dns);
|
||||
errors.handle(ctrl.dns.gc(Some(self.id.clone()), self.ip).await);
|
||||
errors.into_result()
|
||||
self.clear_bindings().await
|
||||
} else {
|
||||
tracing::warn!("NetService dropped after NetController is shutdown");
|
||||
Err(Error::new(
|
||||
@@ -495,11 +547,11 @@ impl NetService {
|
||||
self.ip
|
||||
}
|
||||
|
||||
pub fn get_ext_port(&self, host_id: HostId, internal_port: u16) -> Result<LanInfo, Error> {
|
||||
pub fn get_lan_port(&self, host_id: HostId, internal_port: u16) -> Result<LanInfo, Error> {
|
||||
let host_id_binds = self.binds.get_key_value(&host_id);
|
||||
match host_id_binds {
|
||||
Some((_, binds)) => {
|
||||
if let Some((lan, _, _)) = binds.lan.get(&internal_port) {
|
||||
if let Some((lan, _, _, _)) = binds.lan.get(&internal_port) {
|
||||
Ok(*lan)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
|
||||
Reference in New Issue
Block a user