mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
api fixes
This commit is contained in:
@@ -46,7 +46,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> {
|
||||
let https_db = ctx.db.clone();
|
||||
let https_thread: NonDetachingJoinHandle<()> = tokio::spawn(async move {
|
||||
let mut sub = https_db.subscribe("/webserver".parse().unwrap()).await;
|
||||
while sub.recv().await.is_some() {
|
||||
while {
|
||||
while let Err(e) = async {
|
||||
let webserver = https_db.peek().await.into_webserver();
|
||||
if webserver.as_enabled().de()? {
|
||||
@@ -96,7 +96,8 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> {
|
||||
tracing::debug!("{e:?}");
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
sub.recv().await.is_some()
|
||||
} {}
|
||||
})
|
||||
.into();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::net::gateway::{IdFilter, InterfaceFilter};
|
||||
use crate::prelude::*;
|
||||
use crate::tunnel::context::TunnelContext;
|
||||
use crate::tunnel::db::GatewayPort;
|
||||
use crate::tunnel::wg::{ClientConfig, WgConfig, WgSubnetClients, WgSubnetConfig};
|
||||
use crate::tunnel::wg::{WgConfig, WgSubnetClients, WgSubnetConfig};
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
|
||||
pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
|
||||
@@ -30,6 +30,10 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
|
||||
"subnet",
|
||||
subnet_api::<C>().with_about("Add, remove, or modify subnets"),
|
||||
)
|
||||
.subcommand(
|
||||
"device",
|
||||
device_api::<C>().with_about("Add, remove, or list devices in subnets"),
|
||||
)
|
||||
.subcommand(
|
||||
"port-forward",
|
||||
ParentHandler::<C>::new()
|
||||
@@ -78,28 +82,29 @@ pub fn subnet_api<C: Context>() -> ParentHandler<C, SubnetParams> {
|
||||
.with_about("Remove a subnet")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn device_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"add-device",
|
||||
"add",
|
||||
from_fn_async(add_device)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.with_inherited(|a, _| a)
|
||||
.no_display()
|
||||
.with_about("Add a device to a subnet")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"remove-device",
|
||||
"remove",
|
||||
from_fn_async(remove_device)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.with_inherited(|a, _| a)
|
||||
.no_display()
|
||||
.with_about("Remove a device from a subnet")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"list-devices",
|
||||
"list",
|
||||
from_fn_async(list_devices)
|
||||
.with_inherited(|a, _| a)
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||
use prettytable::*;
|
||||
@@ -124,18 +129,7 @@ pub fn subnet_api<C: Context>() -> ParentHandler<C, SubnetParams> {
|
||||
.subcommand(
|
||||
"show-config",
|
||||
from_fn_async(show_config)
|
||||
.with_inherited(|a, _| a)
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||
if let Some(format) = params.format {
|
||||
return display_serializable(format, res);
|
||||
}
|
||||
|
||||
println!("{}", res);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_about("Show the WireGuard configuration for a subnet")
|
||||
.with_about("Show the WireGuard configuration for a device")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
}
|
||||
@@ -195,14 +189,14 @@ pub async fn remove_subnet(
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddDeviceParams {
|
||||
subnet: Ipv4Net,
|
||||
name: InternedString,
|
||||
ip: Option<Ipv4Addr>,
|
||||
}
|
||||
|
||||
pub async fn add_device(
|
||||
ctx: TunnelContext,
|
||||
AddDeviceParams { name, ip }: AddDeviceParams,
|
||||
SubnetParams { subnet }: SubnetParams,
|
||||
AddDeviceParams { subnet, name, ip }: AddDeviceParams,
|
||||
) -> Result<(), Error> {
|
||||
let config = WgConfig::generate(name);
|
||||
let server = ctx
|
||||
@@ -254,13 +248,13 @@ pub async fn add_device(
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveDeviceParams {
|
||||
subnet: Ipv4Net,
|
||||
device: Ipv4Addr,
|
||||
}
|
||||
|
||||
pub async fn remove_device(
|
||||
ctx: TunnelContext,
|
||||
RemoveDeviceParams { device }: RemoveDeviceParams,
|
||||
SubnetParams { subnet }: SubnetParams,
|
||||
RemoveDeviceParams { subnet, device }: RemoveDeviceParams,
|
||||
) -> Result<(), Error> {
|
||||
let server = ctx
|
||||
.db
|
||||
@@ -279,10 +273,15 @@ pub async fn remove_device(
|
||||
server.sync().await
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ListDevicesParams {
|
||||
subnet: Ipv4Net,
|
||||
}
|
||||
|
||||
pub async fn list_devices(
|
||||
ctx: TunnelContext,
|
||||
_: Empty,
|
||||
SubnetParams { subnet }: SubnetParams,
|
||||
ListDevicesParams { subnet }: ListDevicesParams,
|
||||
) -> Result<WgSubnetConfig, Error> {
|
||||
ctx.db
|
||||
.peek()
|
||||
@@ -297,7 +296,8 @@ pub async fn list_devices(
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ShowConfigParams {
|
||||
device: Ipv4Addr,
|
||||
subnet: Ipv4Net,
|
||||
ip: Ipv4Addr,
|
||||
wan_addr: Option<IpAddr>,
|
||||
#[serde(rename = "__ConnectInfo_local_addr")]
|
||||
#[arg(skip)]
|
||||
@@ -307,12 +307,12 @@ pub struct ShowConfigParams {
|
||||
pub async fn show_config(
|
||||
ctx: TunnelContext,
|
||||
ShowConfigParams {
|
||||
device,
|
||||
subnet,
|
||||
ip,
|
||||
wan_addr,
|
||||
local_addr,
|
||||
}: ShowConfigParams,
|
||||
SubnetParams { subnet }: SubnetParams,
|
||||
) -> Result<ClientConfig, Error> {
|
||||
) -> Result<String, Error> {
|
||||
let peek = ctx.db.peek().await;
|
||||
let wg = peek.as_wg();
|
||||
let client = wg
|
||||
@@ -320,8 +320,8 @@ pub async fn show_config(
|
||||
.as_idx(&subnet)
|
||||
.or_not_found(&subnet)?
|
||||
.as_clients()
|
||||
.as_idx(&device)
|
||||
.or_not_found(&device)?
|
||||
.as_idx(&ip)
|
||||
.or_not_found(&ip)?
|
||||
.de()?;
|
||||
let wan_addr = if let Some(wan_addr) = wan_addr.or(local_addr.map(|a| a.ip())).filter(|ip| {
|
||||
!ip.is_loopback()
|
||||
@@ -348,11 +348,13 @@ pub async fn show_config(
|
||||
.or_not_found("a public IP address")?
|
||||
.addr()
|
||||
};
|
||||
Ok(client.client_config(
|
||||
device,
|
||||
wg.as_key().de()?.verifying_key(),
|
||||
(wan_addr, wg.as_port().de()?).into(),
|
||||
))
|
||||
Ok(client
|
||||
.client_config(
|
||||
ip,
|
||||
wg.as_key().de()?.verifying_key(),
|
||||
(wan_addr, wg.as_port().de()?).into(),
|
||||
)
|
||||
.to_string())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
|
||||
@@ -120,6 +120,20 @@ pub struct SignerInfo {
|
||||
|
||||
pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"login",
|
||||
from_fn_async(crate::auth::login_impl::<TunnelContext>)
|
||||
.with_metadata("login", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"logout",
|
||||
from_fn_async(crate::auth::logout::<TunnelContext>)
|
||||
.with_metadata("get_session", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Log out of current auth session")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand("set-password", from_fn_async(set_password_rpc).no_cli())
|
||||
.subcommand(
|
||||
"set-password",
|
||||
|
||||
@@ -23,7 +23,7 @@ use url::Url;
|
||||
use crate::auth::Sessions;
|
||||
use crate::context::config::ContextConfig;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::db::model::public::{NetworkInterfaceInfo, NetworkInterfaceType};
|
||||
use crate::else_empty_dir;
|
||||
use crate::middleware::auth::{Auth, AuthContext};
|
||||
use crate::middleware::cors::Cors;
|
||||
@@ -140,6 +140,11 @@ impl TunnelContext {
|
||||
|
||||
for iface in net_iface.peek(|i| {
|
||||
i.iter()
|
||||
.filter(|(_, info)| {
|
||||
info.ip_info.as_ref().map_or(false, |i| {
|
||||
i.device_type != Some(NetworkInterfaceType::Loopback)
|
||||
})
|
||||
})
|
||||
.map(|(name, _)| name)
|
||||
.filter(|id| id.as_str() != WIREGUARD_INTERFACE_NAME)
|
||||
.cloned()
|
||||
@@ -206,6 +211,12 @@ impl AsRef<RpcContinuations> for TunnelContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OpenAuthedContinuations<Option<InternedString>>> for TunnelContext {
|
||||
fn as_ref(&self) -> &OpenAuthedContinuations<Option<InternedString>> {
|
||||
&self.open_authed_continuations
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for TunnelContext {}
|
||||
impl Deref for TunnelContext {
|
||||
type Target = TunnelContextSeed;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::ws;
|
||||
use clap::Parser;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use imbl::{HashMap, OrdMap};
|
||||
@@ -20,11 +22,13 @@ use crate::auth::Sessions;
|
||||
use crate::context::CliContext;
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||
use crate::sign::AnyVerifyingKey;
|
||||
use crate::tunnel::auth::SignerInfo;
|
||||
use crate::tunnel::context::TunnelContext;
|
||||
use crate::tunnel::web::WebserverInfo;
|
||||
use crate::tunnel::wg::WgServer;
|
||||
use crate::util::net::WebSocketExt;
|
||||
use crate::util::serde::{HandlerExtSerde, apply_expr, deserialize_from_str, serialize_display};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@@ -112,6 +116,12 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
|
||||
.with_metadata("admin", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"subscribe",
|
||||
from_fn_async(subscribe)
|
||||
.with_metadata("get_session", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"apply",
|
||||
from_fn_async(cli_apply)
|
||||
@@ -260,3 +270,75 @@ pub async fn apply(ctx: TunnelContext, ApplyParams { expr, .. }: ApplyParams) ->
|
||||
.await
|
||||
.result
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubscribeParams {
|
||||
#[ts(type = "string | null")]
|
||||
pointer: Option<JsonPointer>,
|
||||
#[ts(skip)]
|
||||
#[serde(rename = "__Auth_session")]
|
||||
session: Option<InternedString>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubscribeRes {
|
||||
#[ts(type = "{ id: number; value: unknown }")]
|
||||
pub dump: Dump,
|
||||
pub guid: Guid,
|
||||
}
|
||||
|
||||
pub async fn subscribe(
|
||||
ctx: TunnelContext,
|
||||
SubscribeParams { pointer, session }: SubscribeParams,
|
||||
) -> Result<SubscribeRes, Error> {
|
||||
let (dump, mut sub) = ctx
|
||||
.db
|
||||
.dump_and_sub(pointer.unwrap_or_else(|| ROOT.to_owned()))
|
||||
.await;
|
||||
let guid = Guid::new();
|
||||
ctx.rpc_continuations
|
||||
.add(
|
||||
guid.clone(),
|
||||
RpcContinuation::ws_authed(
|
||||
&ctx,
|
||||
session,
|
||||
|mut ws| async move {
|
||||
if let Err(e) = async {
|
||||
loop {
|
||||
tokio::select! {
|
||||
rev = sub.recv() => {
|
||||
if let Some(rev) = rev {
|
||||
ws.send(ws::Message::Text(
|
||||
serde_json::to_string(&rev)
|
||||
.with_kind(ErrorKind::Serialization)?
|
||||
.into(),
|
||||
))
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
} else {
|
||||
return ws.normal_close("complete").await;
|
||||
}
|
||||
}
|
||||
msg = ws.recv() => {
|
||||
if msg.transpose().with_kind(ErrorKind::Network)?.is_none() {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.await
|
||||
{
|
||||
tracing::error!("Error in db websocket: {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
}
|
||||
},
|
||||
Duration::from_secs(30),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(SubscribeRes { dump, guid })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user