dnapi-rs - make EnrollMeta cloneable
tfclient - finish enrollment routine
This commit is contained in:
parent
a77e126319
commit
bd76d760f4
|
@ -622,7 +622,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dnapi-rs"
|
name = "dnapi-rs"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"base64-serde",
|
"base64-serde",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "dnapi-rs"
|
name = "dnapi-rs"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A rust client for the Defined Networking API"
|
description = "A rust client for the Defined Networking API"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
@ -21,9 +21,4 @@ base64 = "0.21.0"
|
||||||
serde_json = "1.0.95"
|
serde_json = "1.0.95"
|
||||||
trifid-pki = { version = "0.1.6", path = "../trifid-pki", features = ["serde_derive"] }
|
trifid-pki = { version = "0.1.6", path = "../trifid-pki", features = ["serde_derive"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["blocking"]
|
|
||||||
blocking = ["reqwest/blocking"]
|
|
||||||
async = []
|
|
|
@ -24,7 +24,7 @@ pub struct Client {
|
||||||
server_url: Url
|
server_url: Url
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
/// A struct containing organization metadata returned as a result of enrollment
|
/// A struct containing organization metadata returned as a result of enrollment
|
||||||
pub struct EnrollMeta {
|
pub struct EnrollMeta {
|
||||||
/// The server organization ID this node is now a member of
|
/// The server organization ID this node is now a member of
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub struct Client {
|
||||||
server_url: Url
|
server_url: Url
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
/// A struct containing organization metadata returned as a result of enrollment
|
/// A struct containing organization metadata returned as a result of enrollment
|
||||||
pub struct EnrollMeta {
|
pub struct EnrollMeta {
|
||||||
/// The server organization ID this node is now a member of
|
/// The server organization ID this node is now a member of
|
||||||
|
|
|
@ -15,18 +15,10 @@
|
||||||
#![allow(clippy::too_many_lines)]
|
#![allow(clippy::too_many_lines)]
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![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;
|
pub mod message;
|
||||||
|
|
||||||
#[cfg(feature = "blocking")]
|
pub mod client_blocking;
|
||||||
#[path = "client_blocking.rs"]
|
pub mod client_async;
|
||||||
pub mod client;
|
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
|
||||||
#[path = "client_async.rs"]
|
|
||||||
pub mod client;
|
|
||||||
|
|
||||||
pub mod credentials;
|
pub mod credentials;
|
||||||
pub mod crypto;
|
pub mod crypto;
|
|
@ -24,7 +24,7 @@ base64 = "0.21.0"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
ipnet = "2.7.1"
|
ipnet = "2.7.1"
|
||||||
base64-serde = "0.7.0"
|
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]
|
[build-dependencies]
|
||||||
serde = { version = "1.0.157", features = ["derive"] }
|
serde = { version = "1.0.157", features = ["derive"] }
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
use std::fs;
|
||||||
use std::sync::mpsc::{Receiver, TryRecvError};
|
use std::sync::mpsc::{Receiver, TryRecvError};
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use dnapi_rs::client_blocking::Client;
|
||||||
use trifid_pki::cert::{serialize_ed25519_public, serialize_x25519_public};
|
use trifid_pki::cert::{serialize_ed25519_public, serialize_x25519_public};
|
||||||
use trifid_pki::ed25519_dalek::{SecretKey, SigningKey};
|
use trifid_pki::ed25519_dalek::{SecretKey, SigningKey};
|
||||||
use trifid_pki::rand_core::OsRng;
|
use trifid_pki::rand_core::OsRng;
|
||||||
use trifid_pki::x25519_dalek::StaticSecret;
|
use trifid_pki::x25519_dalek::StaticSecret;
|
||||||
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
||||||
use crate::daemon::ThreadMessageSender;
|
use crate::daemon::ThreadMessageSender;
|
||||||
|
use crate::dirs::get_nebulaconfig_file;
|
||||||
|
|
||||||
pub enum APIWorkerMessage {
|
pub enum APIWorkerMessage {
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
@ -19,6 +22,8 @@ pub enum APIWorkerMessage {
|
||||||
pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _transmitters: ThreadMessageSender, rx: Receiver<APIWorkerMessage>) {
|
pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _transmitters: ThreadMessageSender, rx: Receiver<APIWorkerMessage>) {
|
||||||
let server = Url::parse(&url).unwrap();
|
let server = Url::parse(&url).unwrap();
|
||||||
|
|
||||||
|
let client = Client::new(format!("tfclient/{}", env!("CARGO_PKG_VERSION")), server).unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match rx.try_recv() {
|
match rx.try_recv() {
|
||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
|
@ -37,7 +42,7 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if cdata.host_id.is_none() {
|
if cdata.creds.is_none() {
|
||||||
info!("not enrolled, cannot perform config update");
|
info!("not enrolled, cannot perform config update");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -53,63 +58,30 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if cdata. {
|
if cdata.creds.is_some() {
|
||||||
warn!("enrollment failed: already enrolled");
|
warn!("enrollment failed: already enrolled");
|
||||||
continue;
|
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);
|
let (config, dh_privkey, creds, meta) = match client.enroll(&code) {
|
||||||
info!("KeyPool {}, org {} {}", resp.data.trusted_keys, resp.data.organization.name, resp.data.organization.id);
|
Ok(resp) => resp,
|
||||||
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,
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("error with enrollment: {}", e);
|
error!("error with enrollment request: {}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = match base64::engine::general_purpose::STANDARD.decode(resp.data.config) {
|
match fs::write(get_nebulaconfig_file(&instance).expect("Unable to determine nebula config file location"), config) {
|
||||||
Ok(p) => p,
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("error with enrollment: {}", e);
|
error!("unable to save nebula config: {}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
let config_str = String::from_utf8(config).unwrap();
|
|
||||||
|
|
||||||
|
cdata.creds = Some(creds);
|
||||||
cdata.host_id = Some(resp.data.host_id);
|
cdata.dh_privkey = Some(dh_privkey.try_into().expect("32 != 32"));
|
||||||
cdata.counter = resp.data.counter as i32;
|
cdata.meta = Some(meta);
|
||||||
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);
|
|
||||||
|
|
||||||
// Save vardata
|
// Save vardata
|
||||||
match save_cdata(&instance, cdata) {
|
match save_cdata(&instance, cdata) {
|
||||||
|
@ -120,6 +92,8 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("Configuration updated. Sending signal to Nebula worker thread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::fs;
|
||||||
|
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use dnapi_rs::client_blocking::EnrollMeta;
|
||||||
use dnapi_rs::credentials::Credentials;
|
use dnapi_rs::credentials::Credentials;
|
||||||
|
|
||||||
use crate::dirs::{get_cdata_dir, get_cdata_file, get_config_dir, get_config_file};
|
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)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct TFClientData {
|
pub struct TFClientData {
|
||||||
pub dh_privkey: Option<[u8; 32]>,
|
pub dh_privkey: Option<[u8; 32]>,
|
||||||
pub creds: Option<Credentials>
|
pub creds: Option<Credentials>,
|
||||||
|
pub meta: Option<EnrollMeta>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_config(instance: &str) -> Result<(), Box<dyn Error>> {
|
pub fn create_config(instance: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
@ -56,7 +58,7 @@ pub fn create_cdata(instance: &str) -> Result<(), Box<dyn Error>> {
|
||||||
info!("Creating data directory...");
|
info!("Creating data directory...");
|
||||||
fs::create_dir_all(get_cdata_dir(instance).ok_or("Unable to load data dir")?)?;
|
fs::create_dir_all(get_cdata_dir(instance).ok_or("Unable to load data dir")?)?;
|
||||||
info!("Copying default data file to config directory...");
|
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)?;
|
let config_str = toml::to_string(&config)?;
|
||||||
fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?;
|
fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -18,4 +18,8 @@ pub fn get_cdata_dir(instance: &str) -> Option<PathBuf> {
|
||||||
|
|
||||||
pub fn get_cdata_file(instance: &str) -> Option<PathBuf> {
|
pub fn get_cdata_file(instance: &str) -> Option<PathBuf> {
|
||||||
get_cdata_dir(instance).map(|f| f.join("tfclient.toml"))
|
get_cdata_dir(instance).map(|f| f.join("tfclient.toml"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nebulaconfig_file(instance: &str) -> Option<PathBuf> {
|
||||||
|
get_cdata_dir(instance).map(|f| f.join("nebula.sk_embedded.yml"))
|
||||||
}
|
}
|
|
@ -6,7 +6,8 @@ use crate::config::TFClientConfig;
|
||||||
use crate::daemon::ThreadMessageSender;
|
use crate::daemon::ThreadMessageSender;
|
||||||
|
|
||||||
pub enum NebulaWorkerMessage {
|
pub enum NebulaWorkerMessage {
|
||||||
Shutdown
|
Shutdown,
|
||||||
|
ConfigUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nebulaworker_main(_config: TFClientConfig, _transmitter: ThreadMessageSender, rx: Receiver<NebulaWorkerMessage>) {
|
pub fn nebulaworker_main(_config: TFClientConfig, _transmitter: ThreadMessageSender, rx: Receiver<NebulaWorkerMessage>) {
|
||||||
|
@ -17,6 +18,9 @@ pub fn nebulaworker_main(_config: TFClientConfig, _transmitter: ThreadMessageSen
|
||||||
NebulaWorkerMessage::Shutdown => {
|
NebulaWorkerMessage::Shutdown => {
|
||||||
info!("recv on command socket: shutdown, stopping");
|
info!("recv on command socket: shutdown, stopping");
|
||||||
break;
|
break;
|
||||||
|
},
|
||||||
|
NebulaWorkerMessage::ConfigUpdated => {
|
||||||
|
info!("our configuration has been updated - reloading");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -232,8 +232,8 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
client.stream.write_all(&ctob(JsonMessage::HostID {
|
client.stream.write_all(&ctob(JsonMessage::HostID {
|
||||||
has_id: data.host_id.is_some(),
|
has_id: data.creds.is_some(),
|
||||||
id: data.host_id
|
id: data.creds.map(|c| c.host_id)
|
||||||
}))?;
|
}))?;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue