2023-03-23 17:17:37 +00:00
|
|
|
use std::sync::mpsc::{Receiver, TryRecvError};
|
2023-03-28 16:16:00 +00:00
|
|
|
use base64::Engine;
|
|
|
|
use chrono::Local;
|
|
|
|
use log::{error, info, warn};
|
|
|
|
use url::Url;
|
|
|
|
use trifid_pki::cert::{serialize_ed25519_public, serialize_x25519_public};
|
2023-03-27 16:32:26 +00:00
|
|
|
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};
|
2023-03-22 18:34:06 +00:00
|
|
|
use crate::daemon::ThreadMessageSender;
|
|
|
|
|
|
|
|
pub enum APIWorkerMessage {
|
2023-03-28 16:16:00 +00:00
|
|
|
Shutdown,
|
2023-03-29 13:37:03 +00:00
|
|
|
Enroll { code: String },
|
|
|
|
Timer
|
2023-03-22 18:34:06 +00:00
|
|
|
}
|
|
|
|
|
2023-03-28 16:16:00 +00:00
|
|
|
pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _transmitters: ThreadMessageSender, rx: Receiver<APIWorkerMessage>) {
|
|
|
|
let server = Url::parse(&url).unwrap();
|
|
|
|
|
2023-03-27 16:32:26 +00:00
|
|
|
// Generate dhPubkey and edPubkey if it doesn't exist
|
|
|
|
// Load vardata
|
|
|
|
let mut vdata = match load_cdata(&instance) {
|
|
|
|
Ok(d) => d,
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error loading vdata: {}", e);
|
|
|
|
error!("APIWorker exiting with error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if vdata.ed_privkey.is_none() {
|
|
|
|
info!("Generating ed25519 key");
|
|
|
|
let mut csprng = OsRng;
|
|
|
|
let key = SigningKey::generate(&mut csprng);
|
|
|
|
let ed_key_bytes = key.to_bytes().to_vec();
|
|
|
|
vdata.ed_privkey = Some(ed_key_bytes.try_into().unwrap());
|
|
|
|
}
|
|
|
|
if vdata.dh_privkey.is_none() {
|
|
|
|
info!("Generating ecdh key");
|
|
|
|
let mut csprng = OsRng;
|
|
|
|
let key = StaticSecret::new(&mut csprng);
|
|
|
|
let dh_key_bytes = key.to_bytes();
|
|
|
|
vdata.dh_privkey = Some(dh_key_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
info!("Loading keys");
|
|
|
|
let ed_key = SigningKey::from_bytes(&SecretKey::from(vdata.ed_privkey.unwrap()));
|
|
|
|
let dh_key = StaticSecret::from(vdata.dh_privkey.unwrap());
|
|
|
|
info!("Keys loaded successfully");
|
|
|
|
|
|
|
|
// Save vardata
|
|
|
|
match save_cdata(&instance, vdata) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error saving vdata: {}", e);
|
|
|
|
error!("APIWorker exiting with error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-03-23 17:17:37 +00:00
|
|
|
loop {
|
|
|
|
match rx.try_recv() {
|
|
|
|
Ok(msg) => {
|
|
|
|
match msg {
|
|
|
|
APIWorkerMessage::Shutdown => {
|
|
|
|
info!("recv on command socket: shutdown, stopping");
|
|
|
|
break;
|
2023-03-28 16:16:00 +00:00
|
|
|
},
|
2023-03-29 13:37:03 +00:00
|
|
|
APIWorkerMessage::Timer => {
|
|
|
|
info!("updating config");
|
|
|
|
let mut cdata = match load_cdata(&instance) {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(e) => {
|
|
|
|
error!("error in api worker thread: {}", e);
|
|
|
|
error!("APIWorker exiting with error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if cdata.host_id.is_none() {
|
|
|
|
info!("not enrolled, cannot perform config update");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
2023-03-28 16:16:00 +00:00
|
|
|
APIWorkerMessage::Enroll { code } => {
|
|
|
|
info!("recv on command socket: enroll {}", code);
|
|
|
|
let mut cdata = match load_cdata(&instance) {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(e) => {
|
|
|
|
error!("error in api worker thread: {}", e);
|
|
|
|
error!("APIWorker exiting with error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if cdata.host_id.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);
|
2023-03-28 17:10:11 +00:00
|
|
|
info!("KeyPool {}, org {} {}", resp.data.trusted_keys, resp.data.organization.name, resp.data.organization.id);
|
2023-03-28 16:16:00 +00:00
|
|
|
info!("Config: {}", resp.data.config);
|
|
|
|
|
2023-03-28 17:10:11 +00:00
|
|
|
// 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) => {
|
|
|
|
error!("error with enrollment: {}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let config = match base64::engine::general_purpose::STANDARD.decode(resp.data.config) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(e) => {
|
|
|
|
error!("error with enrollment: {}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let config_str = String::from_utf8(config).unwrap();
|
|
|
|
|
|
|
|
|
2023-03-28 16:16:00 +00:00
|
|
|
cdata.host_id = Some(resp.data.host_id);
|
|
|
|
cdata.counter = resp.data.counter as i32;
|
2023-03-28 17:10:11 +00:00
|
|
|
cdata.key_pool = Some(String::from_utf8(key_pool_pem).unwrap());
|
2023-03-28 16:16:00 +00:00
|
|
|
cdata.org_name = Some(resp.data.organization.name);
|
|
|
|
cdata.org_id = Some(resp.data.organization.id);
|
2023-03-28 17:10:11 +00:00
|
|
|
cdata.config = Some(config_str);
|
2023-03-28 16:16:00 +00:00
|
|
|
|
|
|
|
// Save vardata
|
|
|
|
match save_cdata(&instance, cdata) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error saving cdata: {}", e);
|
|
|
|
error!("APIWorker exiting with error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-03-23 17:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
match e {
|
|
|
|
TryRecvError::Empty => {}
|
|
|
|
TryRecvError::Disconnected => {
|
|
|
|
error!("apiworker command socket disconnected, shutting down to prevent orphaning");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-22 18:34:06 +00:00
|
|
|
}
|