Skip to content

Commit

Permalink
add support for modifying hosts for a package
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-bonez committed Nov 15, 2024
1 parent 86a731d commit 645b856
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 7 deletions.
5 changes: 5 additions & 0 deletions core/models/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,11 @@ impl From<reqwest::Error> for Error {
Error::new(e, kind)
}
}
impl From<torut::onion::OnionAddressParseError> for Error {
fn from(e: torut::onion::OnionAddressParseError) -> Self {
Error::new(e, ErrorKind::Tor)
}
}
impl From<patch_db::value::Error> for Error {
fn from(value: patch_db::value::Error) -> Self {
match value.kind {
Expand Down
11 changes: 8 additions & 3 deletions core/startos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,23 +355,23 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::start)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Start a package container")
.with_about("Start a service")
.with_call_remote::<CliContext>(),
)
.subcommand(
"stop",
from_fn_async(control::stop)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Stop a package container")
.with_about("Stop a service")
.with_call_remote::<CliContext>(),
)
.subcommand(
"restart",
from_fn_async(control::restart)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Restart a package container")
.with_about("Restart a service")
.with_call_remote::<CliContext>(),
)
.subcommand(
Expand Down Expand Up @@ -409,9 +409,14 @@ pub fn package<C: Context>() -> ParentHandler<C> {
"attach",
from_fn_async(service::attach)
.with_metadata("get_session", Value::Bool(true))
.with_about("Execute commands within a service container")
.no_cli(),
)
.subcommand("attach", from_fn_async(service::cli_attach).no_display())
.subcommand(
"host",
net::host::host::<C>().with_about("Manage network hosts for a package"),
)
}

pub fn diagnostic_api() -> ParentHandler<DiagnosticContext> {
Expand Down
9 changes: 9 additions & 0 deletions core/startos/src/net/host/address.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::fmt;
use std::str::FromStr;

use clap::builder::ValueParserFactory;
use imbl_value::InternedString;
use models::FromStrParser;
use serde::{Deserialize, Serialize};
use torut::onion::OnionAddressV3;
use ts_rs::TS;
Expand Down Expand Up @@ -46,3 +48,10 @@ impl fmt::Display for HostAddress {
}
}
}

impl ValueParserFactory for HostAddress {
type Parser = FromStrParser<Self>;
fn value_parser() -> Self::Parser {
Self::Parser::new()
}
}
130 changes: 130 additions & 0 deletions core/startos/src/net/host/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::collections::{BTreeMap, BTreeSet};

use clap::Parser;
use imbl_value::InternedString;
use models::{HostId, PackageId};
use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler};
use serde::{Deserialize, Serialize};
use ts_rs::TS;

use crate::context::{CliContext, RpcContext};
use crate::db::model::DatabaseModel;
use crate::net::forward::AvailablePorts;
use crate::net::host::address::HostAddress;
Expand Down Expand Up @@ -134,3 +137,130 @@ impl Model<Host> {
})
}
}

#[derive(Deserialize, Serialize, Parser)]
pub struct HostParams {
package: PackageId,
host: HostId,
}

pub fn host<C: Context>() -> ParentHandler<C, HostParams> {
ParentHandler::<C, HostParams>::new().subcommand(
"address",
address::<C>().with_inherited(|params: HostParams, _| (params.package, params.host)),
)
}

pub fn address<C: Context>() -> ParentHandler<C, Empty, (PackageId, HostId)> {
ParentHandler::<C, Empty, (PackageId, HostId)>::new()
.subcommand(
"add",
from_fn_async(add_address)
.with_inherited(|_, inherited| inherited)
.with_about("Add an address to this host")
.no_display()
.with_call_remote::<CliContext>(),
)
.subcommand(
"remove",
from_fn_async(remove_address)
.with_inherited(|_, inherited| inherited)
.with_about("Remove an address from this host")
.no_display()
.with_call_remote::<CliContext>(),
)
.subcommand(
"list",
from_fn_async(list_addresses)
.with_inherited(|_, inherited| inherited)
.with_about("List addresses for this host")
.with_custom_display_fn(|_, res| {
for address in res {
println!("{address}")
}
Ok(())
})
.with_call_remote::<CliContext>(),
)
}

#[derive(Deserialize, Serialize, Parser)]
pub struct AddressParams {
pub address: HostAddress,
}

pub async fn add_address(
ctx: RpcContext,
AddressParams { address }: AddressParams,
(package, host): (PackageId, HostId),
) -> Result<(), Error> {
ctx.db
.mutate(|db| {
if let HostAddress::Onion { address } = address {
db.as_private()
.as_key_store()
.as_onion()
.get_key(&address)?;
}

db.as_public_mut()
.as_package_data_mut()
.as_idx_mut(&package)
.or_not_found(&package)?
.as_hosts_mut()
.as_idx_mut(&host)
.or_not_found(&host)?
.as_addresses_mut()
.mutate(|a| Ok(a.insert(address)))
})
.await?;
let service = ctx.services.get(&package).await;
let service_ref = service.as_ref().or_not_found(&package)?;
service_ref.update_host(host).await?;

Ok(())
}

pub async fn remove_address(
ctx: RpcContext,
AddressParams { address }: AddressParams,
(package, host): (PackageId, HostId),
) -> Result<(), Error> {
ctx.db
.mutate(|db| {
db.as_public_mut()
.as_package_data_mut()
.as_idx_mut(&package)
.or_not_found(&package)?
.as_hosts_mut()
.as_idx_mut(&host)
.or_not_found(&host)?
.as_addresses_mut()
.mutate(|a| Ok(a.remove(&address)))
})
.await?;
let service = ctx.services.get(&package).await;
let service_ref = service.as_ref().or_not_found(&package)?;
service_ref.update_host(host).await?;

Ok(())
}

