mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Feature/start tunnel (#3037)
* fix live-build resolv.conf * improved debuggability * wip: start-tunnel * fixes for trixie and tor * non-free-firmware on trixie * wip * web server WIP * wip: tls refactor * FE patchdb, mocks, and most endpoints * fix editing records and patch mocks * refactor complete * finish api * build and formatter update * minor change toi viewing addresses and fix build * fixes * more providers * endpoint for getting config * fix tests * api fixes * wip: separate port forward controller into parts * simplify iptables rules * bump sdk * misc fixes * predict next subnet and ip, use wan ips, and form validation * refactor: break big components apart and address todos (#3043) * refactor: break big components apart and address todos * starttunnel readme, fix pf mocks, fix adding tor domain in startos --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * better tui * tui tweaks * fix: address comments * better regex for subnet * fixes * better validation * handle rpc errors * build fixes * fix: address comments (#3044) * fix: address comments * fix unread notification mocks * fix row click for notification --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * fix raspi build * fix build * fix build * fix build * fix build * try to fix build * fix tests * fix tests * fix rsync tests * delete useless effectful test --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> Co-authored-by: Alex Inkin <alexander@inkin.ru>
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::ws;
|
||||
use clap::Parser;
|
||||
use imbl::{HashMap, OrdMap};
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::Ipv4Net;
|
||||
use itertools::Itertools;
|
||||
use models::GatewayId;
|
||||
use patch_db::Dump;
|
||||
use patch_db::json_ptr::{JsonPointer, ROOT};
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
@@ -16,21 +19,48 @@ use ts_rs::TS;
|
||||
|
||||
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};
|
||||
|
||||
#[derive(Default, Deserialize, Serialize, HasModel)]
|
||||
#[derive(Default, Deserialize, Serialize, HasModel, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct TunnelDatabase {
|
||||
pub webserver: WebserverInfo,
|
||||
pub sessions: Sessions,
|
||||
pub password: String,
|
||||
pub auth_pubkeys: HashSet<AnyVerifyingKey>,
|
||||
pub password: Option<String>,
|
||||
#[ts(as = "std::collections::HashMap::<AnyVerifyingKey, SignerInfo>")]
|
||||
pub auth_pubkeys: HashMap<AnyVerifyingKey, SignerInfo>,
|
||||
#[ts(as = "std::collections::BTreeMap::<AnyVerifyingKey, SignerInfo>")]
|
||||
pub gateways: OrdMap<GatewayId, NetworkInterfaceInfo>,
|
||||
pub wg: WgServer,
|
||||
pub port_forwards: BTreeMap<SocketAddrV4, SocketAddrV4>,
|
||||
pub port_forwards: PortForwards,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_bindings_tunnel_db() {
|
||||
TunnelDatabase::export_all_to("bindings/tunnel").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
pub struct PortForwards(pub BTreeMap<SocketAddrV4, SocketAddrV4>);
|
||||
impl Map for PortForwards {
|
||||
type Key = SocketAddrV4;
|
||||
type Value = SocketAddrV4;
|
||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
||||
Self::key_string(key)
|
||||
}
|
||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
||||
Ok(InternedString::from_display(key))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_api<C: Context>() -> ParentHandler<C> {
|
||||
@@ -47,6 +77,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)
|
||||
@@ -195,3 +231,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