From ba3b3a185fe307dc2d781c8921a3147ec955cf73 Mon Sep 17 00:00:00 2001 From: core Date: Thu, 30 Mar 2023 17:34:00 -0400 Subject: [PATCH] allow manual configuration update --- .idea/vcs.xml | 1 - Cargo.lock | 2 +- tfclient/Cargo.toml | 2 +- tfclient/config.example.toml | 7 ++++++ tfclient/src/apiworker.rs | 3 ++- tfclient/src/config.rs | 7 ++++-- tfclient/src/daemon.rs | 31 ++++++++++++++------------ tfclient/src/main.rs | 24 ++++++++++++++++++++ tfclient/src/socketclient.rs | 43 ++++++++++++++++++++++++++++++++++++ tfclient/src/socketworker.rs | 10 ++++++++- 10 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 tfclient/config.example.toml diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 04303aa..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a4bbbd1..ba13516 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2469,7 +2469,7 @@ dependencies = [ [[package]] name = "tfclient" -version = "0.1.1" +version = "0.1.2" dependencies = [ "base64 0.21.0", "base64-serde", diff --git a/tfclient/Cargo.toml b/tfclient/Cargo.toml index e58450c..5ce8753 100644 --- a/tfclient/Cargo.toml +++ b/tfclient/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tfclient" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "An open-source reimplementation of a Defined Networking-compatible client" license = "GPL-3.0-or-later" diff --git a/tfclient/config.example.toml b/tfclient/config.example.toml new file mode 100644 index 0000000..ca5e9a9 --- /dev/null +++ b/tfclient/config.example.toml @@ -0,0 +1,7 @@ +# The port that the tfclient daemon should listen on for JSON RPC requests. +listen_port = 8157 + +# Set this to true in order to disable automatic callbacks back to the API server. +# Use this if you are using an API server which you do not completley trust. +# If this is enabled, you'll need to run `tfclient --update` to manually update your client configuration. +disable_automatic_config_updates = false \ No newline at end of file diff --git a/tfclient/src/apiworker.rs b/tfclient/src/apiworker.rs index 4246e93..3b2a1de 100644 --- a/tfclient/src/apiworker.rs +++ b/tfclient/src/apiworker.rs @@ -17,6 +17,7 @@ use crate::nebulaworker::NebulaWorkerMessage; pub enum APIWorkerMessage { Shutdown, Enroll { code: String }, + Update, Timer } @@ -33,7 +34,7 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx info!("recv on command socket: shutdown, stopping"); break; }, - APIWorkerMessage::Timer => { + APIWorkerMessage::Timer | APIWorkerMessage::Update => { info!("updating config"); let mut cdata = match load_cdata(&instance) { Ok(c) => c, diff --git a/tfclient/src/config.rs b/tfclient/src/config.rs index 517a09b..9fd2927 100644 --- a/tfclient/src/config.rs +++ b/tfclient/src/config.rs @@ -18,7 +18,9 @@ fn default_port() -> u16 { DEFAULT_PORT } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TFClientConfig { #[serde(default = "default_port")] - pub listen_port: u16 + pub listen_port: u16, + #[serde(default = "bool_false")] + pub disable_automatic_config_updates: bool } #[derive(Serialize, Deserialize, Clone)] @@ -33,7 +35,8 @@ pub fn create_config(instance: &str) -> Result<(), Box> { 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 + listen_port: DEFAULT_PORT, + disable_automatic_config_updates: false, }; let config_str = toml::to_string(&config)?; fs::write(get_config_file(instance).ok_or("Unable to load config dir")?, config_str)?; diff --git a/tfclient/src/daemon.rs b/tfclient/src/daemon.rs index 6f59054..6cbba58 100644 --- a/tfclient/src/daemon.rs +++ b/tfclient/src/daemon.rs @@ -95,10 +95,23 @@ pub fn daemon_main(name: String, server: String) { }); info!("Starting timer thread..."); - let timer_transmitter = transmitter.clone(); - let timer_thread = thread::spawn(move || { - timer_main(timer_transmitter, rx_timer); - }); + if !config.disable_automatic_config_updates { + let timer_transmitter = transmitter.clone(); + let timer_thread = thread::spawn(move || { + timer_main(timer_transmitter, rx_timer); + }); + 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"); + } else { + info!("automatic config updates have been disabled - not starting timer thread"); + } info!("Starting socket worker thread..."); let name_socket = name.clone(); @@ -126,16 +139,6 @@ pub fn daemon_main(name: String, server: String) { } 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..."); match nebula_thread.join() { Ok(_) => (), diff --git a/tfclient/src/main.rs b/tfclient/src/main.rs index 4c0adfe..e8848e1 100644 --- a/tfclient/src/main.rs +++ b/tfclient/src/main.rs @@ -123,6 +123,13 @@ enum Commands { #[clap(short, long)] /// Enrollment code used to enroll this node code: String, + }, + + /// Manually trigger a config update. It's useful to use this in combination with the `disable_automatic_config_updates` option. + Update { + #[clap(short, long, default_value = "tfclient")] + /// Service name specified on install + name: String, } } @@ -247,6 +254,23 @@ fn main() { std::process::exit(1); } }; + }, + Commands::Update { name } => { + 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::update(&config) { + Ok(_) => (), + Err(e) => { + error!("Error sending update request: {}", e); + std::process::exit(1); + } + }; } } } diff --git a/tfclient/src/socketclient.rs b/tfclient/src/socketclient.rs index 62e0449..addb9ff 100644 --- a/tfclient/src/socketclient.rs +++ b/tfclient/src/socketclient.rs @@ -50,6 +50,49 @@ pub fn enroll(code: &str, config: &TFClientConfig) -> Result<(), Box> Ok(()) } +pub fn update(config: &TFClientConfig) -> Result<(), Box> { + info!("Connecting to local command socket..."); + let mut stream = TcpStream::connect(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), config.listen_port))?; + let 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::Update {}))?; + + info!("Sending disconnect..."); + stream.write_all(&ctob(JsonMessage::Goodbye { + reason: DisconnectReason::Done, + }))?; + + info!("Told tfclient daemon to trigger a configuration update. Check tfclient's logs to see if this was successful."); + + Ok(()) +} + fn read_msg(reader: &mut BufReader<&TcpStream>) -> Result> { let mut str = String::new(); reader.read_line(&mut str)?; diff --git a/tfclient/src/socketworker.rs b/tfclient/src/socketworker.rs index a87ec21..498e53c 100644 --- a/tfclient/src/socketworker.rs +++ b/tfclient/src/socketworker.rs @@ -241,6 +241,12 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm info!("Client sent enroll with code {}", code); info!("Sending enroll request to apiworker"); transmitter.api_thread.send(APIWorkerMessage::Enroll { code }).unwrap(); + }, + + JsonMessage::Update {} => { + info!("Client sent update request."); + info!("Telling apiworker to update configuration"); + transmitter.api_thread.send(APIWorkerMessage::Update).unwrap(); } _ => { @@ -290,7 +296,9 @@ pub enum JsonMessage { #[serde(rename = "enroll")] Enroll { code: String - } + }, + #[serde(rename = "update")] + Update {} } #[derive(Serialize, Deserialize, Debug)]