diff --git a/Cargo.lock b/Cargo.lock index 43ba615..9960b99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1365,15 +1365,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2038,22 +2029,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.157" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707de5fcf5df2b5788fca98dd7eab490bc2fd9b7ef1404defc462833b83f25ca" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.157" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78997f4555c22a7971214540c4a661291970619afd56de19f77e0de86296e1e5" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.2", + "syn 2.0.4", ] [[package]] @@ -2343,9 +2334,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d3276aee1fa0c33612917969b5172b5be2db051232a6e4826f1a1a9191b045" +checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae" dependencies = [ "proc-macro2", "quote", @@ -2412,7 +2403,9 @@ dependencies = [ "simple_logger", "tar", "tempfile", + "toml 0.7.3", "trifid-pki", + "url", ] [[package]] @@ -2576,9 +2569,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772c1426ab886e7362aedf4abc9c0d1348a979517efedfc25862944d10137af0" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" dependencies = [ "serde", "serde_spanned", @@ -2597,15 +2590,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.1" +version = "0.19.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a238ee2e6ede22fb95350acc78e21dc40da00bb66c0334bde83de4ed89424e" +checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" dependencies = [ "indexmap", - "nom8", "serde", "serde_spanned", "toml_datetime", + "winnow", ] [[package]] @@ -2713,7 +2706,7 @@ dependencies = [ "sha2", "sqlx", "tokio", - "toml 0.7.1", + "toml 0.7.3", "totp-rs", "trifid-pki", "url", @@ -3149,6 +3142,15 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "winnow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" diff --git a/tfclient/Cargo.toml b/tfclient/Cargo.toml index 4976a67..968fc53 100644 --- a/tfclient/Cargo.toml +++ b/tfclient/Cargo.toml @@ -14,6 +14,9 @@ log = "0.4.17" simple_logger = "4.1.0" sha2 = "0.10.6" hex = "0.4.3" +url = "2.3.1" +toml = "0.7.3" +serde = { version = "1.0.158", features = ["derive"] } [build-dependencies] serde = { version = "1.0.157", features = ["derive"] } diff --git a/tfclient/src/config.rs b/tfclient/src/config.rs new file mode 100644 index 0000000..d9d7597 --- /dev/null +++ b/tfclient/src/config.rs @@ -0,0 +1,42 @@ +use std::error::Error; +use std::fs; +use log::{debug, info}; +use serde::{Deserialize, Serialize}; +use crate::dirs::{get_config_dir, get_config_file}; + +pub const DEFAULT_PORT: u16 = 8157; +fn default_port() -> u16 { DEFAULT_PORT } + +#[derive(Serialize, Deserialize)] +pub struct TFClientConfig { + #[serde(default = "default_port")] + listen_port: u16 +} + +pub fn create_config(instance: &str) -> Result<(), Box> { + info!("Creating config directory..."); + fs::create_dir_all(get_config_dir(instance).ok_or("Unable to load config dir")?)?; + info!("Copying default config file to config directory..."); + let config = TFClientConfig { + listen_port: DEFAULT_PORT + }; + let config_str = toml::to_string(&config)?; + fs::write(get_config_file(instance).ok_or("Unable to load config dir")?, config_str)?; + Ok(()) +} + +pub fn load_config(instance: &str) -> Result> { + info!("Loading config..."); + let config_file = get_config_file(instance).ok_or("Unable to load config dir")?; + + if !config_file.exists() { + create_config(instance)?; + } + + debug!("opening {}", config_file.as_path().display()); + let config_str = fs::read_to_string(config_file)?; + debug!("parsing config file"); + let config: TFClientConfig = toml::from_str(&config_str)?; + info!("Loaded config successfully"); + Ok(config) +} \ No newline at end of file diff --git a/tfclient/src/daemon.rs b/tfclient/src/daemon.rs new file mode 100644 index 0000000..e6d36e9 --- /dev/null +++ b/tfclient/src/daemon.rs @@ -0,0 +1,32 @@ +use log::{error, info, warn}; +use url::Url; +use crate::config::load_config; + +pub fn daemon_main(name: String, server: String) { + // Validate the `server` + info!("Checking server url..."); + let api_base = match Url::parse(&server) { + Ok(u) => u, + Err(e) => { + error!("Invalid server url `{}`: {}", server, e); + std::process::exit(1); + } + }; + match api_base.scheme() { + "http" => { warn!("HTTP api urls are not reccomended. Please switch to HTTPS if possible.") }, + "https" => (), + _ => { + error!("Unsupported protocol `{}` (expected one of http, https)", api_base.scheme()); + std::process::exit(1); + } + } + + info!("Loading config..."); + let config = match load_config(&name) { + Ok(cfg) => cfg, + Err(e) => { + error!("Error loading configuration: {}", e); + std::process::exit(1); + } + }; +} \ No newline at end of file diff --git a/tfclient/src/dirs.rs b/tfclient/src/dirs.rs index 038984a..24d85ad 100644 --- a/tfclient/src/dirs.rs +++ b/tfclient/src/dirs.rs @@ -2,4 +2,12 @@ use std::path::PathBuf; pub fn get_data_dir() -> Option { dirs::data_dir().map(|f| f.join("tfclient/")) +} + +pub fn get_config_dir(instance: &str) -> Option { + dirs::config_dir().map(|f| f.join("tfclient/").join(format!("{}/", instance))) +} + +pub fn get_config_file(instance: &str) -> Option { + get_config_dir(instance).map(|f| f.join("tfclient.toml")) } \ No newline at end of file diff --git a/tfclient/src/main.rs b/tfclient/src/main.rs index 1aac422..8c7f9f0 100644 --- a/tfclient/src/main.rs +++ b/tfclient/src/main.rs @@ -17,6 +17,10 @@ pub mod embedded_nebula; pub mod dirs; pub mod util; +pub mod nebulaworker; +pub mod daemon; +pub mod config; +pub mod service; pub mod nebula_bin { include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs")); @@ -40,6 +44,7 @@ use crate::embedded_nebula::{run_embedded_nebula, run_embedded_nebula_cert}; struct Cli { #[arg(short = 'v', long = "version", action = ArgAction::SetTrue)] #[clap(global = true)] + /// Print the tfclient version, as well as the trifid-pki version and the version of the embedded nebula and nebula-cert binaries version: bool, #[command(subcommand)] @@ -61,7 +66,58 @@ enum Commands { args: Vec }, /// Clear any cached data that tfclient may have added - ClearCache {} + ClearCache {}, + + /// Install the tfclient system service + Install { + #[clap(short, long, default_value = "tfclient")] + /// Optional service name used to run multiple tfclient instances. Specify the same name on all other cli sub-commands (start, uninstall, etc.) to refer to this installed instance + name: String, + #[clap(short, long)] + /// Server to use for API calls. + server: String + }, + + /// Uninstall the tfclient system service + Uninstall { + #[clap(short, long, default_value = "tfclient")] + /// Service name specified on install + name: String + }, + + /// Start the tfclient system service + Start { + #[clap(short, long, default_value = "tfclient")] + /// Service name specified on install + name: String + }, + + /// Stop the tfclient system service + Stop { + #[clap(short, long, default_value = "tfclient")] + /// Service name specified on start + name: String + }, + + /// Run the tfclient daemon in the foreground + Run { + #[clap(short, long, default_value = "tfclient")] + /// Service name specified on install + name: String, + #[clap(short, long)] + /// Server to use for API calls. + server: String + }, + + /// Enroll this host using a trifid-api enrollment code + Enroll { + #[clap(short, long, default_value = "tfclient")] + /// Service name specified on install + name: String, + #[clap(short, long)] + /// Enrollment code used to enroll this node + code: String, + } } fn main() { @@ -154,6 +210,14 @@ fn main() { } } } + Commands::Install { .. } => {} + Commands::Uninstall { .. } => {} + Commands::Start { .. } => {} + Commands::Stop { .. } => {} + Commands::Run { name, server } => { + daemon::daemon_main(name, server); + } + Commands::Enroll { .. } => {} } } diff --git a/tfclient/src/nebulaworker.rs b/tfclient/src/nebulaworker.rs new file mode 100644 index 0000000..0882bf9 --- /dev/null +++ b/tfclient/src/nebulaworker.rs @@ -0,0 +1,5 @@ +// Code to handle the command socket worker + +pub fn socketworker_main() { + +} \ No newline at end of file diff --git a/tfclient/src/service/codegen/mod.rs b/tfclient/src/service/codegen/mod.rs new file mode 100644 index 0000000..547ff34 --- /dev/null +++ b/tfclient/src/service/codegen/mod.rs @@ -0,0 +1,9 @@ +pub mod systemd; + +use std::error::Error; +use std::path::PathBuf; + +pub trait ServiceFileGenerator { + fn create_service_files(bin_path: PathBuf, name: &str) -> Result<(), Box>; + fn delete_service_files(name: &str) -> Result<(), Box>; +} \ No newline at end of file diff --git a/tfclient/src/service/codegen/systemd.rs b/tfclient/src/service/codegen/systemd.rs new file mode 100644 index 0000000..0820264 --- /dev/null +++ b/tfclient/src/service/codegen/systemd.rs @@ -0,0 +1,14 @@ +use std::error::Error; +use std::path::PathBuf; +use crate::service::codegen::ServiceFileGenerator; + +pub struct SystemDServiceFileGenerator {} +impl ServiceFileGenerator for SystemDServiceFileGenerator { + fn create_service_files(bin_path: PathBuf, name: &str) -> Result<(), Box> { + todo!() + } + + fn delete_service_files(name: &str) -> Result<(), Box> { + todo!() + } +} \ No newline at end of file diff --git a/tfclient/src/service/mod.rs b/tfclient/src/service/mod.rs new file mode 100644 index 0000000..c5ff059 --- /dev/null +++ b/tfclient/src/service/mod.rs @@ -0,0 +1,12 @@ +use std::error::Error; +use std::path::PathBuf; + +pub mod codegen; +pub mod systemd; + +pub trait ServiceManager { + fn install(bin_path: PathBuf, name: &str) -> Result<(), Box>; + fn uninstall(name: &str) -> Result<(), Box>; + fn start(name: &str) -> Result<(), Box>; + fn stop(name: &str) -> Result<(), Box>; +} \ No newline at end of file diff --git a/tfclient/src/service/systemd.rs b/tfclient/src/service/systemd.rs new file mode 100644 index 0000000..42bb249 --- /dev/null +++ b/tfclient/src/service/systemd.rs @@ -0,0 +1,22 @@ +use std::error::Error; +use std::path::PathBuf; +use crate::service::ServiceManager; + +pub struct SystemDServiceManager {} +impl ServiceManager for SystemDServiceManager { + fn install(bin_path: PathBuf, name: &str) -> Result<(), Box> { + todo!() + } + + fn uninstall(name: &str) -> Result<(), Box> { + todo!() + } + + fn start(name: &str) -> Result<(), Box> { + todo!() + } + + fn stop(name: &str) -> Result<(), Box> { + todo!() + } +} \ No newline at end of file