start porting dnapi

This commit is contained in:
c0repwn3r 2023-03-29 09:37:03 -04:00
parent 05d452bc50
commit 26ef187ff3
Signed by: core
GPG Key ID: FDBF740DADDCEECF
9 changed files with 241 additions and 59 deletions

11
Cargo.lock generated
View File

@ -154,6 +154,16 @@ version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "base64-serde"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba368df5de76a5bea49aaf0cf1b39ccfbbef176924d1ba5db3e4135216cbe3c7"
dependencies = [
"base64 0.21.0",
"serde",
]
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.5.3" version = "1.5.3"
@ -2421,6 +2431,7 @@ name = "tfclient"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.21.0",
"base64-serde",
"chrono", "chrono",
"clap", "clap",
"ctrlc", "ctrlc",

View File

@ -23,7 +23,7 @@ reqwest = { version = "0.11.16", features = ["blocking"] }
base64 = "0.21.0" 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"
[build-dependencies] [build-dependencies]
serde = { version = "1.0.157", features = ["derive"] } serde = { version = "1.0.157", features = ["derive"] }

View File

@ -1,61 +1,13 @@
use std::error::Error; use std::error::Error;
use base64_serde::base64_serde_type;
use log::trace; use log::trace;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use url::Url; use url::Url;
#[derive(Serialize, Deserialize)] const ENDPOINT_V1: &str = "/v1/dnclient";
pub struct EnrollRequest {
pub code: String,
#[serde(rename = "dhPubkey")]
pub dh_pubkey: String,
#[serde(rename = "edPubkey")]
pub ed_pubkey: String,
pub timestamp: String,
}
#[derive(Serialize, Deserialize)] base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
pub struct EnrollResponseMetadata {}
#[derive(Serialize, Deserialize)]
pub struct EnrollResponseOrganization {
pub id: String,
pub name: String,
}
#[derive(Serialize, Deserialize)]
pub struct EnrollResponseData {
pub config: String,
#[serde(rename = "hostID")]
pub host_id: String,
pub counter: i64,
#[serde(rename = "trustedKeys")]
pub trusted_keys: String,
pub organization: EnrollResponseOrganization,
}
#[derive(Serialize, Deserialize)]
pub struct EnrollResponse {
pub data: EnrollResponseData,
pub metadata: EnrollResponseMetadata,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum APIResponse {
Error(EnrollError),
Success(EnrollResponse)
}
#[derive(Serialize, Deserialize)]
pub struct EnrollError {
pub errors: Vec<EnrollErrorSingular>
}
#[derive(Serialize, Deserialize)]
pub struct EnrollErrorSingular {
pub code: String,
pub message: String
}
pub fn enroll(server: &Url, request: &EnrollRequest) -> Result<APIResponse, Box<dyn Error>> { pub fn enroll(server: &Url, request: &EnrollRequest) -> Result<APIResponse, Box<dyn Error>> {
let endpoint = server.join("/v2/enroll")?; let endpoint = server.join("/v2/enroll")?;
@ -68,3 +20,126 @@ pub fn enroll(server: &Url, request: &EnrollRequest) -> Result<APIResponse, Box<
let resp = client.post(endpoint).body(text).send()?; let resp = client.post(endpoint).body(text).send()?;
Ok(resp.json()?) Ok(resp.json()?)
} }
#[derive(Serialize, Deserialize)]
pub struct RequestV1 {
pub version: i32,
#[serde(rename = "hostID")]
pub host_id: String,
pub counter: u32,
pub message: String,
#[serde(with = "Base64Standard")]
pub signature: Vec<u8>
}
#[derive(Serialize, Deserialize)]
pub struct RequestWrapper {
#[serde(rename = "type")]
pub message_type: String,
#[serde(with = "Base64Standard")]
pub value: Vec<u8>,
pub timestamp: String
}
#[derive(Serialize, Deserialize)]
pub struct SignedResponseWrapper {
pub data: SignedResponse
}
#[derive(Serialize, Deserialize)]
pub struct SignedResponse {
pub version: i32,
#[serde(with = "Base64Standard")]
pub message: Vec<u8>,
#[serde(with = "Base64Standard")]
pub signature: Vec<u8>
}
#[derive(Serialize, Deserialize)]
pub struct CheckForUpdateResponseWrapper {
pub data: CheckForUpdateResponse
}
#[derive(Serialize, Deserialize)]
pub struct CheckForUpdateResponse {
#[serde(rename = "updateAvailable")]
pub update_available: bool
}
#[derive(Serialize, Deserialize)]
pub struct DoUpdateRequest {
#[serde(rename = "edPubkeyPEM")]
#[serde(with = "Base64Standard")]
pub ed_pubkey_pem: Vec<u8>,
#[serde(rename = "dhPubkeyPEM")]
#[serde(with = "Base64Standard")]
pub dh_pubkey_pem: Vec<u8>,
#[serde(with = "Base64Standard")]
pub nonce: Vec<u8>
}
#[derive(Serialize, Deserialize)]
pub struct DoUpdateResponse {
#[serde(with = "Base64Standard")]
pub config: Vec<u8>,
pub counter: u32,
#[serde(with = "Base64Standard")]
pub nonce: Vec<u8>,
#[serde(rename = "trustedKeys")]
#[serde(with = "Base64Standard")]
pub trusted_keys: Vec<u8>
}
const ENROLL_ENDPOINT: &str = "/v2/enroll";
#[derive(Serialize, Deserialize)]
pub struct EnrollRequest {
pub code: String,
#[serde(rename = "dhPubkey")]
#[serde(with = "Base64Standard")]
pub dh_pubkey: Vec<u8>,
#[serde(rename = "edPubkey")]
#[serde(with = "Base64Standard")]
pub ed_pubkey: Vec<u8>,
pub timestamp: String
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum EnrollResponse {
Success {
data: EnrollResponseData
},
Error {
errors: APIErrors
}
}
#[derive(Serialize, Deserialize)]
pub struct EnrollResponseData {
#[serde(with = "Base64Standard")]
pub config: Vec<u8>,
#[serde(rename = "hostID")]
pub host_id: String,
pub counter: u32,
#[serde(rename = "trustedKeys")]
#[serde(with = "Base64Standard")]
pub trusted_keys: Vec<u8>,
pub organization: EnrollResponseDataOrg
}
#[derive(Serialize, Deserialize)]
pub struct EnrollResponseDataOrg {
pub id: String,
pub name: String
}
#[derive(Serialize, Deserialize)]
pub struct APIError {
pub code: String,
pub message: String,
pub path: Option<String>
}
pub type APIErrors = Vec<APIError>;

View File

@ -3,7 +3,6 @@ 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 trifid_pki::ca::NebulaCAPool;
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;
@ -14,7 +13,8 @@ use crate::daemon::ThreadMessageSender;
pub enum APIWorkerMessage { pub enum APIWorkerMessage {
Shutdown, Shutdown,
Enroll { code: String } Enroll { code: String },
Timer
} }
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>) {
@ -68,6 +68,22 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _tr
info!("recv on command socket: shutdown, stopping"); info!("recv on command socket: shutdown, stopping");
break; break;
}, },
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;
}
},
APIWorkerMessage::Enroll { code } => { APIWorkerMessage::Enroll { code } => {
info!("recv on command socket: enroll {}", code); info!("recv on command socket: enroll {}", code);
let mut cdata = match load_cdata(&instance) { let mut cdata = match load_cdata(&instance) {

View File

@ -8,6 +8,7 @@ use crate::config::load_config;
use crate::main; use crate::main;
use crate::nebulaworker::{nebulaworker_main, NebulaWorkerMessage}; use crate::nebulaworker::{nebulaworker_main, NebulaWorkerMessage};
use crate::socketworker::{socketworker_main, SocketWorkerMessage}; use crate::socketworker::{socketworker_main, SocketWorkerMessage};
use crate::timerworker::{timer_main, TimerWorkerMessage};
use crate::util::check_server_url; use crate::util::check_server_url;
pub fn daemon_main(name: String, server: String) { pub fn daemon_main(name: String, server: String) {
@ -28,11 +29,13 @@ pub fn daemon_main(name: String, server: String) {
let (tx_api, rx_api) = mpsc::channel::<APIWorkerMessage>(); let (tx_api, rx_api) = mpsc::channel::<APIWorkerMessage>();
let (tx_socket, rx_socket) = mpsc::channel::<SocketWorkerMessage>(); let (tx_socket, rx_socket) = mpsc::channel::<SocketWorkerMessage>();
let (tx_nebula, rx_nebula) = mpsc::channel::<NebulaWorkerMessage>(); let (tx_nebula, rx_nebula) = mpsc::channel::<NebulaWorkerMessage>();
let (tx_timer, rx_timer) = mpsc::channel::<TimerWorkerMessage>();
let transmitter = ThreadMessageSender { let transmitter = ThreadMessageSender {
socket_thread: tx_socket, socket_thread: tx_socket,
api_thread: tx_api, api_thread: tx_api,
nebula_thread: tx_nebula nebula_thread: tx_nebula,
timer_thread: tx_timer,
}; };
let mainthread_transmitter = transmitter.clone(); let mainthread_transmitter = transmitter.clone();
@ -59,6 +62,12 @@ pub fn daemon_main(name: String, server: String) {
error!("Error sending shutdown message to socket worker thread: {}", e); error!("Error sending shutdown message to socket worker thread: {}", e);
} }
} }
match mainthread_transmitter.timer_thread.send(TimerWorkerMessage::Shutdown) {
Ok(_) => (),
Err(e) => {
error!("Error sending shutdown message to timer worker thread: {}", e);
}
}
}) { }) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
@ -69,8 +78,6 @@ pub fn daemon_main(name: String, server: String) {
info!("Starting API thread..."); info!("Starting API thread...");
let config_api = config.clone(); let config_api = config.clone();
let transmitter_api = transmitter.clone(); let transmitter_api = transmitter.clone();
let name_api = name.clone(); let name_api = name.clone();
@ -86,6 +93,12 @@ pub fn daemon_main(name: String, server: String) {
nebulaworker_main(config_nebula, transmitter_nebula, rx_nebula); nebulaworker_main(config_nebula, transmitter_nebula, rx_nebula);
}); });
info!("Starting timer thread...");
let timer_transmitter = transmitter.clone();
let timer_thread = thread::spawn(move || {
timer_main(timer_transmitter, rx_timer);
});
info!("Starting socket worker thread..."); info!("Starting socket worker thread...");
let socket_thread = thread::spawn(move || { let socket_thread = thread::spawn(move || {
socketworker_main(config, name.clone(), transmitter, rx_socket); socketworker_main(config, name.clone(), transmitter, rx_socket);
@ -111,6 +124,16 @@ pub fn daemon_main(name: String, server: String) {
} }
info!("API thread exited"); info!("API thread exited");
info!("Waiting for timer thread to exit...");
match timer_thread.join() {
Ok(_) => (),
Err(_) => {
error!("Error waiting for timer thread to exit.");
std::process::exit(1);
}
}
info!("Timer thread exited");
info!("Waiting for Nebula thread to exit..."); info!("Waiting for Nebula thread to exit...");
match nebula_thread.join() { match nebula_thread.join() {
Ok(_) => (), Ok(_) => (),
@ -128,5 +151,6 @@ pub fn daemon_main(name: String, server: String) {
pub struct ThreadMessageSender { pub struct ThreadMessageSender {
pub socket_thread: Sender<SocketWorkerMessage>, pub socket_thread: Sender<SocketWorkerMessage>,
pub api_thread: Sender<APIWorkerMessage>, pub api_thread: Sender<APIWorkerMessage>,
pub nebula_thread: Sender<NebulaWorkerMessage> pub nebula_thread: Sender<NebulaWorkerMessage>,
pub timer_thread: Sender<TimerWorkerMessage>
} }

View File

@ -0,0 +1 @@
/// Essentially a direct port of https://github.com/DefinedNet/dnapi

View File

@ -25,6 +25,7 @@ pub mod apiworker;
pub mod socketworker; pub mod socketworker;
pub mod api; pub mod api;
pub mod socketclient; pub mod socketclient;
pub mod timerworker;
pub mod nebula_bin { pub mod nebula_bin {
include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs")); include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs"));

View File

@ -12,6 +12,7 @@ use crate::apiworker::APIWorkerMessage;
use crate::config::{load_cdata, TFClientConfig}; use crate::config::{load_cdata, TFClientConfig};
use crate::daemon::ThreadMessageSender; use crate::daemon::ThreadMessageSender;
use crate::nebulaworker::NebulaWorkerMessage; use crate::nebulaworker::NebulaWorkerMessage;
use crate::timerworker::TimerWorkerMessage;
pub enum SocketWorkerMessage { pub enum SocketWorkerMessage {
Shutdown Shutdown
@ -214,6 +215,12 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm
error!("Error sending shutdown message to socket worker thread: {}", e); error!("Error sending shutdown message to socket worker thread: {}", e);
} }
} }
match transmitter.timer_thread.send(TimerWorkerMessage::Shutdown) {
Ok(_) => (),
Err(e) => {
error!("Error sending shutdown message to timer worker thread: {}", e);
}
}
}, },
JsonMessage::GetHostID {} => { JsonMessage::GetHostID {} => {

View File

@ -0,0 +1,47 @@
use std::ops::Add;
use std::sync::mpsc::{Receiver, TryRecvError};
use std::time::{Duration, SystemTime};
use log::{error, info};
use crate::apiworker::APIWorkerMessage;
use crate::daemon::ThreadMessageSender;
pub enum TimerWorkerMessage {
Shutdown
}
pub fn timer_main(tx: ThreadMessageSender, rx: Receiver<TimerWorkerMessage>) {
let mut api_reload_timer = SystemTime::now().add(Duration::from_secs(60));
loop {
match rx.try_recv() {
Ok(msg) => {
match msg {
TimerWorkerMessage::Shutdown => {
info!("recv on command socket: shutdown, stopping");
break;
}
}
},
Err(e) => {
match e {
TryRecvError::Empty => {}
TryRecvError::Disconnected => {
error!("timerworker command socket disconnected, shutting down to prevent orphaning");
break;
}
}
}
}
if SystemTime::now().gt(&api_reload_timer) {
info!("Timer triggered: API_RELOAD_TIMER");
api_reload_timer = SystemTime::now().add(Duration::from_secs(60));
match tx.api_thread.send(APIWorkerMessage::Timer) {
Ok(_) => (),
Err(e) => {
error!("Error sending timer message to api worker thread: {}", e);
}
}
}
}
}