123 lines
3.9 KiB
Rust
123 lines
3.9 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use indexmap::IndexSet;
|
|
pub use models::InterfaceId;
|
|
use serde::{Deserialize, Deserializer, Serialize};
|
|
use sqlx::{Executor, Postgres};
|
|
use tracing::instrument;
|
|
|
|
use crate::db::model::{InterfaceAddressMap, InterfaceAddresses};
|
|
use crate::net::keys::Key;
|
|
use crate::s9pk::manifest::PackageId;
|
|
use crate::util::serde::Port;
|
|
use crate::{Error, ResultExt};
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct Interfaces(pub BTreeMap<InterfaceId, Interface>); // TODO
|
|
impl Interfaces {
|
|
#[instrument(skip_all)]
|
|
pub fn validate(&self) -> Result<(), Error> {
|
|
for (_, interface) in &self.0 {
|
|
interface.validate().with_ctx(|_| {
|
|
(
|
|
crate::ErrorKind::ValidateS9pk,
|
|
format!("Interface {}", interface.name),
|
|
)
|
|
})?;
|
|
}
|
|
Ok(())
|
|
}
|
|
#[instrument(skip_all)]
|
|
pub async fn install<Ex>(
|
|
&self,
|
|
secrets: &mut Ex,
|
|
package_id: &PackageId,
|
|
) -> Result<InterfaceAddressMap, Error>
|
|
where
|
|
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
|
{
|
|
let mut interface_addresses = InterfaceAddressMap(BTreeMap::new());
|
|
for (id, iface) in &self.0 {
|
|
let mut addrs = InterfaceAddresses {
|
|
tor_address: None,
|
|
lan_address: None,
|
|
};
|
|
if iface.tor_config.is_some() || iface.lan_config.is_some() {
|
|
let key =
|
|
Key::for_interface(secrets, Some((package_id.clone(), id.clone()))).await?;
|
|
if iface.tor_config.is_some() {
|
|
addrs.tor_address = Some(key.tor_address().to_string());
|
|
}
|
|
if iface.lan_config.is_some() {
|
|
addrs.lan_address = Some(key.local_address());
|
|
}
|
|
}
|
|
interface_addresses.0.insert(id.clone(), addrs);
|
|
}
|
|
Ok(interface_addresses)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct Interface {
|
|
pub name: String,
|
|
pub description: String,
|
|
pub tor_config: Option<TorConfig>,
|
|
pub lan_config: Option<BTreeMap<Port, LanPortConfig>>,
|
|
pub ui: bool,
|
|
pub protocols: IndexSet<String>,
|
|
}
|
|
impl Interface {
|
|
#[instrument(skip_all)]
|
|
pub fn validate(&self) -> Result<(), color_eyre::eyre::Report> {
|
|
if self.tor_config.is_some() && !self.protocols.contains("tcp") {
|
|
color_eyre::eyre::bail!("must support tcp to set up a tor hidden service");
|
|
}
|
|
if self.lan_config.is_some() && !self.protocols.contains("http") {
|
|
color_eyre::eyre::bail!("must support http to set up a lan service");
|
|
}
|
|
if self.ui && !(self.protocols.contains("http") || self.protocols.contains("https")) {
|
|
color_eyre::eyre::bail!("must support http or https to serve a ui");
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct TorConfig {
|
|
pub port_mapping: BTreeMap<Port, Port>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct LanPortConfig {
|
|
pub ssl: bool,
|
|
pub internal: u16,
|
|
}
|
|
impl<'de> Deserialize<'de> for LanPortConfig {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
struct PermissiveLanPortConfig {
|
|
ssl: bool,
|
|
internal: Option<u16>,
|
|
mapping: Option<u16>,
|
|
}
|
|
|
|
let config = PermissiveLanPortConfig::deserialize(deserializer)?;
|
|
Ok(LanPortConfig {
|
|
ssl: config.ssl,
|
|
internal: config
|
|
.internal
|
|
.or(config.mapping)
|
|
.ok_or_else(|| serde::de::Error::missing_field("internal"))?,
|
|
})
|
|
}
|
|
}
|