From 29892269cdab7256739de9b30e8d6b019f7f8c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fern=C3=A1ndez?= Date: Wed, 26 Jul 2023 13:22:53 +0200 Subject: [PATCH 1/4] Added Splunk integration option --- src/auditevent.rs | 67 ++++++++--- src/config.rs | 100 +++++++++++++---- src/event.rs | 104 +++++++++++++----- src/index.rs | 26 +++-- src/launcher.rs | 2 + src/monitor.rs | 10 +- .../unit/config/common/test_push_template.yml | 22 ++++ .../linux/events_credentials_password.yml | 2 +- .../config/linux/events_credentials_token.yml | 29 +++++ .../config/linux/events_credentials_user.yml | 2 +- .../windows/events_credentials_password.yml | 2 +- .../windows/events_credentials_token.yml | 22 ++++ .../windows/events_credentials_user.yml | 2 +- 13 files changed, 306 insertions(+), 84 deletions(-) create mode 100644 test/unit/config/common/test_push_template.yml create mode 100644 test/unit/config/linux/events_credentials_token.yml create mode 100644 test/unit/config/windows/events_credentials_token.yml diff --git a/src/auditevent.rs b/src/auditevent.rs index 39cdf30..ea245c6 100644 --- a/src/auditevent.rs +++ b/src/auditevent.rs @@ -354,22 +354,50 @@ impl Event { // Function to send events through network pub async fn send(&self, index: String, address: String, user: String, pass: String, insecure: bool) { - let data = self.get_json(); - - let request_url = format!("{}/{}/_doc/{}", address, index, self.id); - let client = Client::builder() - .danger_accept_invalid_certs(insecure) - .timeout(Duration::from_secs(30)) - .build().unwrap(); - match client - .post(request_url) - .basic_auth(user, Some(pass)) - .json(&data) - .send() - .await{ - Ok(response) => debug!("Response received: {:?}", response), - Err(e) => debug!("Error on request: {:?}", e) - }; + let event = self.get_json(); + let config = unsafe { super::GCONFIG.clone().unwrap() }; + // Splunk endpoint integration + if config.endpoint_type == "Splunk" { + let data = json!({ + "source": self.node.clone(), + "sourcetype": "_json", + "event": event, + "index": "fim_events" + }); + debug!("Sending received event to Splunk integration, event: {}", data); + let request_url = format!("{}/services/collector/event", address); + let client = Client::builder() + .danger_accept_invalid_certs(insecure) + .timeout(Duration::from_secs(30)) + .build().unwrap(); + match client + .post(request_url) + .header("Authorization", format!("Splunk {}", config.endpoint_token)) + .json(&data) + .send() + .await { + Ok(response) => debug!("Response received: {:?}", + response.text().await.unwrap()), + Err(e) => debug!("Error on request: {:?}", e) + } + // Elastic endpoint integration + } else { + let request_url = format!("{}/{}/_doc/{}", address, index, self.id); + let client = Client::builder() + .danger_accept_invalid_certs(insecure) + .timeout(Duration::from_secs(30)) + .build().unwrap(); + match client + .post(request_url) + .basic_auth(user, Some(pass)) + .json(&event) + .send() + .await { + Ok(response) => debug!("Response received: {:?}", + response.text().await.unwrap()), + Err(e) => debug!("Error on request: {:?}", e) + } + } } // ------------------------------------------------------------------------ @@ -492,6 +520,12 @@ mod tests { fs::remove_file(filename).unwrap() } + fn initialize() { + unsafe{ + super::super::GCONFIG = Some(config::Config::new(&utils::get_os(), None)); + } + } + fn create_empty_event() -> Event { Event { id: String::from(""), timestamp: String::from(""), @@ -911,6 +945,7 @@ mod tests { #[test] fn test_send() { + initialize(); let event = create_test_event(); block_on( event.send( String::from("test"), String::from("https://127.0.0.1:9200"), diff --git a/src/config.rs b/src/config.rs index 3a3e900..e13c1be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ // Copyright (C) 2021, Achiefs. // Global constants definitions -pub const VERSION: &str = "0.4.7"; +pub const VERSION: &str = "0.4.8"; pub const NETWORK_MODE: &str = "NETWORK"; pub const FILE_MODE: &str = "FILE"; pub const BOTH_MODE: &str = "BOTH"; @@ -34,12 +34,13 @@ pub struct Config { pub events_watcher: String, pub events_destination: String, pub events_max_file_checksum: usize, + pub endpoint_type: String, pub endpoint_address: String, pub endpoint_user: String, pub endpoint_pass: String, + pub endpoint_token: String, pub events_file: String, pub monitor: Array, - //pub monitor_integrations: Array, pub audit: Array, pub node: String, pub log_file: String, @@ -57,9 +58,11 @@ impl Config { events_watcher: self.events_watcher.clone(), events_destination: self.events_destination.clone(), events_max_file_checksum: self.events_max_file_checksum, + endpoint_type: self.endpoint_type.clone(), endpoint_address: self.endpoint_address.clone(), endpoint_user: self.endpoint_user.clone(), endpoint_pass: self.endpoint_pass.clone(), + endpoint_token: self.endpoint_token.clone(), events_file: self.events_file.clone(), monitor: self.monitor.clone(), audit: self.audit.clone(), @@ -106,7 +109,7 @@ impl Config { println!("[ERROR] events->file not found in config.yml."); panic!("events->file not found in config.yml."); }else{ - String::from("Not_used") + String::from("Not_defined") } } }; @@ -136,20 +139,26 @@ impl Config { println!("[ERROR] events->endpoint->address not found in config.yml."); panic!("events->endpoint->address not found in config.yml."); }else{ - String::from("Not_used") + String::from("Not_defined") } } }; + // Manage null value on events->endpoint->credentials->token value + let endpoint_token = match yaml[0]["events"]["endpoint"]["credentials"]["token"].as_str() { + Some(value) => String::from(value), + None => String::from("Not_defined") + }; + // Manage null value on events->endpoint->credentials->user value let endpoint_user = match yaml[0]["events"]["endpoint"]["credentials"]["user"].as_str() { Some(value) => String::from(value), None => { - if events_destination != *"file" { + if events_destination != *"file" && endpoint_token.is_empty() { println!("[ERROR] events->endpoint->credentials->user not found in config.yml."); panic!("events->endpoint->credentials->user not found in config.yml."); }else{ - String::from("Not_used") + String::from("Not_defined") } } }; @@ -158,15 +167,30 @@ impl Config { let endpoint_pass = match yaml[0]["events"]["endpoint"]["credentials"]["password"].as_str() { Some(value) => String::from(value), None => { - if events_destination != *"file" { + if events_destination != *"file" && endpoint_token.is_empty() { println!("[ERROR] events->endpoint->credentials->password not found in config.yml."); panic!("events->endpoint->credentials->password not found in config.yml."); }else{ - String::from("Not_used") + String::from("Not_defined") } } }; + let endpoint_type = if endpoint_token != "Not_defined" { + String::from("Splunk") + }else if endpoint_user != "Not_defined" && endpoint_pass != "Not_defined" { + String::from("Elastic") + }else{ + String::from("Not_defined") + }; + + if endpoint_token == "Not_defined" && (endpoint_user == "Not_defined" || + endpoint_pass == "Not_defined") && events_destination != *"file" { + println!("[ERROR] events->endpoint->credentials->[token or user and password] not found in config.yml."); + panic!("No endpoint credentials provided in config.yml."); + } + + // Manage null value on monitor value let monitor = match yaml[0]["monitor"].as_vec() { Some(value) => value.to_vec(), @@ -234,9 +258,11 @@ impl Config { events_watcher, events_destination, events_max_file_checksum, + endpoint_type, endpoint_address, endpoint_user, endpoint_pass, + endpoint_token, events_file, monitor, audit, @@ -431,9 +457,11 @@ mod tests { events_watcher: String::from("Recommended"), events_destination: String::from(events_destination), events_max_file_checksum: 64, + endpoint_type: String::from("Elastic"), endpoint_address: String::from("test"), endpoint_user: String::from("test"), endpoint_pass: String::from("test"), + endpoint_token: String::from("test"), events_file: String::from("test"), monitor: Array::new(), audit: Array::new(), @@ -455,9 +483,11 @@ mod tests { assert_eq!(config.path, cloned.path); assert_eq!(config.events_destination, cloned.events_destination); assert_eq!(config.events_max_file_checksum, cloned.events_max_file_checksum); + assert_eq!(config.endpoint_type, cloned.endpoint_type); assert_eq!(config.endpoint_address, cloned.endpoint_address); assert_eq!(config.endpoint_user, cloned.endpoint_user); assert_eq!(config.endpoint_pass, cloned.endpoint_pass); + assert_eq!(config.endpoint_token, cloned.endpoint_token); assert_eq!(config.events_file, cloned.events_file); assert_eq!(config.monitor, cloned.monitor); assert_eq!(config.audit, cloned.audit); @@ -476,9 +506,11 @@ mod tests { let config = Config::new("windows", None); assert_eq!(config.version, String::from(VERSION)); assert_eq!(config.events_destination, String::from("file")); - assert_eq!(config.endpoint_address, String::from("Not_used")); - assert_eq!(config.endpoint_user, String::from("Not_used")); - assert_eq!(config.endpoint_pass, String::from("Not_used")); + assert_eq!(config.endpoint_address, String::from("Not_defined")); + assert_eq!(config.endpoint_type, String::from("Not_defined")); + assert_eq!(config.endpoint_user, String::from("Not_defined")); + assert_eq!(config.endpoint_pass, String::from("Not_defined")); + assert_eq!(config.endpoint_token, String::from("Not_defined")); assert_eq!(config.events_file, String::from("C:\\ProgramData\\fim\\events.json")); // monitor // audit @@ -513,7 +545,7 @@ mod tests { #[test] fn test_new_config_windows_events_destination_network() { let config = Config::new("windows", Some("test/unit/config/windows/events_destination_network.yml")); - assert_eq!(config.events_file, String::from("Not_used")); + assert_eq!(config.events_file, String::from("Not_defined")); } // ------------------------------------------------------------------------ @@ -567,7 +599,7 @@ mod tests { #[test] fn test_new_config_windows_events_credentials_user() { let config = Config::new("windows", Some("test/unit/config/windows/events_credentials_user.yml")); - assert_eq!(config.endpoint_user, "test"); + assert_eq!(config.endpoint_user, "test_user"); } // ------------------------------------------------------------------------ @@ -585,7 +617,16 @@ mod tests { #[test] fn test_new_config_windows_events_credentials_password() { let config = Config::new("windows", Some("test/unit/config/windows/events_credentials_password.yml")); - assert_eq!(config.endpoint_pass, "test"); + assert_eq!(config.endpoint_pass, "test_password"); + } + + // ------------------------------------------------------------------------ + + #[cfg(target_os = "windows")] + #[test] + fn test_new_config_windows_events_credentials_token() { + let config = Config::new("windows", Some("test/unit/config/windows/events_credentials_token.yml")); + assert_eq!(config.endpoint_token, "test_token"); } // ------------------------------------------------------------------------ @@ -657,7 +698,7 @@ mod tests { #[test] fn test_new_config_linux_events_destination_network() { let config = Config::new("linux", Some("test/unit/config/linux/events_destination_network.yml")); - assert_eq!(config.events_file, String::from("Not_used")); + assert_eq!(config.events_file, String::from("Not_defined")); } // ------------------------------------------------------------------------ @@ -711,7 +752,7 @@ mod tests { #[test] fn test_new_config_linux_events_credentials_user() { let config = Config::new("linux", Some("test/unit/config/linux/events_credentials_user.yml")); - assert_eq!(config.endpoint_user, "test"); + assert_eq!(config.endpoint_user, "test_user"); } // ------------------------------------------------------------------------ @@ -729,7 +770,16 @@ mod tests { #[test] fn test_new_config_linux_events_credentials_password() { let config = Config::new("linux", Some("test/unit/config/linux/events_credentials_password.yml")); - assert_eq!(config.endpoint_pass, "test"); + assert_eq!(config.endpoint_pass, "test_password"); + } + + // ------------------------------------------------------------------------ + + #[cfg(target_os = "linux")] + #[test] + fn test_new_config_linux_events_credentials_token() { + let config = Config::new("linux", Some("test/unit/config/linux/events_credentials_token.yml")); + assert_eq!(config.endpoint_token, "test_token"); } // ------------------------------------------------------------------------ @@ -807,9 +857,11 @@ mod tests { let config = Config::new("linux", None); assert_eq!(config.version, String::from(VERSION)); assert_eq!(config.events_destination, String::from("file")); - assert_eq!(config.endpoint_address, String::from("Not_used")); - assert_eq!(config.endpoint_user, String::from("Not_used")); - assert_eq!(config.endpoint_pass, String::from("Not_used")); + assert_eq!(config.endpoint_type, String::from("Not_defined")); + assert_eq!(config.endpoint_address, String::from("Not_defined")); + assert_eq!(config.endpoint_user, String::from("Not_defined")); + assert_eq!(config.endpoint_pass, String::from("Not_defined")); + assert_eq!(config.endpoint_token, String::from("Not_defined")); assert_eq!(config.events_file, String::from("/var/lib/fim/events.json")); // monitor // audit @@ -828,9 +880,11 @@ mod tests { let config = Config::new("macos", None); assert_eq!(config.version, String::from(VERSION)); assert_eq!(config.events_destination, String::from("file")); - assert_eq!(config.endpoint_address, String::from("Not_used")); - assert_eq!(config.endpoint_user, String::from("Not_used")); - assert_eq!(config.endpoint_pass, String::from("Not_used")); + assert_eq!(config.endpoint_type, String::from("Not_defined")); + assert_eq!(config.endpoint_address, String::from("Not_defined")); + assert_eq!(config.endpoint_user, String::from("Not_defined")); + assert_eq!(config.endpoint_pass, String::from("Not_defined")); + assert_eq!(config.endpoint_token, String::from("Not_defined")); assert_eq!(config.events_file, String::from("/var/lib/fim/events.json")); // monitor // audit diff --git a/src/event.rs b/src/event.rs index faab920..5c758c3 100644 --- a/src/event.rs +++ b/src/event.rs @@ -98,34 +98,75 @@ impl Event { // Function to send events through network pub async fn send(&self, index: String, address: String, user: String, pass: String, insecure: bool) { - let data = json!({ - "timestamp": self.timestamp.clone(), - "hostname": self.hostname.clone(), - "node": self.node.clone(), - "fpid": self.fpid.clone(), - "version": self.version.clone(), - "labels": self.labels.clone(), - "operation": self.operation.clone(), - "detailed_operation": self.detailed_operation.clone(), - "file": String::from(self.path.clone().to_str().unwrap()), - "checksum": self.checksum.clone(), - "system": self.system.clone() - }); + let config = unsafe { super::GCONFIG.clone().unwrap() }; + // Splunk endpoint integration + if config.endpoint_type == "Splunk" { + let data = json!({ + "source": self.node.clone(), + "sourcetype": "_json", + "event": json!({ + "timestamp": self.timestamp.clone(), + "hostname": self.hostname.clone(), + "node": self.node.clone(), + "fpid": self.fpid.clone(), + "version": self.version.clone(), + "labels": self.labels.clone(), + "operation": self.operation.clone(), + "detailed_operation": self.detailed_operation.clone(), + "file": String::from(self.path.clone().to_str().unwrap()), + "checksum": self.checksum.clone(), + "system": self.system.clone() + }), + "index": "fim_events" + }); + debug!("Sending received event to Splunk integration, event: {}", data); + let request_url = format!("{}/services/collector/event", address); + let client = Client::builder() + .danger_accept_invalid_certs(insecure) + .timeout(Duration::from_secs(30)) + .build().unwrap(); + match client + .post(request_url) + .header("Authorization", format!("Splunk {}", config.endpoint_token)) + .json(&data) + .send() + .await { + Ok(response) => debug!("Response received: {:?}", + response.text().await.unwrap()), + Err(e) => debug!("Error on request: {:?}", e) + } + // Elastic endpoint integration + } else { + let data = json!({ + "timestamp": self.timestamp.clone(), + "hostname": self.hostname.clone(), + "node": self.node.clone(), + "fpid": self.fpid.clone(), + "version": self.version.clone(), + "labels": self.labels.clone(), + "operation": self.operation.clone(), + "detailed_operation": self.detailed_operation.clone(), + "file": String::from(self.path.clone().to_str().unwrap()), + "checksum": self.checksum.clone(), + "system": self.system.clone() + }); + let request_url = format!("{}/{}/_doc/{}", address, index, self.id); + let client = Client::builder() + .danger_accept_invalid_certs(insecure) + .timeout(Duration::from_secs(30)) + .build().unwrap(); + match client + .post(request_url) + .basic_auth(user, Some(pass)) + .json(&data) + .send() + .await { + Ok(response) => debug!("Response received: {:?}", + response.text().await.unwrap()), + Err(e) => debug!("Error on request: {:?}", e) + } + } - let request_url = format!("{}/{}/_doc/{}", address, index, self.id); - let client = Client::builder() - .danger_accept_invalid_certs(insecure) - .timeout(Duration::from_secs(30)) - .build().unwrap(); - match client - .post(request_url) - .basic_auth(user, Some(pass)) - .json(&data) - .send() - .await { - Ok(response) => debug!("Response received: {:?}", response), - Err(e) => debug!("Error on request: {:?}", e) - }; } // ------------------------------------------------------------------------ @@ -258,6 +299,8 @@ mod tests { use tokio_test::block_on; use std::fs; + //static mut GCONFIG: Option = None; + // ------------------------------------------------------------------------ fn remove_test_file(filename: String) { @@ -282,6 +325,12 @@ mod tests { } } + fn initialize() { + unsafe{ + super::super::GCONFIG = Some(config::Config::new(&utils::get_os(), None)); + } + } + // ------------------------------------------------------------------------ #[test] @@ -326,6 +375,7 @@ mod tests { #[test] fn test_send() { + initialize(); let evt = create_test_event(); block_on( evt.send( String::from("test"), String::from("https://127.0.0.1:9200"), diff --git a/src/index.rs b/src/index.rs index 7eaf455..1709ef5 100644 --- a/src/index.rs +++ b/src/index.rs @@ -7,7 +7,7 @@ use tokio_util::codec::{BytesCodec, FramedRead}; use reqwest::{Client, Body}; use reqwest::header; // To log the program process -use log::{info, debug}; +use log::{info, debug, error}; // To manage paths use std::path::Path; // Handle time intervals @@ -30,27 +30,32 @@ fn get_template_path() -> String { // ---------------------------------------------------------------------------- -pub async fn push_template(address: String, user: String, pass: String, insecure: bool){ +pub async fn push_template(){ + let config = unsafe { super::GCONFIG.clone().unwrap() }; let template_path = get_template_path(); info!("Loaded index template from: {}", template_path); let file = File::open(template_path).await.unwrap(); let stream = FramedRead::new(file, BytesCodec::new()); let body = Body::wrap_stream(stream); - let url = format!("{}/_template/fim", address); + let url = format!("{}/_template/fim", config.endpoint_address); let client = Client::builder() .timeout(Duration::from_secs(120)) - .danger_accept_invalid_certs(insecure) + .danger_accept_invalid_certs(config.insecure) .build().unwrap(); let response = client .put(url) .header(header::CONTENT_TYPE, "application/json") - .basic_auth(user, Some(pass)) + .basic_auth(config.endpoint_user, Some(config.endpoint_pass)) .body(body) .send() .await; - debug!("Push index template response: {:?}", response.unwrap().text().await); + match response { + Ok(response) => debug!("Push index template response: {:?}", + response.text().await.unwrap()), + Err(e) => error!("Error on request: {:?}", e) + } } // ---------------------------------------------------------------------------- @@ -58,12 +63,15 @@ pub async fn push_template(address: String, user: String, pass: String, insecure #[cfg(test)] mod tests { use super::*; + use crate::config; + use crate::utils; #[test] fn test_push_template() { - tokio_test::block_on( push_template( - String::from("https://127.0.0.1:9200"), - String::from("admin"), String::from("admin"), true) ); + unsafe{ + super::super::GCONFIG = Some(config::Config::new(&utils::get_os(), Some("test/unit/config/common/test_push_template.yml"))); + } + tokio_test::block_on( push_template()); } #[test] diff --git a/src/launcher.rs b/src/launcher.rs index fd104fb..a1da2c4 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -66,9 +66,11 @@ mod tests { events_watcher: String::from("Recommended"), events_destination: String::from(events_destination), events_max_file_checksum: 64, + endpoint_type: String::from("Not_defined"), endpoint_address: String::from("test"), endpoint_user: String::from("test"), endpoint_pass: String::from("test"), + endpoint_token: String::from("test"), events_file: String::from("test"), monitor: Array::new(), audit: Array::new(), diff --git a/src/monitor.rs b/src/monitor.rs index a0fe976..974d4fe 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -54,12 +54,12 @@ fn setup_events(destination: &str, config: config::Config){ // ---------------------------------------------------------------------------- -async fn push_template(destination: &str, config: config::Config){ +async fn push_template(destination: &str){ // Perform actions depending on destination match destination { config::NETWORK_MODE|config::BOTH_MODE => { // On start push template (Include check if events won't be ingested by http) - index::push_template(config.endpoint_address, config.endpoint_user, config.endpoint_pass, config.insecure).await; + index::push_template().await; }, _ => { debug!("Template not pushed in file mode"); @@ -78,7 +78,7 @@ pub async fn monitor(tx: mpsc::Sender>, setup_events(destination.as_str(), config.clone()); // Check if we have to push index template - push_template(destination.as_str(), config.clone()).await; + push_template(destination.as_str()).await; let mut watcher = MultiWatcher::new(config.events_watcher.as_str(), tx); @@ -297,8 +297,8 @@ mod tests { fn test_push_template() { let config = config::Config::new(&utils::get_os(), None); fs::create_dir_all(Path::new(&config.log_file).parent().unwrap().to_str().unwrap()).unwrap(); - block_on(push_template("file", config.clone())); - block_on(push_template("network", config.clone())); + block_on(push_template("file")); + block_on(push_template("network")); } // ------------------------------------------------------------------------ diff --git a/test/unit/config/common/test_push_template.yml b/test/unit/config/common/test_push_template.yml new file mode 100644 index 0000000..5fb8a61 --- /dev/null +++ b/test/unit/config/common/test_push_template.yml @@ -0,0 +1,22 @@ +node: "FIM" + +# Events configuration, where to store produced events +events: + destination: both + file: C:\ProgramData\fim\events.json + endpoint: + address: https://127.0.0.1:9200 + insecure: true + credentials: + user: admin + password: admin + +# Monitor folder or files. +monitor: + - path: /common/path + +# App procedure and errors logging +log: + file: / + # Available levels [debug, info, error, warning] + level: info \ No newline at end of file diff --git a/test/unit/config/linux/events_credentials_password.yml b/test/unit/config/linux/events_credentials_password.yml index 657bff5..bd07607 100644 --- a/test/unit/config/linux/events_credentials_password.yml +++ b/test/unit/config/linux/events_credentials_password.yml @@ -7,7 +7,7 @@ events: address: 0.0.0.0 credentials: user: test - password: test + password: test_password # Audit extended files and folders information audit: diff --git a/test/unit/config/linux/events_credentials_token.yml b/test/unit/config/linux/events_credentials_token.yml new file mode 100644 index 0000000..ba89cd1 --- /dev/null +++ b/test/unit/config/linux/events_credentials_token.yml @@ -0,0 +1,29 @@ +node: "FIM" + +# Events configuration, where to store produced events +events: + destination: network + endpoint: + address: 0.0.0.0 + credentials: + token: test_token + +# Audit extended files and folders information +audit: + - path: /tmp + labels: ["tmp", "linux"] + ignore: [".swp"] + +# Simple files and folders information +monitor: + - path: /bin/ + - path: /usr/bin/ + labels: ["usr/bin", "linux"] + - path: /etc + labels: ["etc", "linux"] + +# App procedure and errors logging +log: + file: /var/log/fim/fim.log + # Available levels [debug, info, error, warning] + level: info \ No newline at end of file diff --git a/test/unit/config/linux/events_credentials_user.yml b/test/unit/config/linux/events_credentials_user.yml index 657bff5..aef0eaf 100644 --- a/test/unit/config/linux/events_credentials_user.yml +++ b/test/unit/config/linux/events_credentials_user.yml @@ -6,7 +6,7 @@ events: endpoint: address: 0.0.0.0 credentials: - user: test + user: test_user password: test # Audit extended files and folders information diff --git a/test/unit/config/windows/events_credentials_password.yml b/test/unit/config/windows/events_credentials_password.yml index bbea6c6..b177cd8 100644 --- a/test/unit/config/windows/events_credentials_password.yml +++ b/test/unit/config/windows/events_credentials_password.yml @@ -7,7 +7,7 @@ events: address: 0.0.0.0 credentials: user: test - password: test + password: test_password # Monitor folder or files. monitor: diff --git a/test/unit/config/windows/events_credentials_token.yml b/test/unit/config/windows/events_credentials_token.yml new file mode 100644 index 0000000..8457f6b --- /dev/null +++ b/test/unit/config/windows/events_credentials_token.yml @@ -0,0 +1,22 @@ +node: "FIM" + +# Events configuration, where to store produced events +events: + destination: network + endpoint: + address: 0.0.0.0 + credentials: + token: test_token + +# Monitor folder or files. +monitor: + - path: C:\Program Files\ + labels: ["Program Files", "windows"] + - path: C:\Users\ + labels: ["Users", "windows"] + +# App procedure and errors logging +log: + file: C:\ProgramData\fim\fim.log + # Available levels [debug, info, error, warning] + level: info \ No newline at end of file diff --git a/test/unit/config/windows/events_credentials_user.yml b/test/unit/config/windows/events_credentials_user.yml index bbea6c6..86017cb 100644 --- a/test/unit/config/windows/events_credentials_user.yml +++ b/test/unit/config/windows/events_credentials_user.yml @@ -6,7 +6,7 @@ events: endpoint: address: 0.0.0.0 credentials: - user: test + user: test_user password: test # Monitor folder or files. From 6aa7cb2fd57d6482942bee520e9750a8b9e079a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fern=C3=A1ndez?= Date: Wed, 26 Jul 2023 14:37:10 +0200 Subject: [PATCH 2/4] Added unit tests coverage --- src/auditevent.rs | 32 +++++++++++++------- src/event.rs | 32 +++++++++++++------- test/unit/config/common/test_send_splunk.yml | 19 ++++++++++++ 3 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 test/unit/config/common/test_send_splunk.yml diff --git a/src/auditevent.rs b/src/auditevent.rs index ea245c6..704778d 100644 --- a/src/auditevent.rs +++ b/src/auditevent.rs @@ -353,7 +353,7 @@ impl Event { // ------------------------------------------------------------------------ // Function to send events through network - pub async fn send(&self, index: String, address: String, user: String, pass: String, insecure: bool) { + pub async fn send(&self, index: String) { let event = self.get_json(); let config = unsafe { super::GCONFIG.clone().unwrap() }; // Splunk endpoint integration @@ -365,9 +365,9 @@ impl Event { "index": "fim_events" }); debug!("Sending received event to Splunk integration, event: {}", data); - let request_url = format!("{}/services/collector/event", address); + let request_url = format!("{}/services/collector/event", config.endpoint_address); let client = Client::builder() - .danger_accept_invalid_certs(insecure) + .danger_accept_invalid_certs(config.insecure) .timeout(Duration::from_secs(30)) .build().unwrap(); match client @@ -382,14 +382,14 @@ impl Event { } // Elastic endpoint integration } else { - let request_url = format!("{}/{}/_doc/{}", address, index, self.id); + let request_url = format!("{}/{}/_doc/{}", config.endpoint_address, index, self.id); let client = Client::builder() - .danger_accept_invalid_certs(insecure) + .danger_accept_invalid_certs(config.insecure) .timeout(Duration::from_secs(30)) .build().unwrap(); match client .post(request_url) - .basic_auth(user, Some(pass)) + .basic_auth(config.endpoint_user, Some(config.endpoint_pass)) .json(&event) .send() .await { @@ -407,10 +407,10 @@ impl Event { match destination { config::BOTH_MODE => { self.log(&config.events_file); - self.send( index_name, config.endpoint_address, config.endpoint_user, config.endpoint_pass, config.insecure).await; + self.send(index_name).await; }, config::NETWORK_MODE => { - self.send( index_name, config.endpoint_address, config.endpoint_user, config.endpoint_pass, config.insecure).await; + self.send(index_name).await; }, _ => self.log(&config.events_file) } @@ -947,9 +947,19 @@ mod tests { fn test_send() { initialize(); let event = create_test_event(); - block_on( event.send( - String::from("test"), String::from("https://127.0.0.1:9200"), - String::from("admin"), String::from("admin"), true) ); + block_on( event.send(String::from("test")) ); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_send_splunk() { + initialize(); + let evt = create_test_event(); + unsafe { + super::super::GCONFIG = Some(config::Config::new(&utils::get_os(), Some("test/unit/config/common/test_send_splunk.yml"))); + } + block_on( evt.send(String::from("test")) ); } // ------------------------------------------------------------------------ diff --git a/src/event.rs b/src/event.rs index 5c758c3..a7211ee 100644 --- a/src/event.rs +++ b/src/event.rs @@ -97,7 +97,7 @@ impl Event { // ------------------------------------------------------------------------ // Function to send events through network - pub async fn send(&self, index: String, address: String, user: String, pass: String, insecure: bool) { + pub async fn send(&self, index: String) { let config = unsafe { super::GCONFIG.clone().unwrap() }; // Splunk endpoint integration if config.endpoint_type == "Splunk" { @@ -120,9 +120,9 @@ impl Event { "index": "fim_events" }); debug!("Sending received event to Splunk integration, event: {}", data); - let request_url = format!("{}/services/collector/event", address); + let request_url = format!("{}/services/collector/event", config.endpoint_address); let client = Client::builder() - .danger_accept_invalid_certs(insecure) + .danger_accept_invalid_certs(config.insecure) .timeout(Duration::from_secs(30)) .build().unwrap(); match client @@ -150,14 +150,14 @@ impl Event { "checksum": self.checksum.clone(), "system": self.system.clone() }); - let request_url = format!("{}/{}/_doc/{}", address, index, self.id); + let request_url = format!("{}/{}/_doc/{}", config.endpoint_address, index, self.id); let client = Client::builder() - .danger_accept_invalid_certs(insecure) + .danger_accept_invalid_certs(config.insecure) .timeout(Duration::from_secs(30)) .build().unwrap(); match client .post(request_url) - .basic_auth(user, Some(pass)) + .basic_auth(config.endpoint_user, Some(config.endpoint_pass)) .json(&data) .send() .await { @@ -176,10 +176,10 @@ impl Event { match destination { config::BOTH_MODE => { self.log(config.events_file); - self.send( index_name, config.endpoint_address, config.endpoint_user, config.endpoint_pass, config.insecure).await; + self.send(index_name).await; }, config::NETWORK_MODE => { - self.send( index_name, config.endpoint_address, config.endpoint_user, config.endpoint_pass, config.insecure).await; + self.send(index_name).await; }, _ => self.log(config.events_file) } @@ -377,9 +377,19 @@ mod tests { fn test_send() { initialize(); let evt = create_test_event(); - block_on( evt.send( - String::from("test"), String::from("https://127.0.0.1:9200"), - String::from("admin"), String::from("admin"), true) ); + block_on( evt.send(String::from("test")) ); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_send_splunk() { + initialize(); + let evt = create_test_event(); + unsafe { + super::super::GCONFIG = Some(config::Config::new(&utils::get_os(), Some("test/unit/config/common/test_send_splunk.yml"))); + } + block_on( evt.send(String::from("test")) ); } // ------------------------------------------------------------------------ diff --git a/test/unit/config/common/test_send_splunk.yml b/test/unit/config/common/test_send_splunk.yml new file mode 100644 index 0000000..215f692 --- /dev/null +++ b/test/unit/config/common/test_send_splunk.yml @@ -0,0 +1,19 @@ +node: "FIM" + +# Events configuration, where to store produced events +events: + destination: network + endpoint: + address: 0.0.0.0 + credentials: + token: test_token + +# Monitor folder or files. +monitor: + - path: /test + +# App procedure and errors logging +log: + file: /test + # Available levels [debug, info, error, warning] + level: info \ No newline at end of file From 6296d3692cdfc4636b03117e18112bc0b4259176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fern=C3=A1ndez?= Date: Wed, 26 Jul 2023 14:48:05 +0200 Subject: [PATCH 3/4] Fix version compilation error --- Cargo.toml | 2 +- pkg/deb/debian/changelog | 6 ++++++ pkg/fim.1 | 2 +- pkg/msi/fim.wxs | 2 +- pkg/rpm/fim.spec | 3 +++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90b5afc..36cca24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fim" -version = "0.4.7" +version = "0.4.8" authors = ["José Fernández <´pylott@gmail.com´>"] edition = "2021" diff --git a/pkg/deb/debian/changelog b/pkg/deb/debian/changelog index f93cc35..d96fd12 100644 --- a/pkg/deb/debian/changelog +++ b/pkg/deb/debian/changelog @@ -1,3 +1,9 @@ +fim (0.4.8-1) xenial; urgency=medium + + * More info: https://github.com/Achiefs/fim/releases/tag/v0.4.8 + + -- Jose Fernandez Fri, 21 Jul 2023 10:00:00 +0000 + fim (0.4.7-1) xenial; urgency=medium * More info: https://github.com/Achiefs/fim/releases/tag/v0.4.7 diff --git a/pkg/fim.1 b/pkg/fim.1 index 0740080..676b5c3 100644 --- a/pkg/fim.1 +++ b/pkg/fim.1 @@ -19,7 +19,7 @@ .\" * .\" ************************************************************************** .\" -.TH fim 1 "01 Jun 2022" "FIM 0.4.7" "FIM Manual" +.TH fim 1 "01 Jun 2022" "FIM 0.4.8" "FIM Manual" .SH NAME .B FIM diff --git a/pkg/msi/fim.wxs b/pkg/msi/fim.wxs index 53161e7..1fd3eeb 100644 --- a/pkg/msi/fim.wxs +++ b/pkg/msi/fim.wxs @@ -2,7 +2,7 @@ + Language='1033' Codepage='1252' Version='0.4.8'> - 0.4.8 +- More info: https://github.com/Achiefs/fim/releases/tag/v0.4.8 + * Fri May 26 2023 support - 0.4.7 - More info: https://github.com/Achiefs/fim/releases/tag/v0.4.7 From 54f6a88422d0ed51e1f20c09acc4c97f54fe93fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fern=C3=A1ndez?= Date: Wed, 26 Jul 2023 15:41:30 +0200 Subject: [PATCH 4/4] Fix initialization error in test_process unit test --- src/auditevent.rs | 2 ++ src/event.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/auditevent.rs b/src/auditevent.rs index 704778d..23b9522 100644 --- a/src/auditevent.rs +++ b/src/auditevent.rs @@ -356,6 +356,7 @@ impl Event { pub async fn send(&self, index: String) { let event = self.get_json(); let config = unsafe { super::GCONFIG.clone().unwrap() }; + // Splunk endpoint integration if config.endpoint_type == "Splunk" { let data = json!({ @@ -966,6 +967,7 @@ mod tests { #[test] fn test_process() { + initialize(); let config = Config::new(&utils::get_os(), None); let event = create_test_event(); diff --git a/src/event.rs b/src/event.rs index a7211ee..e212903 100644 --- a/src/event.rs +++ b/src/event.rs @@ -99,6 +99,7 @@ impl Event { // Function to send events through network pub async fn send(&self, index: String) { let config = unsafe { super::GCONFIG.clone().unwrap() }; + // Splunk endpoint integration if config.endpoint_type == "Splunk" { let data = json!({ @@ -504,6 +505,7 @@ mod tests { #[test] fn test_process() { + initialize(); let config = Config::new(&utils::get_os(), None); let event = create_test_event();