diff --git a/Cargo.toml b/Cargo.toml index 0bb00dd..ec79e21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ resolver = "2" members = [ "server", "client", + "formnet", "hostsfile", "shared", "publicip", diff --git a/client/src/data_store.rs b/client/src/data_store.rs index 20efc47..1a77e77 100644 --- a/client/src/data_store.rs +++ b/client/src/data_store.rs @@ -1,4 +1,4 @@ -use crate::Error; +use shared::Error; use anyhow::bail; use serde::{Deserialize, Serialize}; use shared::{chmod, ensure_dirs_exist, Cidr, IoErrorContext, Peer, WrappedIoError}; diff --git a/client/src/lib.rs b/client/src/lib.rs new file mode 100644 index 0000000..e866ab1 --- /dev/null +++ b/client/src/lib.rs @@ -0,0 +1,3 @@ +pub mod util; +pub mod data_store; +pub mod nat; diff --git a/formnet/Cargo.toml b/formnet/Cargo.toml new file mode 100644 index 0000000..47c7975 --- /dev/null +++ b/formnet/Cargo.toml @@ -0,0 +1,49 @@ +[package] +authors = [ + "Andrew Smith as@formation.cloud" +] +description = "A service that uses innernet behind the scenes to coordinate the formation fog network." +edition = "2021" +license = "MIT" +name = "formnet" +publish = false +readme = "README.md" +version = "0.1.0" + +[features] +integration = [] + +[lib] +path = "src/lib.rs" + +[[bin]] +name = "formnet" +path = "src/main.rs" + +[[bin]] +name = "formnet-client" +path = "src/client.rs" + + +[dependencies] +innernet-server = { path = "../server" } +client = { path = "../client" } +wireguard-control = { path = "../wireguard-control" } +form-types = { path = "../../form-types" } +tokio = { version = "1", features=["full"] } +clap = { version = "4.5.23", features=["derive"] } +conductor = { git = "https://github.com/versatus/conductor.git" } +shared = { path = "../shared" } +ipnet = "2.9" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +async-trait = "0.1.83" +bincode = "1.3.3" +axum = "0.7" +reqwest = { version = "0.12", features = ["json"] } +log = "0.4" +simple_logger = "5.0.0" +log4rs = "1.3.0" + +[dev-dependencies] +random_word = { version = "0.4.3", features = ["en"] } diff --git a/formnet/src/client.rs b/formnet/src/client.rs new file mode 100644 index 0000000..1b85472 --- /dev/null +++ b/formnet/src/client.rs @@ -0,0 +1,4 @@ +#[tokio::main] +async fn main() -> Result<(), Box> { + Ok(()) +} diff --git a/formnet/src/lib.rs b/formnet/src/lib.rs new file mode 100644 index 0000000..4bbcabd --- /dev/null +++ b/formnet/src/lib.rs @@ -0,0 +1,585 @@ +use std::{net::SocketAddr, path::PathBuf, str::FromStr, time::SystemTime}; +use axum::{http::StatusCode, Json}; +use ipnet::IpNet; +use shared::{interface_config::{InterfaceConfig, InterfaceInfo}, IpNetExt, Timestring, PERSISTENT_KEEPALIVE_INTERVAL_SECS}; +use shared::{interface_config::ServerInfo, Cidr, CidrTree, Hostname, Peer, PeerContents}; +use tokio::{io::{AsyncReadExt, AsyncWriteExt}, sync::broadcast::Receiver}; +use wireguard_control::{InterfaceName, KeyPair}; +use client::util::Api; +use innernet_server::{ + ConfigFile, + ServerConfig, + open_database_connection, + DatabasePeer, + DatabaseCidr, +}; + +use conductor::{ + HEADER_SIZE, + TOPIC_SIZE_OFFSET, + util::{ + try_get_topic_len, try_get_message_len, parse_next_message + }, + subscriber::SubStream, + publisher::PubStream +}; +use form_types::{GenericPublisher, VmmEvent, VmmTopic, FormnetMessage}; +use tokio::net::TcpStream; +use serde::{Serialize, Deserialize}; + +pub const CONFIG_DIR: &'static str = "/etc/innernet"; +pub const SERVER_CONFIG_DIR: &'static str = "/etc/innernet-server"; +pub const SERVER_DATA_DIR: &'static str = "/var/lib/innernet-server"; + +pub async fn add_peer<'a>( + peers: &[Peer], + cidr_tree: &CidrTree<'a>, + peer_type: &PeerType, + peer_id: &str +) -> Result<(PeerContents, KeyPair), Box> { + let leaves = cidr_tree.leaves(); + let cidr = match peer_type { + PeerType::User => { + leaves.iter().filter(|cidr| cidr.name == "operators").collect::>().first().cloned() + } + PeerType::Operator => { + leaves.iter().filter(|cidr| cidr.name == "operators").collect::>().first().cloned() + } + PeerType::Instance => { + leaves.iter().filter(|cidr| cidr.name == "vm-subnet").collect::>().first().cloned() + } + }.ok_or( + Box::new( + std::io::Error::new( + std::io::ErrorKind::Other, + "CIDRs are not properly set up" + ) + ) + )?; + + log::info!("Choose CIDR: {cidr:?}"); + + let mut available_ip = None; + let candidate_ips = cidr.hosts().filter(|ip| cidr.is_assignable(ip)); + for ip in candidate_ips { + if !peers.iter().any(|peer| peer.ip == ip) { + available_ip = Some(ip); + break; + } + } + let ip = available_ip.ok_or( + Box::new( + std::io::Error::new( + std::io::ErrorKind::Other, + "No IPs in this CIDR are available" + ) + ) + )?; + log::info!("Choose IP: {ip:?}"); + + let default_keypair = KeyPair::generate(); + + log::info!("Generated Keypair"); + + let invite_expires: Timestring = "1d".parse().map_err(|e| { + Box::new( + std::io::Error::new( + std::io::ErrorKind::Other, + e + ) + ) + })?; + log::info!("Generated expiration"); + + let name = Hostname::from_str(peer_id)?; + log::info!("Generated Hostname"); + + let peer_request = PeerContents { + name, + ip, + cidr_id: cidr.id, + public_key: default_keypair.public.to_base64(), + endpoint: None, + is_admin: match peer_type { + PeerType::Operator => true, + _ => false + }, + is_disabled: false, + is_redeemed: false, + persistent_keepalive_interval: Some(PERSISTENT_KEEPALIVE_INTERVAL_SECS), + invite_expires: Some(SystemTime::now() + invite_expires.into()), + candidates: vec![], + }; + + Ok((peer_request, default_keypair)) +} + +pub async fn server_add_peer( + inet: &InterfaceName, + conf: &ServerConfig, + peer_type: &PeerType, + peer_id: &str, +) -> Result> { + log::info!("Reading config file into ConfigFile..."); + let config = ConfigFile::from_file(conf.config_path(inet))?; + log::info!("Opening database connection..."); + let conn = open_database_connection(inet, conf)?; + log::info!("Collecting peers..."); + let peers = DatabasePeer::list(&conn)? + .into_iter().map(|dp| dp.inner) + .collect::>(); + + log::info!("Collecting CIDRs..."); + let cidrs = DatabaseCidr::list(&conn)?; + let cidr_tree = CidrTree::new(&cidrs[..]); + + log::info!("calling add peer to get key pair and contents..."); + let (contents, keypair) = add_peer(&peers, &cidr_tree, peer_type, peer_id).await?; + + log::info!("Getting Server Peer..."); + let server_peer = DatabasePeer::get(&conn, 1)?; + + log::info!("Creating peer..."); + let peer = DatabasePeer::create(&conn, contents)?; + + log::info!("building invitation..."); + let peer_invitation = InterfaceConfig { + interface: InterfaceInfo { + network_name: inet.to_string(), + private_key: keypair.private.to_base64(), + address: IpNet::new(peer.ip, cidr_tree.prefix_len())?, + listen_port: None, + }, + server: ServerInfo { + external_endpoint: server_peer + .endpoint + .clone() + .expect("The innernet server should have a WireGuard endpoint"), + internal_endpoint: SocketAddr::new(config.address, config.listen_port), + public_key: server_peer.public_key.clone(), + }, + }; + + log::info!("returning invitation..."); + Ok(peer_invitation) +} + +pub async fn respond_with_peer_invitation<'a>( + peer: &Peer, + server: ServerInfo, + root_cidr: &CidrTree<'a>, + keypair: KeyPair, +) -> Result<(), Box> { + let invite = InterfaceConfig { + interface: InterfaceInfo { + network_name: "formnet".to_string(), + private_key: keypair.private.to_base64(), + address: IpNet::new(peer.ip, root_cidr.prefix_len())?, + listen_port: None, + }, + server + }; + + // Write to the MessageBroker under `VmmTopic` as this + // should represent a request for a new Instance to be added to the + // network + let mut publisher = GenericPublisher::new("127.0.0.1:5555").await?; + publisher.publish( + Box::new(VmmTopic), + Box::new(VmmEvent::NetworkSetupComplete { + invite: serde_json::to_string(&invite)? + }) + ).await?; + + Ok(()) +} + +pub async fn server_respond_with_peer_invitation(invitation: InterfaceConfig) -> Result<(), Box> { + // Write to the MessageBroker under `VmmTopic` as this + // should represent a request for a new Instance to be added to the + // network + let mut publisher = GenericPublisher::new("127.0.0.1:5555").await?; + publisher.publish( + Box::new(VmmTopic), + Box::new(VmmEvent::NetworkSetupComplete { + invite: serde_json::to_string(&invitation)? + }) + ).await?; + + Ok(()) +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum FormnetEvent { + AddPeer { + peer_type: PeerType, + peer_id: String, + callback: SocketAddr + }, + DisablePeer, + EnablePeer, + SetListenPort, + OverrideEndpoint, +} + +impl FormnetEvent { + #[cfg(not(any(feature = "integration", test)))] + pub const INTERFACE_NAME: &'static str = "formnet"; + #[cfg(any(feature = "integration", test))] + pub const INTERFACE_NAME: &'static str = "test-net"; +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum PeerType { + Operator, + User, + Instance, +} + +impl From for PeerType { + fn from(value: form_types::PeerType) -> Self { + match value { + form_types::PeerType::User => PeerType::User, + form_types::PeerType::Operator => PeerType::Operator, + form_types::PeerType::Instance => PeerType::Instance, + } + } +} + +impl From<&form_types::PeerType> for PeerType { + fn from(value: &form_types::PeerType) -> Self { + match value { + form_types::PeerType::User => PeerType::User, + form_types::PeerType::Operator => PeerType::Operator, + form_types::PeerType::Instance => PeerType::Instance, + } + } +} + +impl From for form_types::PeerType { + fn from(value: PeerType) -> Self { + match value { + PeerType::User => form_types::PeerType::User, + PeerType::Operator => form_types::PeerType::Operator, + PeerType::Instance => form_types::PeerType::Instance , + } + } +} + +impl From<&PeerType> for form_types::PeerType { + fn from(value: &PeerType) -> Self { + match value { + PeerType::User => form_types::PeerType::User, + PeerType::Operator => form_types::PeerType::Operator, + PeerType::Instance => form_types::PeerType::Instance , + } + } +} + +pub struct FormnetSubscriber { + stream: TcpStream +} + +impl FormnetSubscriber { + pub async fn new(uri: &str, topics: Vec) -> std::io::Result { + let mut stream = TcpStream::connect(uri).await?; + let topic_str = topics.join(","); + stream.write_all(topic_str.as_bytes()).await?; + Ok(Self { stream }) + } +} + +#[async_trait::async_trait] +impl SubStream for FormnetSubscriber { + type Message = Vec; + + async fn receive(&mut self) -> std::io::Result { + let mut buffer = Vec::new(); + loop { + let mut read_buffer = [0; 4096]; + match self.stream.read(&mut read_buffer).await { + Err(e) => log::error!("Error reading stream to buffer: {e}..."), + Ok(n) => { + if n == 0 { + break; + } + + buffer.extend_from_slice(&read_buffer[..n]); + let results = Self::parse_messages(&mut buffer).await?; + if !results.is_empty() { + return Ok(results); + } + } + } + } + Err(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "No complete messages received", + )) + } + + async fn parse_messages(msg: &mut Vec) -> std::io::Result { + let mut results = Vec::new(); + while msg.len() >= HEADER_SIZE { + let total_len = try_get_message_len(msg)?; + if msg.len() >= total_len { + let topic_len = try_get_topic_len(msg)?; + let (_, message) = parse_next_message(total_len, topic_len, msg).await; + let message_offset = TOPIC_SIZE_OFFSET + topic_len; + let msg = &message[message_offset..message_offset + total_len]; + results.push(msg.to_vec()); + } + } + + let msg_results = results + .iter() + .filter_map(|m| serde_json::from_slice(&m).ok()) + .collect(); + + Ok(msg_results) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UserJoinRequest { + user_id: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum UserJoinResponse { + Success { + #[serde(flatten)] + invitation: InterfaceConfig, + }, + Error(String) +} + +pub fn create_router() -> axum::Router { + axum::Router::new().route("/join", axum::routing::post(handle_join_request)) +} + +pub fn is_server() -> bool { + PathBuf::from(SERVER_CONFIG_DIR).join( + format!("{}.conf", FormnetMessage::INTERFACE_NAME) + ).exists() +} + +async fn handle_join_request_from_server( + join_request: UserJoinRequest, + inet: InterfaceName +) -> (StatusCode, axum::Json) { + match server_add_peer( + &inet, + &ServerConfig { config_dir: SERVER_CONFIG_DIR.into(), data_dir: SERVER_DATA_DIR.into() }, + &PeerType::User, + &join_request.user_id + ).await { + Ok(invitation) => { + let resp = UserJoinResponse::Success { invitation }; + return ( + StatusCode::OK, + Json(resp) + ) + }, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(e.to_string())) + ) + } + } +} + +async fn handle_join_request_from_admin_client( + join_request: UserJoinRequest, + inet: InterfaceName +) -> (StatusCode, axum::Json) { + let InterfaceConfig { server, ..} = { + match InterfaceConfig::from_interface( + &PathBuf::from(CONFIG_DIR).as_path(), + &inet + ) { + Ok(config) => config, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Failed to acquire config for innernet server: {}", e))) + ) + } + } + }; + + let api = Api::new(&server); + + log::info!("Fetching CIDRs..."); + let cidrs: Vec = match api.http("GET", "/admin/cidrs") { + Ok(cidr_list) => cidr_list, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Failed to acquire CIDR list for innernet network: {e}"))) + ) + } + }; + log::info!("Fetching Peers..."); + let peers: Vec = match api.http("GET", "/admin/peers") { + Ok(peer_list) => peer_list, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Failed to acquire Peer list for innernet network: {e}"))) + ) + } + }; + log::info!("Creating CIDR Tree..."); + let cidr_tree = CidrTree::new(&cidrs[..]); + + match add_peer( + &peers, &cidr_tree, &PeerType::User, &join_request.user_id + ).await { + Ok((content, keypair)) => { + log::info!("Creating peer..."); + let peer: Peer = match api.http_form("POST", "/admin/peers", content) { + Ok(peer) => peer, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Failed to create peer: {e}"))) + ) + } + }; + + match api_respond_with_peer_invitation(&peer, server, &cidr_tree, keypair).await { + Ok(resp) => { + return ( + StatusCode::OK, + Json(resp) + ) + } + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Unable to build peer invitation: {e}"))) + ) + } + } + } + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Failed to add peer to innernet: {e}"))) + ) + } + } +} + +pub async fn handle_join_request(axum::Json(join_request): axum::Json) -> impl axum::response::IntoResponse { + let inet = match InterfaceName::from_str( + FormnetMessage::INTERFACE_NAME + ) { + Ok(inet) => inet, + Err(e) => return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(UserJoinResponse::Error(format!("Failed to convert {} into InterfaceName: {e}", FormnetMessage::INTERFACE_NAME))) + ) + }; + + if is_server() { + return handle_join_request_from_server(join_request, inet).await; + } else { + return handle_join_request_from_admin_client(join_request, inet).await; + } +} + +async fn api_respond_with_peer_invitation<'a>( + peer: &Peer, + server: ServerInfo, + root_cidr: &CidrTree<'a>, + keypair: KeyPair, +) -> Result> { + Ok(UserJoinResponse::Success { + invitation: InterfaceConfig { + interface: InterfaceInfo { + network_name: "formnet".to_string(), + private_key: keypair.private.to_base64(), + address: IpNet::new(peer.ip, root_cidr.prefix_len())?, + listen_port: None, + }, + server + } + }) +} + +pub async fn api_shutdown_handler( + mut rx: Receiver<()> +) { + loop { + tokio::select! { + res = rx.recv() => { + log::info!("Received shutdown signal for api server: {res:?}"); + break; + } + } + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use super::*; + use tokio::net::TcpListener; + use reqwest::Client; + + #[tokio::test] + async fn test_user_join() -> Result<(), Box> { + let (tx, rx) = tokio::sync::broadcast::channel(1); + let api_shutdown = rx.resubscribe(); + + let api_handle = tokio::spawn(async move { + let api = create_router(); + let listener = TcpListener::bind("0.0.0.0:3001").await?; + + let _ = axum::serve( + listener, + api + ).with_graceful_shutdown( + api_shutdown_handler(api_shutdown) + ).await; + + Ok::<(), Box>(()) + }); + + tokio::time::sleep(Duration::from_secs(2)).await; + + let user_id = random_word::gen(random_word::Lang::En).to_string(); + let client = Client::new(); + let response = client.post("http://localhost:3001/join") + .json(&UserJoinRequest { + user_id + }).send().await?; + + log::info!("{:?}", response); + + let status = response.status().clone(); + + // Let's print out the error response body if we get a non-success status + if !response.status().is_success() { + let error_body = response.text().await?.clone(); + log::info!("Error response body: {}", error_body); + // Now fail the test + panic!("Request failed with status {} and error: {}", status, error_body); + } + + assert!(response.status().is_success()); + + let join_response = response.json::().await?; + + log::info!("{}", serde_json::to_string_pretty(&join_response)?); + + let _ = tx.send(()); + let _ = api_handle.await?; + + Ok(()) + } +} diff --git a/formnet/src/main.rs b/formnet/src/main.rs new file mode 100644 index 0000000..21876e2 --- /dev/null +++ b/formnet/src/main.rs @@ -0,0 +1,179 @@ +use std::path::PathBuf; +use std::str::FromStr; +use innernet_server::ServerConfig; +use shared::interface_config::InterfaceConfig; +use shared::{Cidr, CidrTree, Peer}; +use tokio::{net::TcpListener, sync::broadcast::Receiver}; +use wireguard_control::InterfaceName; +use client::util::Api; +use conductor::subscriber::SubStream; +use form_types::FormnetMessage; +use formnet::*; + +#[tokio::main] +async fn main() -> Result<(), Box> { + + simple_logger::SimpleLogger::new().init().unwrap(); + + // Create innernet from CLI, Config or Wizard + // If done via wizard save to file + // Listen for messages on topic "Network" from broker + // Handle messages + // + // Formnet service can: + // 1. Add peers + // 2. Remove peers + // 3. Add CIDRs + // 4. Remove CIDRs + // 5. Rename Peers + // 6. Rename CIDRs + // 7. Enable Peers + // 8. Disable Peers + // 9. Manage Associations + // 10. Manage Endpoints + // + // When a new peer joins the network, a join token will be sent to them + // which they will then "install" via their formnet network service. + // + // In the formnet there are 3 types of peers: + // 1. Operators - All operators are admins and can add CIDRs, Peers, Associations, etc. + // All operators run a "server" replica. + // + // 2. Users - Users run a simple client, they are added as a peer, and in future version + // will have more strictly managed associations to ensure they only have + // access to the resources they own. In the first version, they have access + // to the entire network, but instances and resources use internal auth mechanisms + // such as public/private key auth to provide security. + // + // 3. Instances - Instances are user owned resources, such as Virtual Machines, containers, + // etc. Instances are only manageable by their owner. Once they are up and + // running the rest of the network just knows they are there. Operators that + // are responsible for a given instance can be financially penalized for not + // maintaining the instance in the correct state/status. + // + + // So what do we need this to do + // 1. Listen on `topic` for relevant messages from the MessageBroker + // 2. When a message is received, match that message on an action + // 3. Handle the action (by using the API). + + + let (tx, rx) = tokio::sync::broadcast::channel(3); + let api_shutdown = tx.subscribe(); + + let api_handle = tokio::spawn(async move { + let api = create_router(); + let listener = TcpListener::bind("0.0.0.0:3001").await?; + + let _ = axum::serve( + listener, + api + ).with_graceful_shutdown( + api_shutdown_handler(api_shutdown) + ); + + Ok::<(), Box>(()) + }); + + let handle = tokio::spawn(async move { + let sub = FormnetSubscriber::new( + "127.0.0.1:5556", + vec![ + "formnet".to_string() + ] + ).await?; + if let Err(e) = run( + sub, + rx + ).await { + eprintln!("Error running innernet handler: {e}"); + } + + Ok::<(), Box>(()) + }); + + tokio::signal::ctrl_c().await?; + + if let Err(e) = tx.send(()) { + println!("Error sending shutdown signal: {e}"); + } + let _ = handle.await?; + let _ = api_handle.await?; + + Ok(()) +} + +async fn run( + mut subscriber: impl SubStream>, + mut shutdown: Receiver<()> +) -> Result<(), Box> { + loop { + tokio::select! { + Ok(msg) = subscriber.receive() => { + for m in msg { + if let Err(e) = handle_message(&m).await { + eprintln!("Error handling message {m:?}: {e}"); + } + } + } + _ = shutdown.recv() => { + eprintln!("Received shutdown signal for Formnet"); + break; + } + } + } + + Ok(()) +} + +async fn handle_message( + message: &FormnetMessage +) -> Result<(), Box> { + use form_types::FormnetMessage::*; + match message { + AddPeer { peer_type, peer_id, callback } => { + if is_server() { + let server_config = ServerConfig { + config_dir: PathBuf::from(SERVER_CONFIG_DIR), + data_dir: PathBuf::from(SERVER_DATA_DIR) + }; + let inet = InterfaceName::from_str(FormnetMessage::INTERFACE_NAME)?; + if let Ok(invitation) = server_add_peer( + &inet, + &server_config, + &peer_type.into(), + peer_id, + ).await { + return server_respond_with_peer_invitation(invitation).await; + } + } + + let InterfaceConfig { server, ..} = InterfaceConfig::from_interface( + PathBuf::from(CONFIG_DIR).as_path(), + &InterfaceName::from_str( + FormnetMessage::INTERFACE_NAME + )? + )?; + let api = Api::new(&server); + println!("Fetching CIDRs..."); + let cidrs: Vec = api.http("GET", "/admin/cidrs")?; + println!("Fetching Peers..."); + let peers: Vec = api.http("GET", "/admin/peers")?; + println!("Creating CIDR Tree..."); + let cidr_tree = CidrTree::new(&cidrs[..]); + + if let Ok((content, keypair)) = add_peer( + &peers, &cidr_tree, &peer_type.into(), peer_id + ).await { + println!("Creating peer..."); + let peer: Peer = api.http_form("POST", "/admin/peers", content)?; + respond_with_peer_invitation(&peer, server.clone(), &cidr_tree, keypair).await?; + } + }, + DisablePeer => {}, + EnablePeer => {}, + SetListenPort => {}, + OverrideEndpoint => {}, + } + Ok(()) +} diff --git a/server/Cargo.toml b/server/Cargo.toml index dde8211..675a09d 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -27,7 +27,7 @@ v6-test = [] [dependencies] anyhow = "1" -bytes = "1" +bytes = "1.6" clap = { version = "4.3", features = ["derive", "wrap_help"] } clap_complete = "4.3" colored = "2" diff --git a/server/src/db/peer.rs b/server/src/db/peer.rs index 1dd6b40..b1c7aa6 100644 --- a/server/src/db/peer.rs +++ b/server/src/db/peer.rs @@ -87,15 +87,18 @@ impl DatabasePeer { .. } = &contents; log::info!("creating peer {:?}", contents); + println!("creating peer {:?}", contents); if !Self::is_valid_name(name) { log::warn!("peer name is invalid, must conform to hostname(7) requirements."); + println!("peer name is invalid, must conform to hostname(7) requirements."); return Err(ServerError::InvalidQuery); } let cidr = DatabaseCidr::get(conn, *cidr_id)?; if !cidr.cidr.contains(ip) { log::warn!("tried to add peer with IP outside of parent CIDR range."); + println!("tried to add peer with IP outside of parent CIDR range."); return Err(ServerError::InvalidQuery); } @@ -114,6 +117,7 @@ impl DatabasePeer { let candidates = serde_json::to_string(candidates)?; + println!("Executing SQL insert..."); conn.execute( &format!( "INSERT INTO peers ({}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", @@ -132,6 +136,7 @@ impl DatabasePeer { candidates, ], )?; + println!("Executed SQL insert..."); let id = conn.last_insert_rowid(); Ok(Peer { id, contents }.into()) } diff --git a/server/src/lib.rs b/server/src/lib.rs index 5081f48..3bc786b 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -28,14 +28,14 @@ use subtle::ConstantTimeEq; use wireguard_control::{Backend, Device, DeviceUpdate, InterfaceName, Key, PeerConfigBuilder}; mod api; -mod db; +pub mod db; mod error; pub mod initialize; #[cfg(test)] mod test; mod util; -use db::{DatabaseCidr, DatabasePeer}; +pub use db::{DatabaseCidr, DatabasePeer}; pub use error::ServerError; use shared::{prompts, wg, CidrTree, Error, Interface}; @@ -151,7 +151,7 @@ impl ServerConfig { } } -fn open_database_connection( +pub fn open_database_connection( interface: &InterfaceName, conf: &ServerConfig, ) -> Result { @@ -187,6 +187,7 @@ pub fn add_peer( if let Some(result) = shared::prompts::add_peer(&peers, &cidr_tree, &opts)? { let (peer_request, keypair, target_path, mut target_file) = result; + log::info!("Received results from prompts, attempting to create peer in database"); let peer = DatabasePeer::create(&conn, peer_request)?; if cfg!(not(test)) && Device::get(interface, network.backend).is_ok() { // Update the current WireGuard interface with the new peers. @@ -195,7 +196,7 @@ pub fn add_peer( .apply(interface, network.backend) .map_err(|_| ServerError::WireGuard)?; - println!("adding to WireGuard interface: {}", &*peer); + log::info!("adding to WireGuard interface: {}", &*peer); } let server_peer = DatabasePeer::get(&conn, 1)?; @@ -209,7 +210,7 @@ pub fn add_peer( &SocketAddr::new(config.address, config.listen_port), )?; } else { - println!("exited without creating peer."); + log::info!("exited without creating peer."); } Ok(()) @@ -233,7 +234,7 @@ pub fn rename_peer( .ok_or_else(|| anyhow!("Peer not found."))?; db_peer.update(&conn, peer_request)?; } else { - println!("exited without creating peer."); + log::info!("exited without creating peer."); } Ok(()) @@ -304,7 +305,7 @@ pub fn add_cidr( cidr_name = cidr.name.bold() ); } else { - println!("exited without creating CIDR."); + log::info!("exited without creating CIDR."); } Ok(()) @@ -325,7 +326,7 @@ pub fn rename_cidr( .ok_or_else(|| anyhow!("CIDR not found."))?; db::DatabaseCidr::from(db_cidr).update(&conn, cidr_request)?; } else { - println!("exited without renaming CIDR."); + log::info!("exited without renaming CIDR."); } Ok(()) @@ -336,7 +337,7 @@ pub fn delete_cidr( conf: &ServerConfig, args: DeleteCidrOpts, ) -> Result<(), Error> { - println!("Fetching eligible CIDRs"); + log::info!("Fetching eligible CIDRs"); let conn = open_database_connection(interface, conf)?; let cidrs = DatabaseCidr::list(&conn)?; let peers = DatabasePeer::list(&conn)? @@ -346,10 +347,10 @@ pub fn delete_cidr( let cidr_id = prompts::delete_cidr(&cidrs, &peers, &args)?; - println!("Deleting CIDR..."); + log::info!("Deleting CIDR..."); DatabaseCidr::delete(&conn, cidr_id)?; - println!("CIDR deleted."); + log::info!("CIDR deleted."); Ok(()) } @@ -369,7 +370,7 @@ pub fn uninstall( .default(false) .interact()? { - println!("{} bringing down interface (if up).", "[*]".dimmed()); + log::info!("{} bringing down interface (if up).", "[*]".dimmed()); wg::down(interface, network.backend).ok(); let config = conf.config_path(interface); let data = conf.database_path(interface);