mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Feature/network (#2622)
* Feature: Add in the clear bindings * wip: Working on network * fix: Make it so the config gives the url * chore: Remove the repeated types * chore: Add in the todo's here * chore: UPdate and remove some poorly name var * chore: Remove the clear-bindings impl * chore: Remove the wrapper * handle HostnameInfo for Host bindings Co-authored-by: Jade <Blu-J@users.noreply.github.com> * ?? * chore: Make the install work * Fix: Url's not being created * chore: Fix the local onion in url * include port in hostname * Chore of adding a comment just to modify. --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: Jade <Blu-J@users.noreply.github.com>
This commit is contained in:
@@ -10,8 +10,8 @@ use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::net::host::HostInfo;
|
||||
use crate::net::service_interface::ServiceInterfaceWithHostInfo;
|
||||
use crate::net::host::Hosts;
|
||||
use crate::net::service_interface::ServiceInterface;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::FullProgress;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
@@ -333,8 +333,8 @@ pub struct PackageDataEntry {
|
||||
pub last_backup: Option<DateTime<Utc>>,
|
||||
pub current_dependencies: CurrentDependencies,
|
||||
pub actions: BTreeMap<ActionId, ActionMetadata>,
|
||||
pub service_interfaces: BTreeMap<ServiceInterfaceId, ServiceInterfaceWithHostInfo>,
|
||||
pub hosts: HostInfo,
|
||||
pub service_interfaces: BTreeMap<ServiceInterfaceId, ServiceInterface>,
|
||||
pub hosts: Hosts,
|
||||
#[ts(type = "string[]")]
|
||||
pub store_exposed_dependents: Vec<JsonPointer>,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rpc_toolkit::{
|
||||
from_fn_async, CallRemoteHandler, Context, Empty, HandlerExt, ParentHandler,
|
||||
};
|
||||
use rpc_toolkit::{from_fn_async, CallRemoteHandler, Context, Empty, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::net::IpAddr;
|
||||
|
||||
use clap::Parser;
|
||||
use futures::TryStreamExt;
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler};
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::RwLock;
|
||||
use ts_rs::TS;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use imbl_value::InternedString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
@@ -11,17 +10,31 @@ use crate::prelude::*;
|
||||
#[ts(export)]
|
||||
pub struct BindInfo {
|
||||
pub options: BindOptions,
|
||||
pub assigned_lan_port: Option<u16>,
|
||||
pub lan: LanInfo,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, TS, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct LanInfo {
|
||||
pub assigned_port: Option<u16>,
|
||||
pub assigned_ssl_port: Option<u16>,
|
||||
}
|
||||
impl BindInfo {
|
||||
pub fn new(available_ports: &mut AvailablePorts, options: BindOptions) -> Result<Self, Error> {
|
||||
let mut assigned_lan_port = None;
|
||||
if options.add_ssl.is_some() || options.secure.is_some() {
|
||||
assigned_lan_port = Some(available_ports.alloc()?);
|
||||
let mut assigned_port = None;
|
||||
let mut assigned_ssl_port = None;
|
||||
if options.secure.is_some() {
|
||||
assigned_port = Some(available_ports.alloc()?);
|
||||
}
|
||||
if options.add_ssl.is_some() {
|
||||
assigned_ssl_port = Some(available_ports.alloc()?);
|
||||
}
|
||||
Ok(Self {
|
||||
options,
|
||||
assigned_lan_port,
|
||||
lan: LanInfo {
|
||||
assigned_port,
|
||||
assigned_ssl_port,
|
||||
},
|
||||
})
|
||||
}
|
||||
pub fn update(
|
||||
@@ -29,29 +42,38 @@ impl BindInfo {
|
||||
available_ports: &mut AvailablePorts,
|
||||
options: BindOptions,
|
||||
) -> Result<Self, Error> {
|
||||
let Self {
|
||||
mut assigned_lan_port,
|
||||
..
|
||||
} = self;
|
||||
if options.add_ssl.is_some() || options.secure.is_some() {
|
||||
assigned_lan_port = if let Some(port) = assigned_lan_port.take() {
|
||||
let Self { mut lan, .. } = self;
|
||||
if options
|
||||
.secure
|
||||
.map_or(false, |s| !(s.ssl && options.add_ssl.is_some()))
|
||||
// doesn't make sense to have 2 listening ports, both with ssl
|
||||
{
|
||||
lan.assigned_port = if let Some(port) = lan.assigned_port.take() {
|
||||
Some(port)
|
||||
} else {
|
||||
Some(available_ports.alloc()?)
|
||||
};
|
||||
} else {
|
||||
if let Some(port) = assigned_lan_port.take() {
|
||||
if let Some(port) = lan.assigned_port.take() {
|
||||
available_ports.free([port]);
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
options,
|
||||
assigned_lan_port,
|
||||
})
|
||||
if options.add_ssl.is_some() {
|
||||
lan.assigned_ssl_port = if let Some(port) = lan.assigned_ssl_port.take() {
|
||||
Some(port)
|
||||
} else {
|
||||
Some(available_ports.alloc()?)
|
||||
};
|
||||
} else {
|
||||
if let Some(port) = lan.assigned_ssl_port.take() {
|
||||
available_ports.free([port]);
|
||||
}
|
||||
}
|
||||
Ok(Self { options, lan })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Security {
|
||||
@@ -62,8 +84,6 @@ pub struct Security {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct BindOptions {
|
||||
#[ts(type = "string | null")]
|
||||
pub scheme: Option<InternedString>,
|
||||
pub preferred_external_port: u16,
|
||||
pub add_ssl: Option<AddSslOptions>,
|
||||
pub secure: Option<Security>,
|
||||
@@ -73,11 +93,8 @@ pub struct BindOptions {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct AddSslOptions {
|
||||
#[ts(type = "string | null")]
|
||||
pub scheme: Option<InternedString>,
|
||||
pub preferred_external_port: u16,
|
||||
// #[serde(default)]
|
||||
// pub add_x_forwarded_headers: bool, // TODO
|
||||
#[serde(default)]
|
||||
pub alpn: AlpnInfo,
|
||||
pub alpn: Option<AlpnInfo>,
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use imbl_value::InternedString;
|
||||
use models::{HostId, PackageId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::net::forward::AvailablePorts;
|
||||
use crate::net::host::address::HostAddress;
|
||||
use crate::net::host::binding::{BindInfo, BindOptions};
|
||||
use crate::net::service_interface::HostnameInfo;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub mod address;
|
||||
@@ -23,7 +23,8 @@ pub struct Host {
|
||||
pub kind: HostKind,
|
||||
pub bindings: BTreeMap<u16, BindInfo>,
|
||||
pub addresses: BTreeSet<HostAddress>,
|
||||
pub primary: Option<HostAddress>,
|
||||
/// COMPUTED: NetService::update
|
||||
pub hostname_info: BTreeMap<u16, Vec<HostnameInfo>>, // internal port -> Hostnames
|
||||
}
|
||||
impl AsRef<Host> for Host {
|
||||
fn as_ref(&self) -> &Host {
|
||||
@@ -36,7 +37,7 @@ impl Host {
|
||||
kind,
|
||||
bindings: BTreeMap::new(),
|
||||
addresses: BTreeSet::new(),
|
||||
primary: None,
|
||||
hostname_info: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +54,9 @@ pub enum HostKind {
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
pub struct HostInfo(BTreeMap<HostId, Host>);
|
||||
pub struct Hosts(pub BTreeMap<HostId, Host>);
|
||||
|
||||
impl Map for HostInfo {
|
||||
impl Map for Hosts {
|
||||
type Key = HostId;
|
||||
type Value = Host;
|
||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
||||
@@ -75,7 +76,7 @@ pub fn host_for<'a>(
|
||||
fn host_info<'a>(
|
||||
db: &'a mut DatabaseModel,
|
||||
package_id: &PackageId,
|
||||
) -> Result<&'a mut Model<HostInfo>, Error> {
|
||||
) -> Result<&'a mut Model<Hosts>, Error> {
|
||||
Ok::<_, Error>(
|
||||
db.as_public_mut()
|
||||
.as_package_data_mut()
|
||||
@@ -129,9 +130,3 @@ impl Model<Host> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl HostInfo {
|
||||
pub fn get_host_primary(&self, host_id: &HostId) -> Option<HostAddress> {
|
||||
self.0.get(&host_id).and_then(|h| h.primary.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use imbl::OrdMap;
|
||||
use lazy_format::lazy_format;
|
||||
use models::{HostId, OptionExt, PackageId};
|
||||
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
||||
use tracing::instrument;
|
||||
@@ -15,8 +14,9 @@ use crate::hostname::Hostname;
|
||||
use crate::net::dns::DnsController;
|
||||
use crate::net::forward::LanPortForwardController;
|
||||
use crate::net::host::address::HostAddress;
|
||||
use crate::net::host::binding::{AddSslOptions, BindOptions};
|
||||
use crate::net::host::binding::{AddSslOptions, BindOptions, LanInfo};
|
||||
use crate::net::host::{host_for, Host, HostKind};
|
||||
use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname};
|
||||
use crate::net::tor::TorController;
|
||||
use crate::net::vhost::{AlpnInfo, VHostController};
|
||||
use crate::prelude::*;
|
||||
@@ -164,7 +164,7 @@ impl NetController {
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct HostBinds {
|
||||
lan: BTreeMap<u16, (u16, Option<AddSslOptions>, Arc<()>)>,
|
||||
lan: BTreeMap<u16, (LanInfo, Option<AddSslOptions>, Vec<Arc<()>>)>,
|
||||
tor: BTreeMap<OnionAddressV3, (OrdMap<u16, SocketAddr>, Vec<Arc<()>>)>,
|
||||
}
|
||||
|
||||
@@ -209,105 +209,173 @@ impl NetService {
|
||||
.await?;
|
||||
self.update(id, host).await
|
||||
}
|
||||
pub async fn clear_bindings(&mut self) -> Result<(), Error> {
|
||||
// TODO BLUJ
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> {
|
||||
dbg!(&host);
|
||||
dbg!(&self.binds);
|
||||
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()
|
||||
};
|
||||
if true
|
||||
// TODO: if should listen lan
|
||||
{
|
||||
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.assigned_lan_port.as_ref() == Some(external)
|
||||
}); // only keep existing binding if relevant details match
|
||||
if let Some(external) = bind.assigned_lan_port {
|
||||
let new_lan_bind = if let Some(b) = lan_bind {
|
||||
b
|
||||
} else {
|
||||
if let Some(ssl) = &bind.options.add_ssl {
|
||||
let rc = ctrl
|
||||
.vhost
|
||||
let peek = ctrl.db.peek().await;
|
||||
|
||||
// LAN
|
||||
let server_info = peek.as_public().as_server_info();
|
||||
let ip_info = server_info.as_ip_info().de()?;
|
||||
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
|
||||
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);
|
||||
if let Some(ssl) = &bind.options.add_ssl {
|
||||
let external = bind
|
||||
.lan
|
||||
.assigned_ssl_port
|
||||
.or_not_found("assigned ssl port")?;
|
||||
rcs.push(
|
||||
ctrl.vhost
|
||||
.add(
|
||||
None,
|
||||
external,
|
||||
(self.ip, *port).into(),
|
||||
if bind.options.secure.as_ref().map_or(false, |s| s.ssl) {
|
||||
Ok(())
|
||||
if let Some(alpn) = ssl.alpn.clone() {
|
||||
Err(alpn)
|
||||
} else {
|
||||
Err(ssl.alpn.clone())
|
||||
if bind.options.secure.as_ref().map_or(false, |s| s.ssl) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AlpnInfo::Reflect)
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
(*port, Some(ssl.clone()), rc)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
if let Some(security) = bind.options.secure {
|
||||
if bind.options.add_ssl.is_some() && security.ssl {
|
||||
// doesn't make sense to have 2 listening ports, both with ssl
|
||||
} else {
|
||||
let rc = ctrl.forward.add(external, (self.ip, *port).into()).await?;
|
||||
(*port, None, rc)
|
||||
let external =
|
||||
bind.lan.assigned_port.or_not_found("assigned lan port")?;
|
||||
rcs.push(ctrl.forward.add(external, (self.ip, *port).into()).await?);
|
||||
}
|
||||
};
|
||||
binds.lan.insert(*port, new_lan_bind);
|
||||
}
|
||||
(bind.lan, bind.options.add_ssl.clone(), rcs)
|
||||
};
|
||||
let mut bind_hostname_info: Vec<HostnameInfo> =
|
||||
hostname_info.remove(port).unwrap_or_default();
|
||||
for (interface, ip_info) in &ip_info {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
network_interface_id: interface.clone(),
|
||||
public: false,
|
||||
hostname: IpHostname::Local {
|
||||
value: format!("{hostname}.local"),
|
||||
port: new_lan_bind.0.assigned_port,
|
||||
ssl_port: new_lan_bind.0.assigned_ssl_port,
|
||||
},
|
||||
});
|
||||
if let Some(ipv4) = ip_info.ipv4 {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
network_interface_id: interface.clone(),
|
||||
public: false,
|
||||
hostname: IpHostname::Ipv4 {
|
||||
value: ipv4,
|
||||
port: new_lan_bind.0.assigned_port,
|
||||
ssl_port: new_lan_bind.0.assigned_ssl_port,
|
||||
},
|
||||
});
|
||||
}
|
||||
if let Some(ipv6) = ip_info.ipv6 {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
network_interface_id: interface.clone(),
|
||||
public: false,
|
||||
hostname: IpHostname::Ipv6 {
|
||||
value: ipv6,
|
||||
port: new_lan_bind.0.assigned_port,
|
||||
ssl_port: new_lan_bind.0.assigned_ssl_port,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(external) = old_lan_port {
|
||||
hostname_info.insert(*port, bind_hostname_info);
|
||||
binds.lan.insert(*port, new_lan_bind);
|
||||
}
|
||||
if let Some(lan) = old_lan_port {
|
||||
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?;
|
||||
}
|
||||
}
|
||||
let mut removed = BTreeSet::new();
|
||||
let mut removed_ssl = BTreeSet::new();
|
||||
binds.lan.retain(|internal, (external, ssl, _)| {
|
||||
if host.bindings.contains_key(internal) {
|
||||
true
|
||||
} else {
|
||||
if ssl.is_some() {
|
||||
removed_ssl.insert(*external);
|
||||
} else {
|
||||
removed.insert(*external);
|
||||
}
|
||||
false
|
||||
}
|
||||
});
|
||||
for external in removed {
|
||||
ctrl.forward.gc(external).await?;
|
||||
}
|
||||
let mut removed = BTreeSet::new();
|
||||
binds.lan.retain(|internal, (external, _, _)| {
|
||||
if host.bindings.contains_key(internal) {
|
||||
true
|
||||
} else {
|
||||
removed.insert(*external);
|
||||
|
||||
false
|
||||
}
|
||||
for external in removed_ssl {
|
||||
});
|
||||
for lan in removed {
|
||||
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?;
|
||||
}
|
||||
}
|
||||
let tor_binds: OrdMap<u16, SocketAddr> = host
|
||||
.bindings
|
||||
.iter()
|
||||
.flat_map(|(internal, info)| {
|
||||
let non_ssl = (
|
||||
info.options.preferred_external_port,
|
||||
SocketAddr::from((self.ip, *internal)),
|
||||
|
||||
struct TorHostnamePorts {
|
||||
non_ssl: Option<u16>,
|
||||
ssl: Option<u16>,
|
||||
}
|
||||
let mut tor_hostname_ports = BTreeMap::<u16, TorHostnamePorts>::new();
|
||||
let mut tor_binds = OrdMap::<u16, SocketAddr>::new();
|
||||
for (internal, info) in &host.bindings {
|
||||
tor_binds.insert(
|
||||
info.options.preferred_external_port,
|
||||
SocketAddr::from((self.ip, *internal)),
|
||||
);
|
||||
if let (Some(ssl), Some(ssl_internal)) =
|
||||
(&info.options.add_ssl, info.lan.assigned_ssl_port)
|
||||
{
|
||||
tor_binds.insert(
|
||||
ssl.preferred_external_port,
|
||||
SocketAddr::from(([127, 0, 0, 1], ssl_internal)),
|
||||
);
|
||||
if let (Some(ssl), Some(ssl_internal)) =
|
||||
(&info.options.add_ssl, info.assigned_lan_port)
|
||||
{
|
||||
itertools::Either::Left(
|
||||
[
|
||||
(
|
||||
ssl.preferred_external_port,
|
||||
SocketAddr::from(([127, 0, 0, 1], ssl_internal)),
|
||||
),
|
||||
non_ssl,
|
||||
]
|
||||
.into_iter(),
|
||||
)
|
||||
} else {
|
||||
itertools::Either::Right([non_ssl].into_iter())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
tor_hostname_ports.insert(
|
||||
*internal,
|
||||
TorHostnamePorts {
|
||||
non_ssl: Some(info.options.preferred_external_port)
|
||||
.filter(|p| *p != ssl.preferred_external_port),
|
||||
ssl: Some(ssl.preferred_external_port),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
tor_hostname_ports.insert(
|
||||
*internal,
|
||||
TorHostnamePorts {
|
||||
non_ssl: Some(info.options.preferred_external_port),
|
||||
ssl: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut keep_tor_addrs = BTreeSet::new();
|
||||
for addr in match host.kind {
|
||||
HostKind::Multi => {
|
||||
@@ -324,13 +392,10 @@ impl NetService {
|
||||
let new_tor_bind = if let Some(tor_bind) = tor_bind {
|
||||
tor_bind
|
||||
} else {
|
||||
let key = ctrl
|
||||
.db
|
||||
.peek()
|
||||
.await
|
||||
.into_private()
|
||||
.into_key_store()
|
||||
.into_onion()
|
||||
let key = peek
|
||||
.as_private()
|
||||
.as_key_store()
|
||||
.as_onion()
|
||||
.get_key(address)?;
|
||||
let rcs = ctrl
|
||||
.tor
|
||||
@@ -338,6 +403,18 @@ impl NetService {
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -347,6 +424,14 @@ impl NetService {
|
||||
ctrl.tor.gc(Some(addr.clone()), None).await?;
|
||||
}
|
||||
}
|
||||
self.net_controller()?
|
||||
.db
|
||||
.mutate(|db| {
|
||||
host_for(db, &self.id, &id, host.kind)?
|
||||
.as_hostname_info_mut()
|
||||
.ser(&hostname_info)
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -355,12 +440,13 @@ impl NetService {
|
||||
let mut errors = ErrorCollection::new();
|
||||
if let Some(ctrl) = Weak::upgrade(&self.controller) {
|
||||
for (_, binds) in std::mem::take(&mut self.binds) {
|
||||
for (_, (external, ssl, rc)) in binds.lan {
|
||||
for (_, (lan, _, rc)) in binds.lan {
|
||||
drop(rc);
|
||||
if ssl.is_some() {
|
||||
errors.handle(ctrl.vhost.gc(None, external).await);
|
||||
} else {
|
||||
errors.handle(ctrl.forward.gc(external).await);
|
||||
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 {
|
||||
@@ -384,12 +470,12 @@ impl NetService {
|
||||
self.ip
|
||||
}
|
||||
|
||||
pub fn get_ext_port(&self, host_id: HostId, internal_port: u16) -> Result<u16, Error> {
|
||||
pub fn get_ext_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(ext_port_info) = binds.lan.get(&internal_port) {
|
||||
Ok(ext_port_info.0)
|
||||
if let Some((lan, _, _)) = binds.lan.get(&internal_port) {
|
||||
Ok(*lan)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!(
|
||||
|
||||
@@ -1,51 +1,32 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use models::{HostId, ServiceInterfaceId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::net::host::binding::BindOptions;
|
||||
use crate::net::host::HostKind;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServiceInterfaceWithHostInfo {
|
||||
#[serde(flatten)]
|
||||
pub service_interface: ServiceInterface,
|
||||
pub host_info: ExportedHostInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExportedHostInfo {
|
||||
pub id: HostId,
|
||||
pub kind: HostKind,
|
||||
pub hostnames: Vec<ExportedHostnameInfo>,
|
||||
}
|
||||
use crate::net::host::address::HostAddress;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all_fields = "camelCase")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum ExportedHostnameInfo {
|
||||
pub enum HostnameInfo {
|
||||
Ip {
|
||||
network_interface_id: String,
|
||||
public: bool,
|
||||
hostname: ExportedIpHostname,
|
||||
hostname: IpHostname,
|
||||
},
|
||||
Onion {
|
||||
hostname: ExportedOnionHostname,
|
||||
hostname: OnionHostname,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExportedOnionHostname {
|
||||
pub struct OnionHostname {
|
||||
pub value: String,
|
||||
pub port: Option<u16>,
|
||||
pub ssl_port: Option<u16>,
|
||||
@@ -56,7 +37,7 @@ pub struct ExportedOnionHostname {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all_fields = "camelCase")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum ExportedIpHostname {
|
||||
pub enum IpHostname {
|
||||
Ipv4 {
|
||||
value: Ipv4Addr,
|
||||
port: Option<u16>,
|
||||
@@ -110,6 +91,10 @@ pub enum ServiceInterfaceType {
|
||||
pub struct AddressInfo {
|
||||
pub username: Option<String>,
|
||||
pub host_id: HostId,
|
||||
pub bind_options: BindOptions,
|
||||
pub internal_port: u16,
|
||||
#[ts(type = "string | null")]
|
||||
pub scheme: Option<InternedString>,
|
||||
#[ts(type = "string | null")]
|
||||
pub ssl_scheme: Option<InternedString>,
|
||||
pub suffix: String,
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl VHostServer {
|
||||
.into_entries()?
|
||||
.into_iter()
|
||||
.flat_map(|(_, ips)| [
|
||||
ips.as_ipv4().de().map(|ip| ip.map(IpAddr::V4)),
|
||||
ips.as_ipv4().de().map(|ip| ip.map(IpAddr::V4)),
|
||||
ips.as_ipv6().de().map(|ip| ip.map(IpAddr::V6))
|
||||
])
|
||||
.filter_map(|a| a.transpose())
|
||||
|
||||
@@ -138,7 +138,6 @@ impl Middleware<RegistryContext> for Auth {
|
||||
if request.headers().contains_key(AUTH_SIG_HEADER) {
|
||||
self.signer = Some(
|
||||
async {
|
||||
let request = request;
|
||||
let SignatureHeader {
|
||||
commitment,
|
||||
signer,
|
||||
|
||||
@@ -10,7 +10,8 @@ use persistent_container::PersistentContainer;
|
||||
use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, HandlerArgs, HandlerFor};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use start_stop::StartStop;
|
||||
use tokio::{fs::File, sync::Notify};
|
||||
use tokio::fs::File;
|
||||
use tokio::sync::Notify;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
@@ -308,7 +309,7 @@ impl Service {
|
||||
.send(transition::restore::Restore {
|
||||
path: backup_source.path().to_path_buf(),
|
||||
})
|
||||
.await?;
|
||||
.await??;
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
@@ -370,7 +371,7 @@ impl Service {
|
||||
.send(transition::backup::Backup {
|
||||
path: guard.path().to_path_buf(),
|
||||
})
|
||||
.await?;
|
||||
.await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use imbl_value::InternedString;
|
||||
use models::{ProcedureName, VolumeId};
|
||||
use rpc_toolkit::{Empty, Server, ShutdownHandle};
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::fs::{ File};
|
||||
use tokio::fs::File;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::{oneshot, watch, Mutex, OnceCell};
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::sync::{Arc, Weak};
|
||||
use clap::builder::ValueParserFactory;
|
||||
use clap::Parser;
|
||||
use emver::VersionRange;
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::{json, InternedString};
|
||||
use itertools::Itertools;
|
||||
use models::{
|
||||
@@ -30,12 +29,9 @@ use crate::disk::mount::filesystem::idmapped::IdMapped;
|
||||
use crate::disk::mount::filesystem::loop_dev::LoopDev;
|
||||
use crate::disk::mount::filesystem::overlayfs::OverlayGuard;
|
||||
use crate::net::host::address::HostAddress;
|
||||
use crate::net::host::binding::BindOptions;
|
||||
use crate::net::host::{self, HostKind};
|
||||
use crate::net::service_interface::{
|
||||
AddressInfo, ExportedHostInfo, ExportedHostnameInfo, ServiceInterface, ServiceInterfaceType,
|
||||
ServiceInterfaceWithHostInfo,
|
||||
};
|
||||
use crate::net::host::binding::{BindOptions, LanInfo};
|
||||
use crate::net::host::{Host, HostKind};
|
||||
use crate::net::service_interface::{AddressInfo, ServiceInterface, ServiceInterfaceType};
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::merkle_archive::source::http::HttpSource;
|
||||
use crate::s9pk::rpc::SKIP_ENV;
|
||||
@@ -193,7 +189,6 @@ pub fn service_effect_handler<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand("removeAddress", from_fn_async(remove_address).no_cli())
|
||||
.subcommand("exportAction", from_fn_async(export_action).no_cli())
|
||||
.subcommand("removeAction", from_fn_async(remove_action).no_cli())
|
||||
.subcommand("reverseProxy", from_fn_async(reverse_proxy).no_cli())
|
||||
.subcommand("mount", from_fn_async(mount).no_cli())
|
||||
|
||||
// TODO Callbacks
|
||||
@@ -233,8 +228,6 @@ struct ExportServiceInterfaceParams {
|
||||
masked: bool,
|
||||
address_info: AddressInfo,
|
||||
r#type: ServiceInterfaceType,
|
||||
host_kind: HostKind,
|
||||
hostnames: Vec<ExportedHostnameInfo>,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
@@ -242,9 +235,8 @@ struct ExportServiceInterfaceParams {
|
||||
struct GetPrimaryUrlParams {
|
||||
#[ts(type = "string | null")]
|
||||
package_id: Option<PackageId>,
|
||||
service_interface_id: String,
|
||||
service_interface_id: ServiceInterfaceId,
|
||||
callback: Callback,
|
||||
host_id: HostId,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
@@ -276,37 +268,7 @@ struct RemoveActionParams {
|
||||
#[ts(type = "string")]
|
||||
id: ActionId,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ReverseProxyBind {
|
||||
ip: Option<String>,
|
||||
port: u32,
|
||||
ssl: bool,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ReverseProxyDestination {
|
||||
ip: Option<String>,
|
||||
port: u32,
|
||||
ssl: bool,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ReverseProxyHttp {
|
||||
#[ts(type = "null | {[key: string]: string}")]
|
||||
headers: Option<OrdMap<String, String>>,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ReverseProxyParams {
|
||||
bind: ReverseProxyBind,
|
||||
dst: ReverseProxyDestination,
|
||||
http: ReverseProxyHttp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -367,7 +329,7 @@ async fn get_container_ip(context: EffectContext, _: Empty) -> Result<Ipv4Addr,
|
||||
async fn get_service_port_forward(
|
||||
context: EffectContext,
|
||||
data: GetServicePortForwardParams,
|
||||
) -> Result<u16, Error> {
|
||||
) -> Result<LanInfo, Error> {
|
||||
let internal_port = data.internal_port as u16;
|
||||
|
||||
let context = context.deref()?;
|
||||
@@ -404,13 +366,10 @@ async fn export_service_interface(
|
||||
masked,
|
||||
address_info,
|
||||
r#type,
|
||||
host_kind,
|
||||
hostnames,
|
||||
}: ExportServiceInterfaceParams,
|
||||
) -> Result<(), Error> {
|
||||
let context = context.deref()?;
|
||||
let package_id = context.id.clone();
|
||||
let host_id = address_info.host_id.clone();
|
||||
|
||||
let service_interface = ServiceInterface {
|
||||
id: id.clone(),
|
||||
@@ -422,15 +381,7 @@ async fn export_service_interface(
|
||||
address_info,
|
||||
interface_type: r#type,
|
||||
};
|
||||
let host_info = ExportedHostInfo {
|
||||
id: host_id,
|
||||
kind: host_kind,
|
||||
hostnames,
|
||||
};
|
||||
let svc_interface_with_host_info = ServiceInterfaceWithHostInfo {
|
||||
service_interface,
|
||||
host_info,
|
||||
};
|
||||
let svc_interface_with_host_info = service_interface;
|
||||
|
||||
context
|
||||
.ctx
|
||||
@@ -449,35 +400,26 @@ async fn export_service_interface(
|
||||
}
|
||||
async fn get_primary_url(
|
||||
context: EffectContext,
|
||||
data: GetPrimaryUrlParams,
|
||||
) -> Result<HostAddress, Error> {
|
||||
GetPrimaryUrlParams {
|
||||
package_id,
|
||||
service_interface_id,
|
||||
callback,
|
||||
}: GetPrimaryUrlParams,
|
||||
) -> Result<Option<HostAddress>, Error> {
|
||||
let context = context.deref()?;
|
||||
let package_id = context.id.clone();
|
||||
let package_id = package_id.unwrap_or_else(|| context.id.clone());
|
||||
|
||||
let db_model = context.ctx.db.peek().await;
|
||||
|
||||
let pkg_data_model = db_model
|
||||
.as_public()
|
||||
.as_package_data()
|
||||
.as_idx(&package_id)
|
||||
.or_not_found(&package_id)?;
|
||||
|
||||
let host = pkg_data_model.de()?.hosts.get_host_primary(&data.host_id);
|
||||
|
||||
match host {
|
||||
Some(host_address) => Ok(host_address),
|
||||
None => Err(Error::new(
|
||||
eyre!("Primary Url not found for {}", data.host_id),
|
||||
crate::ErrorKind::NotFound,
|
||||
)),
|
||||
}
|
||||
Ok(None) // TODO
|
||||
}
|
||||
async fn list_service_interfaces(
|
||||
context: EffectContext,
|
||||
data: ListServiceInterfacesParams,
|
||||
) -> Result<BTreeMap<ServiceInterfaceId, ServiceInterfaceWithHostInfo>, Error> {
|
||||
ListServiceInterfacesParams {
|
||||
package_id,
|
||||
callback,
|
||||
}: ListServiceInterfacesParams,
|
||||
) -> Result<BTreeMap<ServiceInterfaceId, ServiceInterface>, Error> {
|
||||
let context = context.deref()?;
|
||||
let package_id = context.id.clone();
|
||||
let package_id = package_id.unwrap_or_else(|| context.id.clone());
|
||||
|
||||
context
|
||||
.ctx
|
||||
@@ -553,10 +495,8 @@ async fn remove_action(context: EffectContext, data: RemoveActionParams) -> Resu
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn reverse_proxy(context: EffectContext, data: ReverseProxyParams) -> Result<Value, Error> {
|
||||
todo!()
|
||||
}
|
||||
async fn mount(context: EffectContext, data: MountParams) -> Result<Value, Error> {
|
||||
// TODO
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -564,49 +504,42 @@ async fn mount(context: EffectContext, data: MountParams) -> Result<Value, Error
|
||||
#[ts(export)]
|
||||
struct Callback(#[ts(type = "() => void")] i64);
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
enum GetHostInfoParamsKind {
|
||||
Multi,
|
||||
}
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
struct GetHostInfoParams {
|
||||
kind: Option<GetHostInfoParamsKind>,
|
||||
service_interface_id: String,
|
||||
host_id: HostId,
|
||||
#[ts(type = "string | null")]
|
||||
package_id: Option<PackageId>,
|
||||
callback: Callback,
|
||||
}
|
||||
async fn get_host_info(
|
||||
ctx: EffectContext,
|
||||
GetHostInfoParams { .. }: GetHostInfoParams,
|
||||
) -> Result<Value, Error> {
|
||||
GetHostInfoParams {
|
||||
callback,
|
||||
package_id,
|
||||
host_id,
|
||||
}: GetHostInfoParams,
|
||||
) -> Result<Host, Error> {
|
||||
let ctx = ctx.deref()?;
|
||||
Ok(json!({
|
||||
"id": "fakeId1",
|
||||
"kind": "multi",
|
||||
"hostnames": [{
|
||||
"kind": "ip",
|
||||
"networkInterfaceId": "fakeNetworkInterfaceId1",
|
||||
"public": true,
|
||||
"hostname":{
|
||||
"kind": "domain",
|
||||
"domain": format!("{}", ctx.id),
|
||||
"subdomain": (),
|
||||
"port": (),
|
||||
"sslPort": ()
|
||||
}
|
||||
}
|
||||
let db = ctx.ctx.db.peek().await;
|
||||
let package_id = package_id.unwrap_or_else(|| ctx.id.clone());
|
||||
|
||||
]
|
||||
}))
|
||||
db.as_public()
|
||||
.as_package_data()
|
||||
.as_idx(&package_id)
|
||||
.or_not_found(&package_id)?
|
||||
.as_hosts()
|
||||
.as_idx(&host_id)
|
||||
.or_not_found(&host_id)?
|
||||
.de()
|
||||
}
|
||||
|
||||
async fn clear_bindings(context: EffectContext, _: Empty) -> Result<Value, Error> {
|
||||
todo!()
|
||||
async fn clear_bindings(context: EffectContext, _: Empty) -> Result<(), Error> {
|
||||
let ctx = context.deref()?;
|
||||
let mut svc = ctx.persistent_container.net_service.lock().await;
|
||||
svc.clear_bindings().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
@@ -619,15 +552,13 @@ struct BindParams {
|
||||
#[serde(flatten)]
|
||||
options: BindOptions,
|
||||
}
|
||||
async fn bind(
|
||||
context: EffectContext,
|
||||
BindParams {
|
||||
async fn bind(context: EffectContext, bind_params: Value) -> Result<(), Error> {
|
||||
let BindParams {
|
||||
kind,
|
||||
id,
|
||||
internal_port,
|
||||
options,
|
||||
}: BindParams,
|
||||
) -> Result<(), Error> {
|
||||
} = from_value(bind_params)?;
|
||||
let ctx = context.deref()?;
|
||||
let mut svc = ctx.persistent_container.net_service.lock().await;
|
||||
svc.bind(kind, id, internal_port, options).await
|
||||
@@ -639,39 +570,32 @@ async fn bind(
|
||||
struct GetServiceInterfaceParams {
|
||||
#[ts(type = "string | null")]
|
||||
package_id: Option<PackageId>,
|
||||
service_interface_id: String,
|
||||
service_interface_id: ServiceInterfaceId,
|
||||
callback: Callback,
|
||||
}
|
||||
|
||||
async fn get_service_interface(
|
||||
_: EffectContext,
|
||||
ctx: EffectContext,
|
||||
GetServiceInterfaceParams {
|
||||
callback,
|
||||
package_id,
|
||||
service_interface_id,
|
||||
}: GetServiceInterfaceParams,
|
||||
) -> Result<Value, Error> {
|
||||
// TODO @Dr_Bonez
|
||||
Ok(json!({
|
||||
"id": service_interface_id,
|
||||
"name": service_interface_id,
|
||||
"description": "This is a fake",
|
||||
"hasPrimary": true,
|
||||
"disabled": false,
|
||||
"masked": false,
|
||||
"addressInfo": json!({
|
||||
"username": Value::Null,
|
||||
"hostId": "HostId?",
|
||||
"options": json!({
|
||||
"scheme": Value::Null,
|
||||
"preferredExternalPort": 80,
|
||||
"addSsl":Value::Null,
|
||||
"secure": false,
|
||||
"ssl": false
|
||||
}),
|
||||
"suffix": "http"
|
||||
}),
|
||||
"type": "api"
|
||||
}))
|
||||
) -> Result<ServiceInterface, Error> {
|
||||
let ctx = ctx.deref()?;
|
||||
let package_id = package_id.unwrap_or_else(|| ctx.id.clone());
|
||||
let db = ctx.ctx.db.peek().await;
|
||||
|
||||
let interface = db
|
||||
.as_public()
|
||||
.as_package_data()
|
||||
.as_idx(&package_id)
|
||||
.or_not_found(&package_id)?
|
||||
.as_service_interfaces()
|
||||
.as_idx(&service_interface_id)
|
||||
.or_not_found(&service_interface_id)?
|
||||
.de()?;
|
||||
Ok(interface)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Parser, TS)]
|
||||
@@ -764,6 +688,7 @@ async fn get_ssl_certificate(
|
||||
host_id,
|
||||
}: GetSslCertificateParams,
|
||||
) -> Result<Value, Error> {
|
||||
// TODO
|
||||
let fake = include_str!("./fake.cert.pem");
|
||||
Ok(json!([fake, fake, fake]))
|
||||
}
|
||||
@@ -785,6 +710,7 @@ async fn get_ssl_key(
|
||||
algorithm,
|
||||
}: GetSslKeyParams,
|
||||
) -> Result<Value, Error> {
|
||||
// TODO
|
||||
let fake = include_str!("./fake.cert.key");
|
||||
Ok(json!(fake))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user