diff --git a/Cargo.lock b/Cargo.lock index 23732c9..38e7455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -622,7 +622,7 @@ dependencies = [ [[package]] name = "dnapi-rs" -version = "0.1.4" +version = "0.1.5" dependencies = [ "base64 0.21.0", "base64-serde", diff --git a/dnapi-rs/Cargo.toml b/dnapi-rs/Cargo.toml index 8de3842..8160729 100644 --- a/dnapi-rs/Cargo.toml +++ b/dnapi-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dnapi-rs" -version = "0.1.4" +version = "0.1.5" edition = "2021" description = "A rust client for the Defined Networking API" license = "AGPL-3.0-or-later" @@ -21,9 +21,4 @@ base64 = "0.21.0" serde_json = "1.0.95" trifid-pki = { version = "0.1.6", path = "../trifid-pki", features = ["serde_derive"] } rand = "0.8.5" -chrono = "0.4.24" - -[features] -default = ["blocking"] -blocking = ["reqwest/blocking"] -async = [] \ No newline at end of file +chrono = "0.4.24" \ No newline at end of file diff --git a/dnapi-rs/src/client_async.rs b/dnapi-rs/src/client_async.rs index c4d37df..90d8266 100644 --- a/dnapi-rs/src/client_async.rs +++ b/dnapi-rs/src/client_async.rs @@ -24,7 +24,7 @@ pub struct Client { server_url: Url } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] /// A struct containing organization metadata returned as a result of enrollment pub struct EnrollMeta { /// The server organization ID this node is now a member of diff --git a/dnapi-rs/src/client_blocking.rs b/dnapi-rs/src/client_blocking.rs index 2ee13fc..c0c2cdf 100644 --- a/dnapi-rs/src/client_blocking.rs +++ b/dnapi-rs/src/client_blocking.rs @@ -24,7 +24,7 @@ pub struct Client { server_url: Url } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] /// A struct containing organization metadata returned as a result of enrollment pub struct EnrollMeta { /// The server organization ID this node is now a member of diff --git a/dnapi-rs/src/lib.rs b/dnapi-rs/src/lib.rs index 1317543..b9b63d8 100644 --- a/dnapi-rs/src/lib.rs +++ b/dnapi-rs/src/lib.rs @@ -15,18 +15,10 @@ #![allow(clippy::too_many_lines)] #![allow(clippy::module_name_repetitions)] -#[cfg(all(feature = "blocking", feature = "async"))] -compile_error!("Cannot compile with both blocking and async features at the same time. Please pick one or the other."); - pub mod message; -#[cfg(feature = "blocking")] -#[path = "client_blocking.rs"] -pub mod client; - -#[cfg(feature = "async")] -#[path = "client_async.rs"] -pub mod client; +pub mod client_blocking; +pub mod client_async; pub mod credentials; pub mod crypto; \ No newline at end of file diff --git a/tfclient/Cargo.toml b/tfclient/Cargo.toml index fde466b..f8d138f 100644 --- a/tfclient/Cargo.toml +++ b/tfclient/Cargo.toml @@ -24,7 +24,7 @@ base64 = "0.21.0" chrono = "0.4.24" ipnet = "2.7.1" base64-serde = "0.7.0" -dnapi-rs = { version = "0.1.2", path = "../dnapi-rs" } +dnapi-rs = { version = "0.1.5", path = "../dnapi-rs" } [build-dependencies] serde = { version = "1.0.157", features = ["derive"] } diff --git a/tfclient/src/apiworker.rs b/tfclient/src/apiworker.rs index 42c47cb..3d02828 100644 --- a/tfclient/src/apiworker.rs +++ b/tfclient/src/apiworker.rs @@ -1,14 +1,17 @@ +use std::fs; use std::sync::mpsc::{Receiver, TryRecvError}; use base64::Engine; use chrono::Local; use log::{error, info, warn}; use url::Url; +use dnapi_rs::client_blocking::Client; use trifid_pki::cert::{serialize_ed25519_public, serialize_x25519_public}; use trifid_pki::ed25519_dalek::{SecretKey, SigningKey}; use trifid_pki::rand_core::OsRng; use trifid_pki::x25519_dalek::StaticSecret; use crate::config::{load_cdata, save_cdata, TFClientConfig}; use crate::daemon::ThreadMessageSender; +use crate::dirs::get_nebulaconfig_file; pub enum APIWorkerMessage { Shutdown, @@ -19,6 +22,8 @@ pub enum APIWorkerMessage { pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _transmitters: ThreadMessageSender, rx: Receiver) { let server = Url::parse(&url).unwrap(); + let client = Client::new(format!("tfclient/{}", env!("CARGO_PKG_VERSION")), server).unwrap(); + loop { match rx.try_recv() { Ok(msg) => { @@ -37,7 +42,7 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr return; } }; - if cdata.host_id.is_none() { + if cdata.creds.is_none() { info!("not enrolled, cannot perform config update"); continue; } @@ -53,63 +58,30 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr return; } }; - if cdata. { + if cdata.creds.is_some() { warn!("enrollment failed: already enrolled"); continue; } - let dh_encoded = base64::engine::general_purpose::STANDARD.encode(serialize_x25519_public(&dh_key.to_bytes())); - let ed_encoded = base64::engine::general_purpose::STANDARD.encode(serialize_ed25519_public(&ed_key.to_bytes())); - let req = EnrollRequest { - code, - dh_pubkey: dh_encoded, - ed_pubkey: ed_encoded, - timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(), - }; - let res = match enroll(&server, &req) { - Ok(res) => res, - Err(e) => { - error!("error in api worker thread: {}", e); - error!("APIWorker exiting with error"); - return; - } - }; - let resp = match res { - APIResponse::Error(e) => { - error!("error with enrollment: {}: {}", e.errors[0].code, e.errors[0].message); - continue; - } - APIResponse::Success(resp) => resp - }; - info!("Enrolled with server. Host-ID {} config count {}", resp.data.host_id, resp.data.counter); - info!("KeyPool {}, org {} {}", resp.data.trusted_keys, resp.data.organization.name, resp.data.organization.id); - info!("Config: {}", resp.data.config); - - // Decode the CAPool and config - let key_pool_pem = match base64::engine::general_purpose::STANDARD.decode(resp.data.trusted_keys) { - Ok(p) => p, + let (config, dh_privkey, creds, meta) = match client.enroll(&code) { + Ok(resp) => resp, Err(e) => { - error!("error with enrollment: {}", e); + error!("error with enrollment request: {}", e); continue; } }; - let config = match base64::engine::general_purpose::STANDARD.decode(resp.data.config) { - Ok(p) => p, + match fs::write(get_nebulaconfig_file(&instance).expect("Unable to determine nebula config file location"), config) { + Ok(_) => (), Err(e) => { - error!("error with enrollment: {}", e); + error!("unable to save nebula config: {}", e); continue; } - }; - let config_str = String::from_utf8(config).unwrap(); + } - - cdata.host_id = Some(resp.data.host_id); - cdata.counter = resp.data.counter as i32; - cdata.key_pool = Some(String::from_utf8(key_pool_pem).unwrap()); - cdata.org_name = Some(resp.data.organization.name); - cdata.org_id = Some(resp.data.organization.id); - cdata.config = Some(config_str); + cdata.creds = Some(creds); + cdata.dh_privkey = Some(dh_privkey.try_into().expect("32 != 32")); + cdata.meta = Some(meta); // Save vardata match save_cdata(&instance, cdata) { @@ -120,6 +92,8 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr return; } } + + info!("Configuration updated. Sending signal to Nebula worker thread"); } } }, diff --git a/tfclient/src/config.rs b/tfclient/src/config.rs index bda7a22..c3b6adb 100644 --- a/tfclient/src/config.rs +++ b/tfclient/src/config.rs @@ -5,6 +5,7 @@ use std::fs; use log::{debug, info}; use serde::{Deserialize, Serialize}; +use dnapi_rs::client_blocking::EnrollMeta; use dnapi_rs::credentials::Credentials; use crate::dirs::{get_cdata_dir, get_cdata_file, get_config_dir, get_config_file}; @@ -21,7 +22,8 @@ pub struct TFClientConfig { #[derive(Serialize, Deserialize, Clone)] pub struct TFClientData { pub dh_privkey: Option<[u8; 32]>, - pub creds: Option + pub creds: Option, + pub meta: Option } pub fn create_config(instance: &str) -> Result<(), Box> { @@ -56,7 +58,7 @@ pub fn create_cdata(instance: &str) -> Result<(), Box> { info!("Creating data directory..."); fs::create_dir_all(get_cdata_dir(instance).ok_or("Unable to load data dir")?)?; info!("Copying default data file to config directory..."); - let config = TFClientData { host_id: None, ed_privkey: None, dh_privkey: None, counter: 0, key_pool: None, org_id: None, org_name: None, config: None }; + let config = TFClientData { dh_privkey: None, creds: None, meta: None }; let config_str = toml::to_string(&config)?; fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?; Ok(()) diff --git a/tfclient/src/dirs.rs b/tfclient/src/dirs.rs index d7dd0c3..0010aa1 100644 --- a/tfclient/src/dirs.rs +++ b/tfclient/src/dirs.rs @@ -18,4 +18,8 @@ pub fn get_cdata_dir(instance: &str) -> Option { pub fn get_cdata_file(instance: &str) -> Option { get_cdata_dir(instance).map(|f| f.join("tfclient.toml")) +} + +pub fn get_nebulaconfig_file(instance: &str) -> Option { + get_cdata_dir(instance).map(|f| f.join("nebula.sk_embedded.yml")) } \ No newline at end of file diff --git a/tfclient/src/nebulaworker.rs b/tfclient/src/nebulaworker.rs index 3bef8e6..d54df49 100644 --- a/tfclient/src/nebulaworker.rs +++ b/tfclient/src/nebulaworker.rs @@ -6,7 +6,8 @@ use crate::config::TFClientConfig; use crate::daemon::ThreadMessageSender; pub enum NebulaWorkerMessage { - Shutdown + Shutdown, + ConfigUpdated } pub fn nebulaworker_main(_config: TFClientConfig, _transmitter: ThreadMessageSender, rx: Receiver) { @@ -17,6 +18,9 @@ pub fn nebulaworker_main(_config: TFClientConfig, _transmitter: ThreadMessageSen NebulaWorkerMessage::Shutdown => { info!("recv on command socket: shutdown, stopping"); break; + }, + NebulaWorkerMessage::ConfigUpdated => { + info!("our configuration has been updated - reloading"); } } }, diff --git a/tfclient/src/socketworker.rs b/tfclient/src/socketworker.rs index 21e0d6d..a87ec21 100644 --- a/tfclient/src/socketworker.rs +++ b/tfclient/src/socketworker.rs @@ -232,8 +232,8 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm } }; client.stream.write_all(&ctob(JsonMessage::HostID { - has_id: data.host_id.is_some(), - id: data.host_id + has_id: data.creds.is_some(), + id: data.creds.map(|c| c.host_id) }))?; },