tfclient is done! woo!

This commit is contained in:
c0repwn3r 2023-03-30 12:13:29 -04:00
parent 7513eb8318
commit fa40dcda5b
Signed by: core
GPG Key ID: FDBF740DADDCEECF
11 changed files with 82 additions and 34 deletions

4
Cargo.lock generated
View File

@ -622,7 +622,7 @@ dependencies = [
[[package]]
name = "dnapi-rs"
version = "0.1.6"
version = "0.1.7"
dependencies = [
"base64 0.21.0",
"base64-serde",
@ -2803,7 +2803,7 @@ dependencies = [
[[package]]
name = "trifid-pki"
version = "0.1.8"
version = "0.1.9"
dependencies = [
"ed25519-dalek",
"hex",

View File

@ -1,6 +1,6 @@
[package]
name = "dnapi-rs"
version = "0.1.6"
version = "0.1.7"
edition = "2021"
description = "A rust client for the Defined Networking API"
license = "AGPL-3.0-or-later"

View File

@ -11,6 +11,7 @@ use crate::credentials::{Credentials, ed25519_public_keys_from_pem};
use crate::crypto::{new_keys, nonce};
use crate::message::{CHECK_FOR_UPDATE, CheckForUpdateResponseWrapper, DO_UPDATE, DoUpdateRequest, DoUpdateResponse, ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper};
use serde::{Serialize, Deserialize};
use base64::Engine;
/// A type alias to abstract return types
pub type NebulaConfig = Vec<u8>;
@ -185,12 +186,18 @@ impl Client {
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
})?;
let encoded_msg_bytes = encoded_msg.into_bytes();
let signature = ed_privkey.sign(&encoded_msg_bytes).to_vec();
let b64_msg = base64::engine::general_purpose::STANDARD.encode(encoded_msg_bytes);
let b64_msg_bytes = b64_msg.as_bytes();
let signature = ed_privkey.sign(b64_msg_bytes).to_vec();
ed_privkey.verify(b64_msg_bytes, &Signature::from_slice(&signature)?)?;
debug!("signature valid via clientside check");
let body = RequestV1 {
version: 1,
host_id: host_id.to_string(),
counter,
message: encoded_msg_bytes,
message: b64_msg,
signature,
};

View File

@ -1,8 +1,9 @@
//! Client structs to handle communication with the Defined Networking API. This is the blocking client API - if you want async instead, set no-default-features and enable the async feature instead.
use std::error::Error;
use base64::Engine;
use chrono::Local;
use log::{debug, error};
use log::{debug, error, trace};
use reqwest::StatusCode;
use url::Url;
use trifid_pki::cert::serialize_ed25519_public;
@ -90,6 +91,9 @@ impl Client {
organization_name: r.organization.name,
};
debug!("parsing public keys");
let trusted_keys = ed25519_public_keys_from_pem(&r.trusted_keys)?;
let creds = Credentials {
@ -190,17 +194,25 @@ impl Client {
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
})?;
let encoded_msg_bytes = encoded_msg.into_bytes();
let signature = ed_privkey.sign(&encoded_msg_bytes).to_vec();
let b64_msg = base64::engine::general_purpose::STANDARD.encode(encoded_msg_bytes);
let b64_msg_bytes = b64_msg.as_bytes();
let signature = ed_privkey.sign(b64_msg_bytes).to_vec();
ed_privkey.verify(b64_msg_bytes, &Signature::from_slice(&signature)?)?;
debug!("signature valid via clientside check");
let body = RequestV1 {
version: 1,
host_id: host_id.to_string(),
counter,
message: encoded_msg_bytes,
message: b64_msg,
signature,
};
let post_body = serde_json::to_string(&body)?;
trace!("sending dnclient request {}", post_body);
let resp = self.http_client.post(self.server_url.join(ENDPOINT_V1)?).body(post_body).send()?;
match resp.status() {

View File

@ -23,9 +23,8 @@ pub struct RequestV1 {
pub host_id: String,
/// The counter last returned by the server
pub counter: u32,
#[serde(with = "Base64Standard")]
/// A base64-encoded message
pub message: Vec<u8>,
/// A base64-encoded message. This must be previously base64-encoded, as the signature is signed over the base64-encoded data.
pub message: String,
#[serde(with = "Base64Standard")]
/// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
pub signature: Vec<u8>

View File

@ -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.6", path = "../dnapi-rs" }
dnapi-rs = { version = "0.1.7", path = "../dnapi-rs" }
serde_yaml = "0.9.19"
[build-dependencies]

View File

@ -145,7 +145,9 @@ pub struct NebulaConfig {
pub struct NebulaConfigPki {
pub ca: String,
pub cert: String,
pub key: String,
#[serde(default = "none")]
#[serde(skip_serializing_if = "is_none")]
pub key: Option<String>,
#[serde(default = "empty_vec")]
#[serde(skip_serializing_if = "is_empty_vec")]
pub blocklist: Vec<String>,
@ -180,7 +182,9 @@ pub struct NebulaConfigLighthouse {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NebulaConfigLighthouseDns {
pub host: Ipv4Addr,
#[serde(default = "string_empty")]
#[serde(skip_serializing_if = "is_string_empty")]
pub host: String,
#[serde(default = "u16_53")]
#[serde(skip_serializing_if = "is_u16_53")]
pub port: u16
@ -188,9 +192,9 @@ pub struct NebulaConfigLighthouseDns {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NebulaConfigListen {
#[serde(default = "ipv4_0000")]
#[serde(skip_serializing_if = "is_ipv4_0000")]
pub host: Ipv4Addr,
#[serde(default = "string_empty")]
#[serde(skip_serializing_if = "is_string_empty")]
pub host: String,
#[serde(default = "u16_0")]
#[serde(skip_serializing_if = "is_u16_0")]
pub port: u16,
@ -520,6 +524,9 @@ fn is_u64_1(u: &u64) -> bool { *u == 1 }
fn string_nebula() -> String { "nebula".to_string() }
fn is_string_nebula(s: &str) -> bool { s == "nebula" }
fn string_empty() -> String { String::new() }
fn is_string_empty(s: &str) -> bool { s == "" }
fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol { NebulaConfigStatsGraphiteProtocol::Tcp }
fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool { matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp) }

View File

@ -3,6 +3,7 @@
use std::error::Error;
use std::fs;
use std::sync::mpsc::{Receiver, TryRecvError};
use std::time::{Duration, SystemTime};
use log::{debug, error, info};
use crate::config::{load_cdata, NebulaConfig, save_cdata, TFClientConfig};
use crate::daemon::ThreadMessageSender;
@ -15,13 +16,16 @@ pub enum NebulaWorkerMessage {
}
fn insert_private_key(instance: &str) -> Result<(), Box<dyn Error>> {
if !get_nebulaconfig_file(instance).ok_or("Could not get config file location")?.exists() {
return Ok(()); // cant insert private key into a file that does not exist - BUT. we can gracefully handle nebula crashing - we cannot gracefully handle this fn failing
}
let cdata = load_cdata(instance)?;
let key = cdata.dh_privkey.ok_or("Missing private key")?;
let config_str = fs::read_to_string(get_nebulaconfig_file(instance).ok_or("Could not get config file location")?)?;
let mut config: NebulaConfig = serde_yaml::from_str(&config_str)?;
config.pki.key = String::from_utf8(key)?;
config.pki.key = Some(String::from_utf8(key)?);
debug!("inserted private key into config: {:?}", config);
@ -63,8 +67,25 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter
};
info!("nebula process started");
let mut last_restart_time = SystemTime::now();
// dont need to save it, because we do not, in any circumstance, write to it
loop {
if let Ok(e) = child.try_wait() {
if e.is_some() && SystemTime::now() > last_restart_time + Duration::from_secs(5) {
info!("nebula process has exited, restarting");
child = match run_embedded_nebula(&["-config".to_string(), get_nebulaconfig_file(&instance).unwrap().to_str().unwrap().to_string()]) {
Ok(c) => c,
Err(e) => {
error!("unable to start embedded nebula binary: {}", e);
error!("nebula thread exiting with error");
return;
}
};
info!("nebula process started");
last_restart_time = SystemTime::now();
}
}
match rx.try_recv() {
Ok(msg) => {
match msg {
@ -113,6 +134,7 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter
return;
}
};
last_restart_time = SystemTime::now();
debug!("nebula process restarted");
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "trifid-pki"
version = "0.1.8"
version = "0.1.9"
edition = "2021"
description = "A rust implementation of the Nebula PKI system"
license = "AGPL-3.0-or-later"

View File

@ -307,8 +307,8 @@ pub fn deserialize_ed25519_public(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
return Err(KeyError::WrongPemTag.into())
}
if pem.contents.len() != 64 {
return Err(KeyError::Not64Bytes.into())
if pem.contents.len() != 32 {
return Err(KeyError::Not32Bytes.into())
}
Ok(pem.contents)
}
@ -324,8 +324,8 @@ pub fn deserialize_ed25519_public_many(bytes: &[u8]) -> Result<Vec<Vec<u8>>, Box
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
return Err(KeyError::WrongPemTag.into())
}
if pem.contents.len() != 64 {
return Err(KeyError::Not64Bytes.into())
if pem.contents.len() != 32 {
return Err(KeyError::Not32Bytes.into())
}
keys.push(pem.contents);
}

View File

@ -296,19 +296,20 @@ fn x25519_serialization() {
#[test]
fn ed25519_serialization() {
let bytes = [0u8; 64];
let bytes2 = [0u8; 32];
assert_eq!(deserialize_ed25519_private(&serialize_ed25519_private(&bytes)).unwrap(), bytes);
assert!(deserialize_ed25519_private(&[0u8; 32]).is_err());
assert_eq!(deserialize_ed25519_public(&serialize_ed25519_public(&bytes)).unwrap(), bytes);
assert!(deserialize_ed25519_public(&[0u8; 32]).is_err());
assert_eq!(deserialize_ed25519_public(&serialize_ed25519_public(&bytes2)).unwrap(), bytes2);
assert!(deserialize_ed25519_public(&[0u8; 64]).is_err());
let mut bytes = vec![];
bytes.append(&mut serialize_ed25519_public(&[0u8; 64]));
bytes.append(&mut serialize_ed25519_public(&[1u8; 64]));
bytes.append(&mut serialize_ed25519_public(&[0u8; 32]));
bytes.append(&mut serialize_ed25519_public(&[1u8; 32]));
let deser = deserialize_ed25519_public_many(&bytes).unwrap();
assert_eq!(deser[0], [0u8; 64]);
assert_eq!(deser[1], [1u8; 64]);
assert_eq!(deser[0], [0u8; 32]);
assert_eq!(deser[1], [1u8; 32]);
bytes.append(&mut serialize_ed25519_public(&[1u8; 63]));
bytes.append(&mut serialize_ed25519_public(&[1u8; 33]));
deserialize_ed25519_public_many(&bytes).unwrap_err();
}
@ -647,11 +648,11 @@ bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
#[test]
fn test_deserialize_ed25519_public() {
let priv_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
let pub_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
-----END NEBULA ED25519 PUBLIC KEY-----";
let short_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
-----END NEBULA ED25519 PUBLIC KEY-----";
let invalid_banner = b"-----BEGIN NOT A NEBULA PUBLIC KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
@ -660,7 +661,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
-END NEBULA ED25519 PUBLIC KEY-----";
deserialize_ed25519_public(priv_key).unwrap();
deserialize_ed25519_public(pub_key).unwrap();
deserialize_ed25519_public(short_key).unwrap_err();