// "Son of man, I have made thee a watchman unto the house of Israel; therefore hear the word at my mouth, and give them warning from me." — Ezekiel 3:17 (KJV) // "I will take my stand at my watchpost and station myself on the tower." — Habakkuk 2:1 (ESV) use crate::monitors::devices::USBMon; use crate::monitors::filechanges::FileChanges; // use crate::monitors::network::NETMon; use crate::monitors::ssh_burn_file::SSHBurnMon; use crate::monitors::clients::NewClientMon; use config::Config; use std::path::Path; use std::process::exit; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; use crate::arguments::Arguments; use crate::config::setup_service; use crate::util::{get_cache_dir, get_config_dir, write_default_blacklist, write_default_config}; pub trait EventMonitor { async fn check(&mut self); } #[derive(Debug)] pub enum Monitors { USBMon(USBMon), // NetMon(NETMon), SSHBurnMon(SSHBurnMon), FileChanges(FileChanges), NewClients(NewClientMon), } pub struct AppState { pub mon_usb: bool, pub detection_triggered: bool, pub monitors: Arc>>, pub settings_map: Config, } impl AppState { pub fn new() -> Self { AppState { mon_usb: true, detection_triggered: false, monitors: Arc::new(Mutex::new(vec![])), settings_map: Config::default(), } } pub fn config(&mut self, args: Arguments) { // check for config file if it doesn't exist write default config log::info!("Configuring.."); let cache_dir = get_cache_dir(); let config_dir = get_config_dir(); let mut settings_file_path = format!("{}config/settings.toml", config_dir); let mut webhook = String::new(); if let Some(wh) = args.webhook_url { webhook = wh } if let Some(service_command) = args.service { match service_command.as_str() { "install" => { log::info!("Installing.."); setup_service(); if !Path::new(settings_file_path.as_str()).exists() { log::warn!("Settings.toml does not exist; writing default and exiting after service install"); crate::util::write_default_config(settings_file_path.clone(), webhook.clone()); exit(0); } } _ => {} } } if let Some(config_path) = args.config_path { settings_file_path = config_path.to_string(); } if !Path::new(settings_file_path.as_str()).exists() { log::warn!("Settings.toml does not exist; writing default"); write_default_config(settings_file_path.clone(), webhook); } let blacklist_file_path = format!("{}config/file_mon_blacklist", config_dir); if !Path::new(blacklist_file_path.as_str()).exists() { log::warn!("file_mon_blacklist does not exist; creating default"); write_default_blacklist(blacklist_file_path.clone()); } let config = Config::builder(); let settings = match config .add_source(config::File::with_name(settings_file_path.as_str())) .build() { Ok(s) => s, Err(e) => { log::error!("Configuration error: unable to parse settings: {}", e); exit(2); } }; // initialize monitoring modules let mut monitors: Vec = vec![]; if settings .get::("usb_mon_enabled") .unwrap_or("false".to_string()) .eq_ignore_ascii_case("true") { monitors.push(Monitors::USBMon(USBMon::new(settings.clone()))); } // if settings // .get::("net_mon_enabled") // .unwrap() // .eq_ignore_ascii_case("true") // { // monitors.push(Monitors::NetMon(NETMon::new(settings.clone()))); // } if settings .get::("burn_file_mon_enabled") .unwrap_or("false".to_string()) .eq_ignore_ascii_case("true") { monitors.push(Monitors::SSHBurnMon(SSHBurnMon::new(settings.clone()))); } if settings .get::("fs_mon_enabled") .unwrap_or("false".to_string()) .eq_ignore_ascii_case("true") { monitors.push(Monitors::FileChanges(FileChanges::new( settings.clone(), blacklist_file_path, cache_dir, ))); } // Enable new client scanning monitor if configured if settings .get::("client_scan_enabled") .unwrap_or("false".to_string()) .eq_ignore_ascii_case("true") { monitors.push(Monitors::NewClients(NewClientMon::new(settings.clone()))); } self.monitors.clone_from(&Arc::new(Mutex::new(monitors))); self.settings_map.clone_from(&settings) } pub async fn run(&mut self) { // Defaults if not configured or parse fails let n_fs_check_tick: u64 = self .settings_map .get::("fs_tick_delay_seconds").ok() .and_then(|v| match v.parse::() { Ok(n) => Some(n), Err(e) => { log::warn!("Invalid fs_tick_delay_seconds '{}': {}. Using default 300.", v, e); None } }) .unwrap_or(300); let n_tick: u64 = self .settings_map .get::("tick_delay_seconds").ok() .and_then(|v| match v.parse::() { Ok(n) => Some(n), Err(e) => { log::warn!("Invalid tick_delay_seconds '{}': {}. Using default 5.", v, e); None } }) .unwrap_or(5); let mut last = SystemTime::now(); loop { let mut binding = self.monitors.lock(); let bind = binding.as_mut().unwrap(); for i in bind.iter_mut() { match i { Monitors::USBMon(e) => { e.check().await; } // Monitors::NetMon(e) => { // e.check().await; // } Monitors::SSHBurnMon(e) => { e.check().await; } Monitors::FileChanges(e) => { let now = SystemTime::now(); let dur_since = match now.duration_since(last) { Ok(d) => d, Err(e) => { log::warn!("SystemTime went backwards: {}", e); Duration::new(0, 0) } }; if dur_since.as_secs() > n_fs_check_tick { last = SystemTime::now(); e.check().await; } } Monitors::NewClients(e) => { e.check().await; } } tokio::time::sleep(Duration::new(n_tick, 0)).await; } } } }