mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
WIP: IP, pubkey, system time, system uptime, ca fingerprint (#2091)
* closes #923, #2063, #2012, #1153 * add ca fingerprint * add `server.time` * add `ip-info` to `server-info` * add ssh pubkey * support multiple IPs * rename key * add `ca-fingerprint` and `system-start-time` * fix off-by-one * update compat cargo lock Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
committed by
Aiden McClelland
parent
673e5af030
commit
06cf83b901
43
backend/src/net/dhcp.rs
Normal file
43
backend/src/net/dhcp.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use futures::TryStreamExt;
|
||||
use rpc_toolkit::command;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::IpInfo;
|
||||
use crate::net::net_utils::{iface_is_physical, list_interfaces};
|
||||
use crate::util::display_none;
|
||||
use crate::Error;
|
||||
|
||||
pub async fn init_ips() -> Result<BTreeMap<String, IpInfo>, Error> {
|
||||
let mut res = BTreeMap::new();
|
||||
let mut ifaces = list_interfaces();
|
||||
while let Some(iface) = ifaces.try_next().await? {
|
||||
if iface_is_physical(&iface).await {
|
||||
let ip_info = IpInfo::for_interface(&iface).await?;
|
||||
res.insert(iface, ip_info);
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[command(subcommands(update))]
|
||||
pub async fn dhcp() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(display(display_none))]
|
||||
pub async fn update(#[context] ctx: RpcContext, #[arg] interface: String) -> Result<(), Error> {
|
||||
if iface_is_physical(&interface).await {
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.ip_info()
|
||||
.idx_model(&interface)
|
||||
.put(
|
||||
&mut ctx.db.handle(),
|
||||
&IpInfo::for_interface(&interface).await?,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use crate::util::serde::Port;
|
||||
use crate::Error;
|
||||
|
||||
pub mod cert_resolver;
|
||||
pub mod dhcp;
|
||||
pub mod dns;
|
||||
pub mod embassy_service_http_server;
|
||||
pub mod interface;
|
||||
@@ -28,7 +29,7 @@ pub mod wifi;
|
||||
|
||||
const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl";
|
||||
|
||||
#[command(subcommands(tor::tor))]
|
||||
#[command(subcommands(tor::tor, dhcp::dhcp))]
|
||||
pub fn net() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,115 @@
|
||||
use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_stream::try_stream;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::stream::BoxStream;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use http::{Request, Uri};
|
||||
use hyper::Body;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::util::Invoke;
|
||||
use crate::Error;
|
||||
|
||||
fn parse_iface_ip(output: &str) -> Result<Option<&str>, Error> {
|
||||
let output = output.trim();
|
||||
if output.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(ip) = output
|
||||
.split_ascii_whitespace()
|
||||
.nth(3)
|
||||
.and_then(|range| range.split("/").next())
|
||||
{
|
||||
Ok(Some(ip))
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("malformed output from `ip`"),
|
||||
crate::ErrorKind::Network,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_iface_ipv4_addr(iface: &str) -> Result<Option<Ipv4Addr>, Error> {
|
||||
Ok(parse_iface_ip(&String::from_utf8(
|
||||
Command::new("ip")
|
||||
.arg("-4")
|
||||
.arg("-o")
|
||||
.arg("addr")
|
||||
.arg("show")
|
||||
.arg(iface)
|
||||
.invoke(crate::ErrorKind::Network)
|
||||
.await?,
|
||||
)?)?
|
||||
.map(|s| s.parse())
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
pub async fn get_iface_ipv6_addr(iface: &str) -> Result<Option<Ipv6Addr>, Error> {
|
||||
Ok(parse_iface_ip(&String::from_utf8(
|
||||
Command::new("ip")
|
||||
.arg("-6")
|
||||
.arg("-o")
|
||||
.arg("addr")
|
||||
.arg("show")
|
||||
.arg(iface)
|
||||
.invoke(crate::ErrorKind::Network)
|
||||
.await?,
|
||||
)?)?
|
||||
.map(|s| s.parse())
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
pub async fn iface_is_physical(iface: &str) -> bool {
|
||||
tokio::fs::metadata(Path::new("/sys/class/net").join(iface).join("device"))
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub async fn iface_is_wireless(iface: &str) -> bool {
|
||||
tokio::fs::metadata(Path::new("/sys/class/net").join(iface).join("wireless"))
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn list_interfaces() -> BoxStream<'static, Result<String, Error>> {
|
||||
try_stream! {
|
||||
let mut ifaces = tokio::fs::read_dir("/sys/class/net").await?;
|
||||
while let Some(iface) = ifaces.next_entry().await? {
|
||||
if let Some(iface) = iface.file_name().into_string().ok() {
|
||||
yield iface;
|
||||
}
|
||||
}
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub async fn find_wifi_iface() -> Result<Option<String>, Error> {
|
||||
let mut ifaces = list_interfaces();
|
||||
while let Some(iface) = ifaces.try_next().await? {
|
||||
if iface_is_wireless(&iface).await {
|
||||
return Ok(Some(iface));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn find_eth_iface() -> Result<String, Error> {
|
||||
let mut ifaces = list_interfaces();
|
||||
while let Some(iface) = ifaces.try_next().await? {
|
||||
if iface_is_physical(&iface).await && !iface_is_wireless(&iface).await {
|
||||
return Ok(iface);
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("Could not detect ethernet interface"),
|
||||
crate::ErrorKind::Network,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn host_addr_fqdn(req: &Request<Body>) -> Result<ResourceFqdn, Error> {
|
||||
let host = req.headers().get(http::header::HOST);
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ use crate::context::{DiagnosticContext, InstallContext, RpcContext, SetupContext
|
||||
use crate::core::rpc_continuations::RequestGuid;
|
||||
use crate::db::subscribe;
|
||||
use crate::install::PKG_PUBLIC_DIR;
|
||||
use crate::middleware::auth::HasValidSession;
|
||||
use crate::middleware::auth::{auth as auth_middleware, HasValidSession};
|
||||
use crate::middleware::cors::cors;
|
||||
use crate::middleware::db::db as db_middleware;
|
||||
use crate::middleware::diagnostic::diagnostic as diagnostic_middleware;
|
||||
use crate::net::HttpHandler;
|
||||
use crate::{diagnostic_api, install_api, main_api, setup_api, Error, ErrorKind, ResultExt};
|
||||
|
||||
@@ -48,8 +51,14 @@ pub async fn setup_ui_file_router(ctx: SetupContext) -> Result<HttpHandler, Erro
|
||||
async move {
|
||||
let res = match req.uri().path() {
|
||||
path if path.starts_with("/rpc/") => {
|
||||
let rpc_handler =
|
||||
rpc_handler!({command: setup_api, context: ctx, status: status_fn});
|
||||
let rpc_handler = rpc_handler!({
|
||||
command: setup_api,
|
||||
context: ctx,
|
||||
status: status_fn,
|
||||
middleware: [
|
||||
cors,
|
||||
]
|
||||
});
|
||||
|
||||
rpc_handler(req)
|
||||
.await
|
||||
@@ -76,8 +85,15 @@ pub async fn diag_ui_file_router(ctx: DiagnosticContext) -> Result<HttpHandler,
|
||||
async move {
|
||||
let res = match req.uri().path() {
|
||||
path if path.starts_with("/rpc/") => {
|
||||
let rpc_handler =
|
||||
rpc_handler!({command: diagnostic_api, context: ctx, status: status_fn});
|
||||
let rpc_handler = rpc_handler!({
|
||||
command: diagnostic_api,
|
||||
context: ctx,
|
||||
status: status_fn,
|
||||
middleware: [
|
||||
cors,
|
||||
diagnostic_middleware,
|
||||
]
|
||||
});
|
||||
|
||||
rpc_handler(req)
|
||||
.await
|
||||
@@ -104,8 +120,14 @@ pub async fn install_ui_file_router(ctx: InstallContext) -> Result<HttpHandler,
|
||||
async move {
|
||||
let res = match req.uri().path() {
|
||||
path if path.starts_with("/rpc/") => {
|
||||
let rpc_handler =
|
||||
rpc_handler!({command: install_api, context: ctx, status: status_fn});
|
||||
let rpc_handler = rpc_handler!({
|
||||
command: install_api,
|
||||
context: ctx,
|
||||
status: status_fn,
|
||||
middleware: [
|
||||
cors,
|
||||
]
|
||||
});
|
||||
|
||||
rpc_handler(req)
|
||||
.await
|
||||
@@ -132,8 +154,18 @@ pub async fn main_ui_server_router(ctx: RpcContext) -> Result<HttpHandler, Error
|
||||
async move {
|
||||
let res = match req.uri().path() {
|
||||
path if path.starts_with("/rpc/") => {
|
||||
let rpc_handler =
|
||||
rpc_handler!({command: main_api, context: ctx, status: status_fn});
|
||||
let auth_middleware = auth_middleware(ctx.clone());
|
||||
let db_middleware = db_middleware(ctx.clone());
|
||||
let rpc_handler = rpc_handler!({
|
||||
command: main_api,
|
||||
context: ctx,
|
||||
status: status_fn,
|
||||
middleware: [
|
||||
cors,
|
||||
auth_middleware,
|
||||
db_middleware,
|
||||
]
|
||||
});
|
||||
|
||||
rpc_handler(req)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user