tfclient enrollment successful
This commit is contained in:
parent
8a607733a3
commit
b291d47459
|
@ -219,9 +219,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.23"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
|
@ -1856,9 +1856,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
|||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.14"
|
||||
version = "0.11.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
|
||||
checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"bytes",
|
||||
|
@ -2420,6 +2420,8 @@ dependencies = [
|
|||
name = "tfclient"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"dirs 5.0.0",
|
||||
|
|
|
@ -7,6 +7,9 @@ Connection: close
|
|||
|
||||
{"code":"xM22QsIzd4F0nLDTbh86RCSYwelfU_Hshqt-7u4yy_Y","dhPubkey":"LS0tLS1CRUdJTiBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQpqZW9aaDZZYUNNSHZKK04zWGRlQ1hCbHo3dm5saTBjL1NlQ1hVR3lYbEIwPQotLS0tLUVORCBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQo=","edPubkey":"LS0tLS1CRUdJTiBORUJVTEEgRUQyNTUxOSBQVUJMSUMgS0VZLS0tLS0KWHE0RG9mUGJoQzBubjc4VEhRWUxhNC83V1Ixei9iU1kzSm9pRzNRZ1VMcz0KLS0tLS1FTkQgTkVCVUxBIEVEMjU1MTkgUFVCTElDIEtFWS0tLS0tCg==","timestamp":"2023-02-01T13:24:56.380006369-05:00"}
|
||||
|
||||
2023-02-01T13:24:56.380006369-05:00
|
||||
%Y-%m-%dT%H:%M:%S.%f-%:z
|
||||
|
||||
HTTP/2 200 OK
|
||||
Cache-Control: no-store
|
||||
Content-Security-Policy: default-src 'none'
|
||||
|
|
|
@ -19,6 +19,9 @@ toml = "0.7.3"
|
|||
serde = { version = "1.0.158", features = ["derive"] }
|
||||
serde_json = "1.0.94"
|
||||
ctrlc = "3.2.5"
|
||||
reqwest = { version = "0.11.16", features = ["blocking"] }
|
||||
base64 = "0.21.0"
|
||||
chrono = "0.4.24"
|
||||
|
||||
[build-dependencies]
|
||||
serde = { version = "1.0.157", features = ["derive"] }
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
use std::error::Error;
|
||||
use log::trace;
|
||||
use reqwest::blocking::Client;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
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)]
|
||||
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>> {
|
||||
let endpoint = server.join("/v2/enroll")?;
|
||||
let client = Client::new();
|
||||
|
||||
let text = serde_json::to_string(request)?;
|
||||
|
||||
trace!("sending enroll: {}", text);
|
||||
|
||||
let resp = client.post(endpoint).body(text).send()?;
|
||||
Ok(resp.json()?)
|
||||
}
|
|
@ -1,16 +1,24 @@
|
|||
use std::sync::mpsc::{Receiver, TryRecvError};
|
||||
use log::{error, info};
|
||||
use base64::Engine;
|
||||
use chrono::Local;
|
||||
use log::{error, info, warn};
|
||||
use url::Url;
|
||||
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::api::{APIResponse, enroll, EnrollRequest};
|
||||
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
||||
use crate::daemon::ThreadMessageSender;
|
||||
|
||||
pub enum APIWorkerMessage {
|
||||
Shutdown
|
||||
Shutdown,
|
||||
Enroll { code: String }
|
||||
}
|
||||
|
||||
pub fn apiworker_main(config: TFClientConfig, instance: 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();
|
||||
|
||||
// Generate dhPubkey and edPubkey if it doesn't exist
|
||||
// Load vardata
|
||||
let mut vdata = match load_cdata(&instance) {
|
||||
|
@ -58,6 +66,65 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, _transmitters: T
|
|||
APIWorkerMessage::Shutdown => {
|
||||
info!("recv on command socket: shutdown, stopping");
|
||||
break;
|
||||
},
|
||||
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);
|
||||
info!("NebulaCAPool {}, org {} {}", resp.data.trusted_keys, resp.data.organization.name, resp.data.organization.id);
|
||||
info!("Config: {}", resp.data.config);
|
||||
|
||||
cdata.host_id = Some(resp.data.host_id);
|
||||
cdata.counter = resp.data.counter as i32;
|
||||
cdata.ca_pool = Some(resp.data.trusted_keys);
|
||||
cdata.org_name = Some(resp.data.organization.name);
|
||||
cdata.org_id = Some(resp.data.organization.id);
|
||||
cdata.config = Some(resp.data.config);
|
||||
|
||||
// Save vardata
|
||||
match save_cdata(&instance, cdata) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
error!("Error saving cdata: {}", e);
|
||||
error!("APIWorker exiting with error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -17,7 +17,12 @@ pub struct TFClientConfig {
|
|||
pub struct TFClientData {
|
||||
pub host_id: Option<String>,
|
||||
pub ed_privkey: Option<[u8; 32]>,
|
||||
pub dh_privkey: Option<[u8; 32]>
|
||||
pub dh_privkey: Option<[u8; 32]>,
|
||||
pub counter: i32,
|
||||
pub ca_pool: Option<String>,
|
||||
pub org_id: Option<String>,
|
||||
pub org_name: Option<String>,
|
||||
pub config: Option<String>
|
||||
}
|
||||
|
||||
pub fn create_config(instance: &str) -> Result<(), Box<dyn Error>> {
|
||||
|
@ -52,7 +57,7 @@ pub fn create_cdata(instance: &str) -> Result<(), Box<dyn Error>> {
|
|||
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 };
|
||||
let config = TFClientData { host_id: None, ed_privkey: None, dh_privkey: None, counter: 0, ca_pool: None, org_id: None, org_name: None, config: None };
|
||||
let config_str = toml::to_string(&config)?;
|
||||
fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?;
|
||||
Ok(())
|
||||
|
|
|
@ -74,8 +74,9 @@ pub fn daemon_main(name: String, server: String) {
|
|||
let config_api = config.clone();
|
||||
let transmitter_api = transmitter.clone();
|
||||
let name_api = name.clone();
|
||||
let server_api = server.clone();
|
||||
let api_thread = thread::spawn(move || {
|
||||
apiworker_main(config_api, name_api, transmitter_api, rx_api);
|
||||
apiworker_main(config_api, name_api, server_api,transmitter_api, rx_api);
|
||||
});
|
||||
|
||||
info!("Starting Nebula thread...");
|
||||
|
|
|
@ -23,6 +23,8 @@ pub mod config;
|
|||
pub mod service;
|
||||
pub mod apiworker;
|
||||
pub mod socketworker;
|
||||
pub mod api;
|
||||
pub mod socketclient;
|
||||
|
||||
pub mod nebula_bin {
|
||||
include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs"));
|
||||
|
@ -37,6 +39,7 @@ use std::fs;
|
|||
use clap::{Parser, ArgAction, Subcommand};
|
||||
use log::{error, info};
|
||||
use simple_logger::SimpleLogger;
|
||||
use crate::config::load_config;
|
||||
use crate::dirs::get_data_dir;
|
||||
use crate::embedded_nebula::{run_embedded_nebula, run_embedded_nebula_cert};
|
||||
use crate::service::entry::{cli_install, cli_start, cli_stop, cli_uninstall};
|
||||
|
@ -228,7 +231,23 @@ fn main() {
|
|||
Commands::Run { name, server } => {
|
||||
daemon::daemon_main(name, server);
|
||||
}
|
||||
Commands::Enroll { .. } => {}
|
||||
Commands::Enroll { name, code } => {
|
||||
info!("Loading config...");
|
||||
let config = match load_config(&name) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(e) => {
|
||||
error!("Error loading configuration: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
match socketclient::enroll(&code, &config) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
error!("Error sending enrollment request: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
use std::error::Error;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::net::{IpAddr, SocketAddr, TcpStream};
|
||||
use log::{error, info};
|
||||
use crate::config::TFClientConfig;
|
||||
use crate::socketworker::{ctob, DisconnectReason, JSON_API_VERSION, JsonMessage};
|
||||
|
||||
pub fn enroll(code: &str, config: &TFClientConfig) -> Result<(), Box<dyn Error>> {
|
||||
info!("Connecting to local command socket...");
|
||||
let mut stream = TcpStream::connect(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), config.listen_port))?;
|
||||
let mut stream2 = stream.try_clone()?;
|
||||
let mut reader = BufReader::new(&stream2);
|
||||
|
||||
info!("Sending Hello...");
|
||||
stream.write_all(&ctob(JsonMessage::Hello {
|
||||
version: JSON_API_VERSION,
|
||||
}))?;
|
||||
info!("Waiting for hello...");
|
||||
let msg = read_msg(&mut reader)?;
|
||||
match msg {
|
||||
JsonMessage::Hello { .. } => {
|
||||
info!("Server sent hello, connection established")
|
||||
}
|
||||
JsonMessage::Goodbye { reason } => {
|
||||
error!("Disconnected by server. Reason: {:?}", reason);
|
||||
return Err("Disconnected by server".into());
|
||||
}
|
||||
_ => {
|
||||
error!("Server returned unexpected message: {:?}", msg);
|
||||
error!("Sending goodbye and exiting");
|
||||
stream.write_all(&ctob(JsonMessage::Goodbye {
|
||||
reason: DisconnectReason::UnexpectedMessageType,
|
||||
}))?;
|
||||
return Err("Unexpected message type by server".into());
|
||||
}
|
||||
}
|
||||
|
||||
info!("Sending enroll request...");
|
||||
stream.write_all(&ctob(JsonMessage::Enroll {
|
||||
code: code.to_string(),
|
||||
}))?;
|
||||
|
||||
info!("Sending disconnect...");
|
||||
stream.write_all(&ctob(JsonMessage::Goodbye {
|
||||
reason: DisconnectReason::Done,
|
||||
}))?;
|
||||
|
||||
info!("Sent enroll request to tfclient daemon. Check logs to see if the enrollment was successful.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_msg(reader: &mut BufReader<&TcpStream>) -> Result<JsonMessage, Box<dyn Error>> {
|
||||
let mut str = String::new();
|
||||
reader.read_line(&mut str)?;
|
||||
let msg: JsonMessage = serde_json::from_str(&str)?;
|
||||
Ok(msg)
|
||||
}
|
|
@ -228,6 +228,12 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm
|
|||
has_id: data.host_id.is_some(),
|
||||
id: data.host_id
|
||||
}))?;
|
||||
},
|
||||
|
||||
JsonMessage::Enroll { code } => {
|
||||
info!("Client sent enroll with code {}", code);
|
||||
info!("Sending enroll request to apiworker");
|
||||
transmitter.api_thread.send(APIWorkerMessage::Enroll { code }).unwrap();
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -242,7 +248,7 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm
|
|||
Ok(should_disconnect)
|
||||
}
|
||||
|
||||
fn ctob(command: JsonMessage) -> Vec<u8> {
|
||||
pub fn ctob(command: JsonMessage) -> Vec<u8> {
|
||||
let command_str = serde_json::to_string(&command).unwrap() + "\n";
|
||||
command_str.into_bytes()
|
||||
}
|
||||
|
@ -256,7 +262,7 @@ pub const JSON_API_VERSION: i32 = 1;
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(tag = "method")]
|
||||
enum JsonMessage {
|
||||
pub enum JsonMessage {
|
||||
#[serde(rename = "hello")]
|
||||
Hello {
|
||||
version: i32
|
||||
|
@ -273,12 +279,16 @@ enum JsonMessage {
|
|||
HostID {
|
||||
has_id: bool,
|
||||
id: Option<String>
|
||||
},
|
||||
#[serde(rename = "enroll")]
|
||||
Enroll {
|
||||
code: String
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
enum DisconnectReason {
|
||||
pub enum DisconnectReason {
|
||||
#[serde(rename = "unsupported_version")]
|
||||
UnsupportedVersion { expected: i32, got: i32 },
|
||||
#[serde(rename = "unexpected_message_type")]
|
||||
|
|
Loading…
Reference in New Issue