use crate::config::{manager::ConfigManager, OPNsenseShell}; use crate::error::Error; use async_trait::async_trait; use log::info; use russh_keys::key::KeyPair; use std::sync::Arc; #[derive(Debug)] pub enum SshCredentials { SshKey { username: String, key: Arc }, Password { username: String, password: String }, } #[derive(Debug)] pub struct SshConfigManager { opnsense_shell: Arc, } impl SshConfigManager { pub fn new(opnsense_shell: Arc) -> Self { Self { opnsense_shell } } } impl SshConfigManager { async fn backup_config_remote(&self) -> Result { let ts = chrono::Utc::now(); let backup_filename = format!("config-{}-harmony.xml", ts.format("%s%.3f")); self.opnsense_shell .exec(&format!( "cp /conf/config.xml /conf/backup/{backup_filename}" )) .await } async fn move_to_live_config(&self, new_config_path: &str) -> Result { info!("Overwriting OPNSense /conf/config.xml with {new_config_path}"); self.opnsense_shell .exec(&format!("mv {new_config_path} /conf/config.xml")) .await } async fn reload_all_services(&self) -> Result { info!("Reloading all opnsense services"); self.opnsense_shell .exec("configctl service reload all") .await } } #[async_trait] impl ConfigManager for SshConfigManager { async fn load_as_str(&self) -> Result { self.opnsense_shell.exec("cat /conf/config.xml").await } async fn save_config(&self, content: &str) -> Result<(), Error> { let temp_filename = self .opnsense_shell .write_content_to_temp_file(content) .await?; self.backup_config_remote().await?; self.move_to_live_config(&temp_filename).await?; Ok(()) } async fn apply_new_config(&self, content: &str) -> Result<(), Error> { self.save_config(content).await?; self.reload_all_services().await?; Ok(()) } }