pub async fn list_addresses(
ctx: RpcContext,
_: Empty,
(package, host): (PackageId, HostId),
) -> Result<BTreeSet<HostAddress>, Error> {
ctx.db
.peek()
.await
.into_public()
.into_package_data()
.into_idx(&package)
.or_not_found(&package)?
.into_hosts()
.into_idx(&host)
.or_not_found(&host)?
.into_addresses()
.de()
}
2 changes: 1 addition & 1 deletion core/startos/src/net/net_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ impl NetService {
errors.into_result()
}

async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> {
pub async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> {
let ctrl = self.net_controller()?;
let mut hostname_info = BTreeMap::new();
let binds = self.binds.entry(id.clone()).or_default();
Expand Down
84 changes: 82 additions & 2 deletions core/startos/src/net/tor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::logs::{journalctl, LogSource, LogsParams};
use crate::prelude::*;
use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat};
use crate::util::serde::{display_serializable, Base64, HandlerExtSerde, WithIoFormat};
use crate::util::Invoke;

pub const SYSTEMD_UNIT: &str = "tor@default";
Expand Down Expand Up @@ -59,7 +59,9 @@ impl Model<OnionStore> {
self.insert(&key.public().get_onion_address(), &key)
}
pub fn get_key(&self, address: &OnionAddressV3) -> Result<TorSecretKeyV3, Error> {
self.as_idx(address).or_not_found(address)?.de()
self.as_idx(address)
.or_not_found(lazy_format!("private key for {address}"))?
.de()
}
}

Expand Down Expand Up @@ -108,7 +110,85 @@ pub fn tor<C: Context>() -> ParentHandler<C> {
.with_about("Reset Tor daemon")
.with_call_remote::<CliContext>(),
)
.subcommand(
"key",
key::<C>().with_about("Manage the onion service key store"),
)
}

pub fn key<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"generate",
from_fn_async(generate_key)
.with_about("Generate an onion service key and add it to the key store")
.with_call_remote::<CliContext>(),
)
.subcommand(
"add",
from_fn_async(add_key)
.with_about("Add an onion service key to the key store")
.with_call_remote::<CliContext>(),
)
.subcommand(
"list",
from_fn_async(list_keys)
.with_custom_display_fn(|_, res| {
for addr in res {
println!("{addr}");
}
Ok(())
})
.with_about("List onion services with keys in the key store")
.with_call_remote::<CliContext>(),
)
}

pub async fn generate_key(ctx: RpcContext) -> Result<OnionAddressV3, Error> {
ctx.db
.mutate(|db| {
Ok(db
.as_private_mut()
.as_key_store_mut()
.as_onion_mut()
.new_key()?
.public()
.get_onion_address())
})
.await
}

#[derive(Deserialize, Serialize, Parser)]
pub struct AddKeyParams {
pub key: Base64<[u8; 64]>,
}

pub async fn add_key(
ctx: RpcContext,
AddKeyParams { key }: AddKeyParams,
) -> Result<OnionAddressV3, Error> {
let key = TorSecretKeyV3::from(key.0);
ctx.db
.mutate(|db| {
db.as_private_mut()
.as_key_store_mut()
.as_onion_mut()
.insert_key(&key)
})
.await?;
Ok(key.public().get_onion_address())
}

pub async fn list_keys(ctx: RpcContext) -> Result<Vec<OnionAddressV3>, Error> {
ctx.db
.peek()
.await
.into_private()
.into_key_store()
.into_onion()
.keys()
}

#[derive(Deserialize, Serialize, Parser, TS)]
#[serde(rename_all = "camelCase")]
#[command(rename_all = "kebab-case")]
Expand Down
26 changes: 25 additions & 1 deletion core/startos/src/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use futures::stream::FusedStream;
use futures::{SinkExt, StreamExt, TryStreamExt};
use imbl_value::{json, InternedString};
use itertools::Itertools;
use models::{ActionId, ImageId, PackageId, ProcedureName};
use models::{ActionId, HostId, ImageId, PackageId, ProcedureName};
use nix::sys::signal::Signal;
use persistent_container::{PersistentContainer, Subcontainer};
use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, HandlerArgs, HandlerFor};
Expand Down Expand Up @@ -603,6 +603,30 @@ impl Service {
memory_usage: MiB::from_MiB(used),
})
}

pub async fn update_host(&self, host_id: HostId) -> Result<(), Error> {
let host = self
.seed
.ctx
.db
.peek()
.await
.as_public()
.as_package_data()
.as_idx(&self.seed.id)
.or_not_found(&self.seed.id)?
.as_hosts()
.as_idx(&host_id)
.or_not_found(&host_id)?
.de()?;
self.seed
.persistent_container
.net_service
.lock()
.await
.update(host_id, host)
.await
}
}

#[derive(Debug, Clone)]
Expand Down
6 changes: 6 additions & 0 deletions core/startos/src/util/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,12 @@ impl<T: TryFrom<Vec<u8>>> FromStr for Base64<T> {
})
}
}
impl<T: TryFrom<Vec<u8>>> ValueParserFactory for Base64<T> {
type Parser = FromStrParser<Self>;
fn value_parser() -> Self::Parser {
Self::Parser::new()
}
}
impl<'de, T: TryFrom<Vec<u8>>> Deserialize<'de> for Base64<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down
Loading

0 comments on commit 645b856

Please sign in to comment.