Refactor Certificate management

This commit is contained in:
2024-03-18 13:40:34 +01:00
parent 177d472d59
commit a1b4865b3f
15 changed files with 659 additions and 108 deletions

View File

@@ -2,6 +2,7 @@
name = "bonknet_client"
version = "0.1.0"
edition = "2021"
description = "A CLI Client for the Bonknet system"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,3 +18,5 @@ rmp-serde = "1.1.2"
tracing = "0.1"
tracing-subscriber = "0.3"
uuid = { version = "1.7.0", features = ["serde"] }
clap = { version = "4.5.2", features = ["derive"] }
thiserror = "1.0.56"

View File

@@ -0,0 +1,61 @@
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio_rustls::{TlsConnector};
use tokio_rustls::client::TlsStream;
use tokio_rustls::rustls::pki_types::ServerName;
use tokio_util::codec::{Framed, LengthDelimitedCodec};
use futures::{SinkExt, StreamExt};
use thiserror::Error;
use tokio_rustls::rustls::ClientConfig;
use tracing::{error};
use libbonknet::clientmsg::{FromClientCommand, ToClientResponse};
pub type TransportStream = Framed<TlsStream<TcpStream>, LengthDelimitedCodec>;
#[derive(Error, Debug)]
pub enum SendError {
#[error("Failure during serialization of the msg to send")]
MsgSerializationFailure(rmp_serde::encode::Error),
#[error("Failure during the transport send of the data")]
TransportError(std::io::Error),
#[error("Empty buffer during reply wait")]
EmptyBufferError,
#[error("Generic buffer error during response reading")]
GenericBufferError(std::io::Error),
#[error("Failure during deserialization of the reply msg")]
ReplyDeserializationFailure(rmp_serde::decode::Error),
}
pub struct BonkClient {
transport: TransportStream,
}
impl BonkClient {
pub async fn connect(tlsconfig: ClientConfig, dnsname: &str, port: u16) -> tokio::io::Result<BonkClient> {
// Load TLS Config
let connector = TlsConnector::from(Arc::new(tlsconfig));
let stream = TcpStream::connect(format!("{}:{}", dnsname, port)).await?;
let dnsservername = ServerName::try_from(dnsname).unwrap().to_owned();
let stream = connector.connect(dnsservername, stream).await?;
let transport = Framed::new(stream, LengthDelimitedCodec::new());
Ok(BonkClient { transport })
}
// TODO: make this private and make single calls for each Bonk interaction?
// The main idea is that the messages need to be inside the API and not ouside as elements of it
pub async fn send(&mut self, msg: &FromClientCommand) -> Result<ToClientResponse, SendError> {
let bmsg = rmp_serde::to_vec(msg).map_err(SendError::MsgSerializationFailure)?;
self.transport.send(bmsg.into()).await.map_err(SendError::TransportError)?;
match self.transport.next().await {
None => Err(SendError::EmptyBufferError),
Some(item) => match item {
Ok(buf) => {
let reply: ToClientResponse = rmp_serde::from_slice(&buf).map_err(SendError::ReplyDeserializationFailure)?;
Ok(reply)
}
Err(e) => Err(SendError::GenericBufferError(e))
}
}
}
}

View File

@@ -0,0 +1,64 @@
mod client;
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use libbonknet::cert::*;
use tracing::{info};
use libbonknet::clientmsg::FromClientCommand;
use crate::client::BonkClient;
#[derive(Parser, Debug)]
#[command(version, about)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// connect via ssh to the given remote_id.
Ssh {
remote_id: String,
},
/// send a file from source to remote. This command uses scp syntax and must contain wildcards to work (see examples).
Scp {
remote_id: String,
source: PathBuf,
dest: PathBuf,
},
/// get a list of all the servers connected to the broker.
Serverlist {
pattern: Option<String>,
},
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
// Tracing Subscriber
let subscriber = tracing_subscriber::FmtSubscriber::new();
tracing::subscriber::set_global_default(subscriber).unwrap();
// CLI parsing
let cli = Cli::parse();
// Load Identity files
let client_ident = LeafCertPair::load_from_file("certs_pem/client.pem").unwrap();
let broker_root = BrokerRootCerts::load_from_file("certs_pem/broker_root_ca_cert.pem").unwrap();
let tlsconfig = client_ident.to_tlsclientconfig(&broker_root);
// Execute command
match &cli.command {
Commands::Ssh { remote_id } => {
info!("Run SSH on {remote_id}");
unimplemented!()
}
Commands::Scp { remote_id, source, dest } => {
info!("Run SCP on {} moving {} to {}", remote_id, source.display(), dest.display());
unimplemented!()
}
Commands::Serverlist { pattern } => {
info!("Run Clientlist with pattern {:?}", pattern);
let mut client = BonkClient::connect(tlsconfig, "localhost", 2541).await.unwrap();
let reply = client.send(&FromClientCommand::ServerList).await.unwrap();
info!("Reply: {:?}", reply);
}
}
Ok(())
}