diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1d2459..e20dde87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + +- Added `orig_country_code` and `resp_country_code` fields to event-related + structs that contain origin and response IP addresses. These fields store + 2-letter ISO country codes as `[u8; 2]`, enabling geographic filtering and + analysis of security events. +- Updated `migrate_data_dir` function signature to accept an optional + `ip2location::DB` parameter for resolving country codes during migration + from older database formats. + ### Changed - Changed `Store::network_tag_set` signature to require a `customer_id: u32` diff --git a/Cargo.toml b/Cargo.toml index e9cda439..5831e478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ data-encoding = "2" flate2 = "1.0" ip2location = "0.6.0" ipnet = { version = "2", features = ["serde"] } +itertools = "0.14" jiff = "0.2.15" memchr = "2" num-derive = "0.4" diff --git a/src/backup.rs b/src/backup.rs index 01ce9da5..8c4d4d04 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -111,8 +111,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) diff --git a/src/event.rs b/src/event.rs index efe23d77..49331c5e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -2393,6 +2393,16 @@ impl<'a> EventDb<'a> { EventIterator { inner: iter } } + /// Creates a raw iterator over key-value pairs for the entire events. + /// + /// Unlike `iter_forward`, this returns raw bytes without deserializing the event fields. + /// This is useful for migrations that need to transform data between formats. + #[must_use] + pub fn raw_iter_forward(&self) -> RawEventIterator<'_> { + let iter = self.inner.iterator(IteratorMode::Start); + RawEventIterator { inner: iter } + } + /// Stores a new event into the database. /// /// # Errors @@ -2880,6 +2890,26 @@ pub enum InvalidEvent { Value(Box<[u8]>), } +/// A raw iterator over event key-value pairs. +/// +/// This iterator returns raw bytes without deserializing the event fields, +/// which is useful for migrations that need to transform data between formats. +#[allow(clippy::module_name_repetitions)] +pub struct RawEventIterator<'i> { + inner: rocksdb::DBIteratorWithThreadMode< + 'i, + rocksdb::OptimisticTransactionDB, + >, +} + +impl Iterator for RawEventIterator<'_> { + type Item = (Box<[u8]>, Box<[u8]>); + + fn next(&mut self) -> Option { + self.inner.next().and_then(Result::ok) + } +} + pub type Id = u32; #[derive(Clone, Deserialize, Serialize, PartialEq, Debug)] @@ -2913,7 +2943,7 @@ pub enum TrafficDirection { fn eq_ip_country(locator: &ip2location::DB, addr: IpAddr, country: [u8; 2]) -> bool { let country_code = crate::util::find_ip_country(locator, addr); - country_code.as_bytes() == country + crate::util::country_code_to_bytes(&country_code) == country } #[cfg(test)] @@ -2966,8 +2996,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -3081,8 +3113,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -3127,7 +3161,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="DomainGenerationAlgorithm" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="0.8""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="DomainGenerationAlgorithm" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="0.8""# ); let dga = DomainGenerationAlgorithm::new( @@ -3138,7 +3172,7 @@ mod tests { let dga_display = format!("{event}"); assert_eq!( &dga_display, - r#"time="1970-01-01T00:01:01+00:00" event_kind="DomainGenerationAlgorithm" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="0.8" triage_scores="""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="DomainGenerationAlgorithm" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="0.8" triage_scores="""# ); } @@ -3233,8 +3267,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -3286,7 +3322,7 @@ mod tests { assert_eq!( syslog_message, format!( - "time=\"1970-01-01T00:01:01+00:00\" event_kind=\"HttpThreat\" category=\"Reconnaissance\" sensor=\"collector1\" orig_addr=\"127.0.0.1\" orig_port=\"10000\" resp_addr=\"127.0.0.2\" resp_port=\"80\" proto=\"6\" start_time=\"{start_time}\" duration=\"0\" orig_pkts=\"0\" resp_pkts=\"0\" orig_l2_bytes=\"0\" resp_l2_bytes=\"0\" method=\"GET\" host=\"example.com\" uri=\"/uri/path\" referer=\"-\" version=\"1.1\" user_agent=\"browser\" request_len=\"100\" response_len=\"100\" status_code=\"200\" status_msg=\"-\" username=\"-\" password=\"-\" cookie=\"cookie\" content_encoding=\"encoding type\" content_type=\"content type\" cache_control=\"no cache\" filenames=\"a1,a2\" mime_types=\"b1,b2\" body=\"1234567890...\" state=\"\" db_name=\"db\" rule_id=\"12000\" matched_to=\"match\" cluster_id=\"1111\" attack_kind=\"attack\" confidence=\"0.8\"" + "time=\"1970-01-01T00:01:01+00:00\" event_kind=\"HttpThreat\" category=\"Reconnaissance\" sensor=\"collector1\" orig_addr=\"127.0.0.1\" orig_port=\"10000\" orig_country_code=\"XX\" resp_addr=\"127.0.0.2\" resp_port=\"80\" resp_country_code=\"XX\" proto=\"6\" start_time=\"{start_time}\" duration=\"0\" orig_pkts=\"0\" resp_pkts=\"0\" orig_l2_bytes=\"0\" resp_l2_bytes=\"0\" method=\"GET\" host=\"example.com\" uri=\"/uri/path\" referer=\"-\" version=\"1.1\" user_agent=\"browser\" request_len=\"100\" response_len=\"100\" status_code=\"200\" status_msg=\"-\" username=\"-\" password=\"-\" cookie=\"cookie\" content_encoding=\"encoding type\" content_type=\"content type\" cache_control=\"no cache\" filenames=\"a1,a2\" mime_types=\"b1,b2\" body=\"1234567890...\" state=\"\" db_name=\"db\" rule_id=\"12000\" matched_to=\"match\" cluster_id=\"1111\" attack_kind=\"attack\" confidence=\"0.8\"" ) ); @@ -3304,8 +3340,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -3352,7 +3390,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="NonBrowser" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="1""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="NonBrowser" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="1""# ); let non_browser = Event::NonBrowser(NonBrowser::new( @@ -3370,8 +3408,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -3418,7 +3458,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="BlocklistHttp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="1""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="BlocklistHttp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="example.com" uri="/uri/path" referer="-" version="1.1" user_agent="browser" request_len="100" response_len="100" status_code="200" status_msg="-" username="-" password="-" cookie="cookie" content_encoding="encoding type" content_type="content type" cache_control="no cache" filenames="a1,a2" mime_types="b1,b2" body="1234567890..." state="" confidence="1""# ); let blocklist_http = Event::Blocklist(RecordType::Http(BlocklistHttp::new( @@ -3437,8 +3477,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -3477,7 +3519,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="LockyRansomware" category="Impact" sensor="collector1" orig_addr="127.0.0.3" orig_port="10000" resp_addr="127.0.0.4" resp_port="53" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="locky.com" answer="1.1.1.100" trans_id="1100" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="true" rd_flag="false" ra_flag="false" ttl="120,120,120,120,120" confidence="0.8""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="LockyRansomware" category="Impact" sensor="collector1" orig_addr="127.0.0.3" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.4" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="locky.com" answer="1.1.1.100" trans_id="1100" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="true" rd_flag="false" ra_flag="false" ttl="120,120,120,120,120" confidence="0.8""# ); let locky_ransomware = Event::LockyRansomware(LockyRansomware::new( @@ -3497,8 +3539,10 @@ mod tests { let fields = PortScanFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_ports: vec![80, 443, 8000, 8080, 8888, 8443, 9000, 9001, 9002], + resp_country_code: *b"XX", start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -3525,7 +3569,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="PortScan" category="Reconnaissance" sensor="" orig_addr="127.0.0.1" resp_addr="127.0.0.2" resp_ports="80,443,8000,8080,8888,8443,9000,9001,9002" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" proto="6" confidence="0.3""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="PortScan" category="Reconnaissance" sensor="" orig_addr="127.0.0.1" orig_country_code="XX" resp_addr="127.0.0.2" resp_ports="80,443,8000,8080,8888,8443,9000,9001,9002" resp_country_code="XX" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" proto="6" confidence="0.3""# ); let port_scan = Event::PortScan(PortScan::new( @@ -3535,7 +3579,7 @@ mod tests { .to_string(); assert_eq!( &port_scan, - r#"time="1970-01-01T00:01:01+00:00" event_kind="PortScan" category="Reconnaissance" orig_addr="127.0.0.1" resp_addr="127.0.0.2" resp_ports="80,443,8000,8080,8888,8443,9000,9001,9002" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" proto="6" triage_scores="""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="PortScan" category="Reconnaissance" orig_addr="127.0.0.1" orig_country_code="XX" resp_addr="127.0.0.2" resp_ports="80,443,8000,8080,8888,8443,9000,9001,9002" resp_country_code="XX" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" proto="6" triage_scores="""# ); } @@ -3544,11 +3588,13 @@ mod tests { let fields = MultiHostPortScanFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addrs: vec![ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), ], resp_port: 80, + resp_country_codes: vec![*b"XX", *b"XX"], start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -3575,7 +3621,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="MultiHostPortScan" category="Reconnaissance" sensor="" orig_addr="127.0.0.1" resp_addrs="127.0.0.2,127.0.0.3" resp_port="80" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" confidence="0.3""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="MultiHostPortScan" category="Reconnaissance" sensor="" orig_addr="127.0.0.1" orig_country_code="XX" resp_addrs="127.0.0.2,127.0.0.3" resp_port="80" resp_country_codes="XX,XX" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" confidence="0.3""# ); let multi_host_port_scan = Event::MultiHostPortScan(MultiHostPortScan::new( @@ -3585,7 +3631,7 @@ mod tests { .to_string(); assert_eq!( &multi_host_port_scan, - r#"time="1970-01-01T00:01:01+00:00" event_kind="MultiHostPortScan" category="Reconnaissance" orig_addr="127.0.0.1" resp_addrs="127.0.0.2,127.0.0.3" resp_port="80" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" triage_scores="""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="MultiHostPortScan" category="Reconnaissance" orig_addr="127.0.0.1" orig_country_code="XX" resp_addrs="127.0.0.2,127.0.0.3" resp_port="80" resp_country_codes="XX,XX" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" triage_scores="""# ); } @@ -3597,7 +3643,9 @@ mod tests { IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), ], + orig_country_codes: vec![*b"XX", *b"XX"], resp_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + resp_country_code: *b"XX", start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -3623,7 +3671,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="ExternalDdos" category="Impact" sensor="" orig_addrs="127.0.0.2,127.0.0.3" resp_addr="127.0.0.1" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" confidence="0.3""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="ExternalDdos" category="Impact" sensor="" orig_addrs="127.0.0.2,127.0.0.3" orig_country_codes="XX,XX" resp_addr="127.0.0.1" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" confidence="0.3""# ); let external_ddos = Event::ExternalDdos(ExternalDdos::new( @@ -3633,7 +3681,7 @@ mod tests { .to_string(); assert_eq!( &external_ddos, - r#"time="1970-01-01T01:01:01+00:00" event_kind="ExternalDdos" category="Impact" orig_addrs="127.0.0.2,127.0.0.3" resp_addr="127.0.0.1" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="ExternalDdos" category="Impact" orig_addrs="127.0.0.2,127.0.0.3" orig_country_codes="XX,XX" resp_addr="127.0.0.1" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" triage_scores="""# ); } @@ -3642,8 +3690,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 68, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 67, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -3686,7 +3736,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistBootp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" resp_addr="127.0.0.2" resp_port="67" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" op="1" htype="2" hops="1" xid="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" chaddr="01:02:03:04:05:06" sname="server_name" file="boot_file_name" confidence="1""#, + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistBootp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="67" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" op="1" htype="2" hops="1" xid="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" chaddr="01:02:03:04:05:06" sname="server_name" file="boot_file_name" confidence="1""#, ); let blocklist_bootp = Event::Blocklist(RecordType::Bootp(BlocklistBootp::new( Utc.with_ymd_and_hms(1970, 1, 1, 1, 1, 1).unwrap(), @@ -3696,7 +3746,7 @@ mod tests { assert_eq!( &blocklist_bootp, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistBootp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" resp_addr="127.0.0.2" resp_port="67" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" op="1" htype="2" hops="1" xid="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" chaddr="01:02:03:04:05:06" sname="server_name" file="boot_file_name" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistBootp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="67" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" op="1" htype="2" hops="1" xid="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" chaddr="01:02:03:04:05:06" sname="server_name" file="boot_file_name" triage_scores="""# ); } @@ -3773,8 +3823,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, conn_state: "SAF".to_string(), start_time: Utc @@ -3805,7 +3857,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistConn" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" conn_state="SAF" start_time="1970-01-01T00:00:00+00:00" duration="0" service="http" orig_bytes="100" resp_bytes="100" orig_pkts="1" resp_pkts="1" orig_l2_bytes="122" resp_l2_bytes="122" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistConn" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" conn_state="SAF" start_time="1970-01-01T00:00:00+00:00" duration="0" service="http" orig_bytes="100" resp_bytes="100" orig_pkts="1" resp_pkts="1" orig_l2_bytes="122" resp_l2_bytes="122" confidence="1""# ); let blocklist_conn = Event::Blocklist(RecordType::Conn(BlocklistConn::new( @@ -3815,7 +3867,7 @@ mod tests { .to_string(); assert_eq!( &blocklist_conn, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistConn" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" conn_state="SAF" start_time="1970-01-01T00:00:00+00:00" duration="0" service="http" orig_bytes="100" resp_bytes="100" orig_pkts="1" resp_pkts="1" orig_l2_bytes="122" resp_l2_bytes="122" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistConn" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" conn_state="SAF" start_time="1970-01-01T00:00:00+00:00" duration="0" service="http" orig_bytes="100" resp_bytes="100" orig_pkts="1" resp_pkts="1" orig_l2_bytes="122" resp_l2_bytes="122" triage_scores="""# ); } @@ -3825,8 +3877,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 135, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -3857,7 +3911,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDceRpc" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="135" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" rtt="1" named_pipe="svcctl" endpoint="epmapper" operation="bind" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDceRpc" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="135" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" rtt="1" named_pipe="svcctl" endpoint="epmapper" operation="bind" confidence="1""# ); let blocklist_dce_rpc = Event::Blocklist(RecordType::DceRpc(BlocklistDceRpc::new( @@ -3867,7 +3921,7 @@ mod tests { .to_string(); assert_eq!( &blocklist_dce_rpc, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDceRpc" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="135" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" rtt="1" named_pipe="svcctl" endpoint="epmapper" operation="bind" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDceRpc" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="135" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" rtt="1" named_pipe="svcctl" endpoint="epmapper" operation="bind" triage_scores="""# ); } @@ -3876,8 +3930,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::from_str("127.0.0.1").unwrap(), orig_port: 68, + orig_country_code: *b"XX", resp_addr: IpAddr::from_str("127.0.0.2").unwrap(), resp_port: 67, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -3926,7 +3982,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDhcp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" resp_addr="127.0.0.2" resp_port="67" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" msg_type="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" subnet_mask="255.255.255.0" router="127.0.0.1" domain_name_server="127.0.0.1" req_ip_addr="127.0.0.100" lease_time="100" server_id="127.0.0.1" param_req_list="1,2,3" message="message" renewal_time="100" rebinding_time="200" class_id="MSFT 5.0" client_id_type="1" client_id="07:08:09" confidence="1""#, + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDhcp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="67" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" msg_type="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" subnet_mask="255.255.255.0" router="127.0.0.1" domain_name_server="127.0.0.1" req_ip_addr="127.0.0.100" lease_time="100" server_id="127.0.0.1" param_req_list="1,2,3" message="message" renewal_time="100" rebinding_time="200" class_id="MSFT 5.0" client_id_type="1" client_id="07:08:09" confidence="1""#, ); let blocklist_dhcp = Event::Blocklist(RecordType::Dhcp(BlocklistDhcp::new( @@ -3937,7 +3993,7 @@ mod tests { assert_eq!( &blocklist_dhcp, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDhcp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" resp_addr="127.0.0.2" resp_port="67" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" msg_type="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" subnet_mask="255.255.255.0" router="127.0.0.1" domain_name_server="127.0.0.1" req_ip_addr="127.0.0.100" lease_time="100" server_id="127.0.0.1" param_req_list="1,2,3" message="message" renewal_time="100" rebinding_time="200" class_id="MSFT 5.0" client_id_type="1" client_id="07:08:09" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDhcp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="68" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="67" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" msg_type="1" ciaddr="127.0.0.5" yiaddr="127.0.0.6" siaddr="127.0.0.7" giaddr="127.0.0.8" subnet_mask="255.255.255.0" router="127.0.0.1" domain_name_server="127.0.0.1" req_ip_addr="127.0.0.100" lease_time="100" server_id="127.0.0.1" param_req_list="1,2,3" message="message" renewal_time="100" rebinding_time="200" class_id="MSFT 5.0" client_id_type="1" client_id="07:08:09" triage_scores="""# ); } @@ -4014,8 +4070,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -4054,7 +4112,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="DnsCovertChannel" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" confidence="0.9""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="DnsCovertChannel" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" confidence="0.9""# ); let triage_scores = vec![TriageScore { @@ -4070,7 +4128,7 @@ mod tests { assert_eq!( &dns_covert_channel, - r#"time="1970-01-01T01:01:01+00:00" event_kind="DnsCovertChannel" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" confidence="0.9" triage_scores="109:0.90""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="DnsCovertChannel" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" confidence="0.9" triage_scores="109:0.90""# ); } @@ -4080,8 +4138,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -4121,7 +4181,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="CryptocurrencyMiningPool" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" coins="bitcoin,monero" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="CryptocurrencyMiningPool" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" coins="bitcoin,monero" confidence="1""# ); let cryptocurrency_mining_pool = @@ -4132,7 +4192,7 @@ mod tests { .to_string(); assert_eq!( &cryptocurrency_mining_pool, - r#"time="1970-01-01T01:01:01+00:00" event_kind="CryptocurrencyMiningPool" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" coins="bitcoin,monero" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="CryptocurrencyMiningPool" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" coins="bitcoin,monero" triage_scores="""# ); } @@ -4142,8 +4202,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -4182,7 +4244,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" confidence="1""# ); let blocklist_dns = Event::Blocklist(RecordType::Dns(BlocklistDns::new( Utc.with_ymd_and_hms(1970, 1, 1, 1, 1, 1).unwrap(), @@ -4191,7 +4253,7 @@ mod tests { .to_string(); assert_eq!( &blocklist_dns, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" query="foo.com" answer="10.10.10.10,20.20.20.20" trans_id="123" rtt="1" qclass="0" qtype="0" rcode="0" aa_flag="false" tc_flag="false" rd_flag="false" ra_flag="true" ttl="120,120,120,120,120" triage_scores="""# ); } @@ -4200,8 +4262,10 @@ mod tests { let fields = FtpBruteForceFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 21, + resp_country_code: *b"XX", proto: 6, user_list: vec!["user1".to_string(), "user_2".to_string()], start_time: Utc @@ -4230,7 +4294,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="FtpBruteForce" category="CredentialAccess" sensor="" orig_addr="127.0.0.1" resp_addr="127.0.0.2" resp_port="21" proto="6" user_list="user1,user_2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" is_internal="true" confidence="0.3""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="FtpBruteForce" category="CredentialAccess" sensor="" orig_addr="127.0.0.1" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="21" resp_country_code="XX" proto="6" user_list="user1,user_2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" is_internal="true" confidence="0.3""# ); let ftp_brute_force = Event::FtpBruteForce(FtpBruteForce::new( @@ -4241,7 +4305,7 @@ mod tests { assert_eq!( &ftp_brute_force, - r#"time="1970-01-01T00:01:01+00:00" event_kind="FtpBruteForce" category="CredentialAccess" orig_addr="127.0.0.1" resp_addr="127.0.0.2" resp_port="21" proto="6" user_list="user1,user_2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" is_internal="true" triage_scores="""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="FtpBruteForce" category="CredentialAccess" orig_addr="127.0.0.1" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="21" resp_country_code="XX" proto="6" user_list="user1,user_2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" is_internal="true" triage_scores="""# ); } @@ -4266,8 +4330,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 21, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -4297,7 +4363,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="FtpPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="21" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="FtpPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="21" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" confidence="1""# ); let ftp_plain_text = Event::FtpPlainText(FtpPlainText::new( @@ -4307,7 +4373,7 @@ mod tests { .to_string(); assert_eq!( &ftp_plain_text, - r#"time="1970-01-01T01:01:01+00:00" event_kind="FtpPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="21" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="FtpPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="21" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" triage_scores="""# ); } @@ -4330,8 +4396,10 @@ mod tests { FtpEventFields { orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 21, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -4367,7 +4435,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistFtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="21" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistFtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="21" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" confidence="1""# ); let blocklist_ftp = Event::Blocklist(RecordType::Ftp(BlocklistFtp::new( @@ -4378,7 +4446,7 @@ mod tests { assert_eq!( &blocklist_ftp, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistFtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="21" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistFtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="21" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" user="user1" password="password" commands="ls:200:OK" triage_scores="""# ); } @@ -4460,8 +4528,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 443, + resp_country_code: *b"XX", proto: 6, start_time: now, end_time: now, @@ -4480,7 +4550,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="RepeatedHttpSessions" category="Exfiltration" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T01:01:01+00:00" end_time="1970-01-01T01:01:01+00:00" confidence="0.3""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="RepeatedHttpSessions" category="Exfiltration" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T01:01:01+00:00" end_time="1970-01-01T01:01:01+00:00" confidence="0.3""# ); let repeated_http_sessions = Event::RepeatedHttpSessions(RepeatedHttpSessions::new( Utc.with_ymd_and_hms(1970, 1, 1, 1, 1, 1).unwrap(), @@ -4489,7 +4559,7 @@ mod tests { .to_string(); assert_eq!( &repeated_http_sessions, - r#"time="1970-01-01T01:01:01+00:00" event_kind="RepeatedHttpSessions" category="Exfiltration" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T01:01:01+00:00" end_time="1970-01-01T01:01:01+00:00" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="RepeatedHttpSessions" category="Exfiltration" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T01:01:01+00:00" end_time="1970-01-01T01:01:01+00:00" triage_scores="""# ); } @@ -4499,8 +4569,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 88, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -4536,7 +4608,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistKerberos" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="88" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client_time="100" server_time="101" error_code="0" client_realm="EXAMPLE.COM" cname_type="1" client_name="user1" realm="EXAMPLE.COM" sname_type="1" service_name="krbtgt/EXAMPLE.COM" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistKerberos" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="88" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client_time="100" server_time="101" error_code="0" client_realm="EXAMPLE.COM" cname_type="1" client_name="user1" realm="EXAMPLE.COM" sname_type="1" service_name="krbtgt/EXAMPLE.COM" confidence="1""# ); let blocklist_kerberos = Event::Blocklist(RecordType::Kerberos(BlocklistKerberos::new( @@ -4547,7 +4619,7 @@ mod tests { assert_eq!( &blocklist_kerberos, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistKerberos" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="88" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client_time="100" server_time="101" error_code="0" client_realm="EXAMPLE.COM" cname_type="1" client_name="user1" realm="EXAMPLE.COM" sname_type="1" service_name="krbtgt/EXAMPLE.COM" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistKerberos" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="88" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client_time="100" server_time="101" error_code="0" client_realm="EXAMPLE.COM" cname_type="1" client_name="user1" realm="EXAMPLE.COM" sname_type="1" service_name="krbtgt/EXAMPLE.COM" triage_scores="""# ); } @@ -4556,8 +4628,10 @@ mod tests { let fields = LdapBruteForceFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 389, + resp_country_code: *b"XX", proto: 6, user_pw_list: vec![ ("user1".to_string(), "pw1".to_string()), @@ -4588,7 +4662,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="LdapBruteForce" category="CredentialAccess" sensor="" orig_addr="127.0.0.1" resp_addr="127.0.0.2" resp_port="389" proto="6" user_pw_list="user1:pw1,user_2:pw2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" confidence="0.3""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="LdapBruteForce" category="CredentialAccess" sensor="" orig_addr="127.0.0.1" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="389" resp_country_code="XX" proto="6" user_pw_list="user1:pw1,user_2:pw2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" confidence="0.3""# ); let ldap_brute_force = Event::LdapBruteForce(LdapBruteForce::new( @@ -4599,7 +4673,7 @@ mod tests { assert_eq!( &ldap_brute_force, - r#"time="1970-01-01T00:01:01+00:00" event_kind="LdapBruteForce" category="CredentialAccess" orig_addr="127.0.0.1" resp_addr="127.0.0.2" resp_port="389" proto="6" user_pw_list="user1:pw1,user_2:pw2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" triage_scores="""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="LdapBruteForce" category="CredentialAccess" orig_addr="127.0.0.1" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="389" resp_country_code="XX" proto="6" user_pw_list="user1:pw1,user_2:pw2" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:01:02+00:00" triage_scores="""# ); } @@ -4609,8 +4683,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 389, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -4644,7 +4720,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="LdapPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="389" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" message_id="1" version="3" opcode="bind" result="success" diagnostic_message="msg" object="object" argument="argument" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="LdapPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="389" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" message_id="1" version="3" opcode="bind" result="success" diagnostic_message="msg" object="object" argument="argument" confidence="1""# ); let ldap_plain_text = Event::LdapPlainText(LdapPlainText::new( @@ -4655,7 +4731,7 @@ mod tests { assert_eq!( &ldap_plain_text, - r#"time="1970-01-01T01:01:01+00:00" event_kind="LdapPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="389" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" message_id="1" version="3" opcode="bind" result="success" diagnostic_message="msg" object="object" argument="argument" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="LdapPlainText" category="LateralMovement" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="389" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" message_id="1" version="3" opcode="bind" result="success" diagnostic_message="msg" object="object" argument="argument" triage_scores="""# ); } @@ -4664,8 +4740,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 389, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -4704,7 +4782,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistLdap" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="389" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" message_id="1" version="3" opcode="bind" result="success" diagnostic_message="msg" object="object" argument="argument" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistLdap" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="389" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" message_id="1" version="3" opcode="bind" result="success" diagnostic_message="msg" object="object" argument="argument" confidence="1""# ); let blocklist_ldap = Event::Blocklist(RecordType::Ldap(BlocklistLdap::new( @@ -4791,8 +4869,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 1812, + resp_country_code: *b"XX", proto: 17, start_time: 0, duration: 0, @@ -4924,8 +5004,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 1883, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -4958,7 +5040,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMqtt" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="1883" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="mqtt" version="211" client_id="client1" connack_reason="0" subscribe="topic" suback_reason="error" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMqtt" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="1883" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="mqtt" version="211" client_id="client1" connack_reason="0" subscribe="topic" suback_reason="error" confidence="1""# ); let blocklist_mqtt = Event::Blocklist(RecordType::Mqtt(BlocklistMqtt::new( @@ -4969,7 +5051,7 @@ mod tests { assert_eq!( &blocklist_mqtt, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMqtt" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="1883" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="mqtt" version="211" client_id="client1" connack_reason="0" subscribe="topic" suback_reason="error" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMqtt" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="1883" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="mqtt" version="211" client_id="client1" connack_reason="0" subscribe="topic" suback_reason="error" triage_scores="""# ); } @@ -4980,8 +5062,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, service: "http".to_string(), start_time: Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(), @@ -5012,7 +5096,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="NetworkThreat" category="Reconnaissance" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="80" proto="6" service="http" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" content="content" db_name="db_name" rule_id="1" matched_to="matched_to" cluster_id="1" attack_kind="attack_kind" confidence="0.9""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="NetworkThreat" category="Reconnaissance" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="80" resp_country_code="XX" proto="6" service="http" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" content="content" db_name="db_name" rule_id="1" matched_to="matched_to" cluster_id="1" attack_kind="attack_kind" confidence="0.9""# ); } @@ -5022,8 +5106,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 2049, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5052,7 +5138,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNfs" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="2049" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" read_files="/etc/passwd" write_files="/etc/shadow" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNfs" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="2049" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" read_files="/etc/passwd" write_files="/etc/shadow" confidence="1""# ); let blocklist_nfs = Event::Blocklist(RecordType::Nfs(BlocklistNfs::new( @@ -5063,7 +5149,7 @@ mod tests { assert_eq!( &blocklist_nfs, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNfs" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="2049" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" read_files="/etc/passwd" write_files="/etc/shadow" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNfs" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="2049" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" read_files="/etc/passwd" write_files="/etc/shadow" triage_scores="""# ); } @@ -5073,8 +5159,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 445, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5106,7 +5194,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNtlm" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="445" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="ntlm" username="user1" hostname="host1" domainname="domain1" success="true" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNtlm" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="445" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="ntlm" username="user1" hostname="host1" domainname="domain1" success="true" confidence="1""# ); let blocklist_ntlm = Event::Blocklist(RecordType::Ntlm(BlocklistNtlm::new( @@ -5117,7 +5205,7 @@ mod tests { assert_eq!( &blocklist_ntlm, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNtlm" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="445" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="ntlm" username="user1" hostname="host1" domainname="domain1" success="true" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistNtlm" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="445" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" protocol="ntlm" username="user1" hostname="host1" domainname="domain1" success="true" triage_scores="""# ); } @@ -5136,7 +5224,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRadius" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="1812" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" id="1" code="1" resp_code="2" auth="auth_string" resp_auth="resp_auth_string" user_name="user1" user_passwd="password" chap_passwd="chap_pass" nas_ip="127.0.0.3" nas_port="5060" state="state" nas_id="nas_identifier" nas_port_type="15" message="RADIUS message" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRadius" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="1812" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" id="1" code="1" resp_code="2" auth="auth_string" resp_auth="resp_auth_string" user_name="user1" user_passwd="password" chap_passwd="chap_pass" nas_ip="127.0.0.3" nas_port="5060" state="state" nas_id="nas_identifier" nas_port_type="15" message="RADIUS message" confidence="1""# ); let blocklist_radius = Event::Blocklist(RecordType::Radius(BlocklistRadius::new( @@ -5147,7 +5235,7 @@ mod tests { assert_eq!( &blocklist_radius, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRadius" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="1812" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" id="1" code="1" resp_code="2" auth="auth_string" resp_auth="resp_auth_string" user_name="user1" user_passwd="password" chap_passwd="chap_pass" nas_ip="127.0.0.3" nas_port="5060" state="state" nas_id="nas_identifier" nas_port_type="15" message="RADIUS message" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRadius" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="1812" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" id="1" code="1" resp_code="2" auth="auth_string" resp_auth="resp_auth_string" user_name="user1" user_passwd="password" chap_passwd="chap_pass" nas_ip="127.0.0.3" nas_port="5060" state="state" nas_id="nas_identifier" nas_port_type="15" message="RADIUS message" triage_scores="""# ); } @@ -5156,8 +5244,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5268,7 +5358,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMalformedDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="1000000000" orig_pkts="10" resp_pkts="5" orig_l2_bytes="500" resp_l2_bytes="300" trans_id="1234" flags="33152" question_count="1" answer_count="1" authority_count="0" additional_count="0" query_count="1" resp_count="1" query_bytes="50" resp_bytes="100" query_body="example.com" resp_body="192.0.2.1" confidence="0.95""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMalformedDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="1000000000" orig_pkts="10" resp_pkts="5" orig_l2_bytes="500" resp_l2_bytes="300" trans_id="1234" flags="33152" question_count="1" answer_count="1" authority_count="0" additional_count="0" query_count="1" resp_count="1" query_bytes="50" resp_bytes="100" query_body="example.com" resp_body="192.0.2.1" confidence="0.95""# ); let blocklist_malformed_dns = Event::Blocklist(RecordType::MalformedDns( @@ -5278,7 +5368,7 @@ mod tests { assert_eq!( &blocklist_malformed_dns, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMalformedDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="53" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="1000000000" orig_pkts="10" resp_pkts="5" orig_l2_bytes="500" resp_l2_bytes="300" trans_id="1234" flags="33152" question_count="1" answer_count="1" authority_count="0" additional_count="0" query_count="1" resp_count="1" query_bytes="50" resp_bytes="100" query_body="example.com" resp_body="192.0.2.1" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistMalformedDns" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="53" resp_country_code="XX" proto="17" start_time="1970-01-01T00:00:00+00:00" duration="1000000000" orig_pkts="10" resp_pkts="5" orig_l2_bytes="500" resp_l2_bytes="300" trans_id="1234" flags="33152" question_count="1" answer_count="1" authority_count="0" additional_count="0" query_count="1" resp_count="1" query_bytes="50" resp_bytes="100" query_body="example.com" resp_body="192.0.2.1" triage_scores="""# ); } @@ -5287,10 +5377,12 @@ mod tests { let fields = RdpBruteForceFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addrs: vec![ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), ], + resp_country_codes: vec![*b"XX", *b"XX"], start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -5317,7 +5409,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T00:01:01+00:00" event_kind="RdpBruteForce" category="Discovery" sensor="" orig_addr="127.0.0.1" resp_addrs="127.0.0.2,127.0.0.3" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:10:02+00:00" proto="6" confidence="0.3""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="RdpBruteForce" category="Discovery" sensor="" orig_addr="127.0.0.1" orig_country_code="XX" resp_addrs="127.0.0.2,127.0.0.3" resp_country_codes="XX,XX" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:10:02+00:00" proto="6" confidence="0.3""# ); let rdp_brute_force = Event::RdpBruteForce(RdpBruteForce::new( @@ -5328,7 +5420,7 @@ mod tests { assert_eq!( &rdp_brute_force, - r#"time="1970-01-01T00:01:01+00:00" event_kind="RdpBruteForce" category="Discovery" orig_addr="127.0.0.1" resp_addrs="127.0.0.2,127.0.0.3" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:10:02+00:00" proto="6" triage_scores="""# + r#"time="1970-01-01T00:01:01+00:00" event_kind="RdpBruteForce" category="Discovery" orig_addr="127.0.0.1" orig_country_code="XX" resp_addrs="127.0.0.2,127.0.0.3" resp_country_codes="XX,XX" start_time="1970-01-01T00:01:01+00:00" end_time="1970-01-01T00:10:02+00:00" proto="6" triage_scores="""# ); } @@ -5338,8 +5430,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 3389, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5367,7 +5461,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRdp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="3389" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" cookie="cookie" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRdp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="3389" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" cookie="cookie" confidence="1""# ); let blocklist_rdp = Event::Blocklist(RecordType::Rdp(BlocklistRdp::new( @@ -5378,7 +5472,7 @@ mod tests { assert_eq!( &blocklist_rdp, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRdp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="3389" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" cookie="cookie" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistRdp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="3389" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" cookie="cookie" triage_scores="""# ); } @@ -5388,8 +5482,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 445, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5427,7 +5523,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmb" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="445" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" command="1" path="path" service="service" file_name="file_name" file_size="100" resource_type="1" fid="1" create_time="100" access_time="200" write_time="300" change_time="400" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmb" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="445" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" command="1" path="path" service="service" file_name="file_name" file_size="100" resource_type="1" fid="1" create_time="100" access_time="200" write_time="300" change_time="400" confidence="1""# ); let blocklist_smb = Event::Blocklist(RecordType::Smb(BlocklistSmb::new( @@ -5438,7 +5534,7 @@ mod tests { assert_eq!( &blocklist_smb, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmb" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="445" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" command="1" path="path" service="service" file_name="file_name" file_size="100" resource_type="1" fid="1" create_time="100" access_time="200" write_time="300" change_time="400" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmb" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="445" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" command="1" path="path" service="service" file_name="file_name" file_size="100" resource_type="1" fid="1" create_time="100" access_time="200" write_time="300" change_time="400" triage_scores="""# ); } @@ -5448,8 +5544,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 25, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5483,7 +5581,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="25" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" mailfrom="mailfrom" date="date" from="from" to="to" subject="subject" agent="agent" state="state" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="25" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" mailfrom="mailfrom" date="date" from="from" to="to" subject="subject" agent="agent" state="state" confidence="1""# ); let blocklist_smtp = Event::Blocklist(RecordType::Smtp(BlocklistSmtp::new( @@ -5494,7 +5592,7 @@ mod tests { assert_eq!( &blocklist_smtp, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="25" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" mailfrom="mailfrom" date="date" from="from" to="to" subject="subject" agent="agent" state="state" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSmtp" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="25" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" mailfrom="mailfrom" date="date" from="from" to="to" subject="subject" agent="agent" state="state" triage_scores="""# ); } @@ -5504,8 +5602,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 22, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5545,7 +5645,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSsh" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="22" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client="client" server="server" cipher_alg="cipher_alg" mac_alg="mac_alg" compression_alg="compression_alg" kex_alg="kex_alg" host_key_alg="host_key_alg" hassh_algorithms="hassh_algorithms" hassh="hassh" hassh_server_algorithms="hassh_server_algorithms" hassh_server="hassh_server" client_shka="client_shka" server_shka="server_shka" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSsh" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="22" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client="client" server="server" cipher_alg="cipher_alg" mac_alg="mac_alg" compression_alg="compression_alg" kex_alg="kex_alg" host_key_alg="host_key_alg" hassh_algorithms="hassh_algorithms" hassh="hassh" hassh_server_algorithms="hassh_server_algorithms" hassh_server="hassh_server" client_shka="client_shka" server_shka="server_shka" confidence="1""# ); let blocklist_ssh = Event::Blocklist(RecordType::Ssh(BlocklistSsh::new( @@ -5556,7 +5656,7 @@ mod tests { assert_eq!( &blocklist_ssh, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSsh" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="22" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client="client" server="server" cipher_alg="cipher_alg" mac_alg="mac_alg" compression_alg="compression_alg" kex_alg="kex_alg" host_key_alg="host_key_alg" hassh_algorithms="hassh_algorithms" hassh="hassh" hassh_server_algorithms="hassh_server_algorithms" hassh_server="hassh_server" client_shka="client_shka" server_shka="server_shka" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistSsh" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="22" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" client="client" server="server" cipher_alg="cipher_alg" mac_alg="mac_alg" compression_alg="compression_alg" kex_alg="kex_alg" host_key_alg="host_key_alg" hassh_algorithms="hassh_algorithms" hassh="hassh" hassh_server_algorithms="hassh_server_algorithms" hassh_server="hassh_server" client_shka="client_shka" server_shka="server_shka" triage_scores="""# ); } @@ -5620,8 +5720,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 443, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5669,7 +5771,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistTls" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistTls" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9""# ); let blocklist_tls = Event::Blocklist(RecordType::Tls(BlocklistTls::new( @@ -5680,7 +5782,7 @@ mod tests { assert_eq!( &blocklist_tls, - r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistTls" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="BlocklistTls" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9" triage_scores="""# ); } @@ -5699,8 +5801,10 @@ mod tests { resp_l2_bytes: 0, orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 443, + resp_country_code: *b"XX", proto: 6, method: "GET".to_string(), host: "host".to_string(), @@ -5742,7 +5846,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="TorConnection" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="host" uri="uri" referer="referer" version="version" user_agent="user_agent" request_len="100" response_len="200" status_code="200" status_msg="OK" username="user" password="password" cookie="cookie" content_encoding="content_encoding" content_type="content_type" cache_control="cache_control" filenames="filename" mime_types="mime_type" body="post_body" state="state" confidence="1""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="TorConnection" category="CommandAndControl" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T00:01:01+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" method="GET" host="host" uri="uri" referer="referer" version="version" user_agent="user_agent" request_len="100" response_len="200" status_code="200" status_msg="OK" username="user" password="password" cookie="cookie" content_encoding="content_encoding" content_type="content_type" cache_control="cache_control" filenames="filename" mime_types="mime_type" body="post_body" state="state" confidence="1""# ); let tor_connection = Event::TorConnection(TorConnection::new( @@ -5829,8 +5933,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 443, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -5884,7 +5990,7 @@ mod tests { let (_, _, syslog_message) = message.unwrap(); assert_eq!( &syslog_message, - r#"time="1970-01-01T01:01:01+00:00" event_kind="SuspiciousTlsTraffic" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="SuspiciousTlsTraffic" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9""# ); let suspicious_tls_traffic = @@ -5909,7 +6015,7 @@ mod tests { assert_eq!( &blocklist_tls, - r#"time="1970-01-01T01:01:01+00:00" event_kind="SuspiciousTlsTraffic" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" resp_addr="127.0.0.2" resp_port="443" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9" triage_scores="""# + r#"time="1970-01-01T01:01:01+00:00" event_kind="SuspiciousTlsTraffic" category="InitialAccess" sensor="collector1" orig_addr="127.0.0.1" orig_port="10000" orig_country_code="XX" resp_addr="127.0.0.2" resp_port="443" resp_country_code="XX" proto="6" start_time="1970-01-01T00:00:00+00:00" duration="0" orig_pkts="0" resp_pkts="0" orig_l2_bytes="0" resp_l2_bytes="0" server_name="server" alpn_protocol="alpn" ja3="ja3" version="version" client_cipher_suites="1,2,3" client_extensions="4,5,6" cipher="1" extensions="7,8,9" ja3s="ja3s" serial="serial" subject_country="country" subject_org_name="org" subject_common_name="common" validity_not_before="100" validity_not_after="200" subject_alt_name="alt" issuer_country="country" issuer_org_name="org" issuer_org_unit_name="unit" issuer_common_name="common" last_alert="1" confidence="0.9" triage_scores="""# ); } diff --git a/src/event/bootp.rs b/src/event/bootp.rs index 4d49569e..2ae2eb12 100644 --- a/src/event/bootp.rs +++ b/src/event/bootp.rs @@ -40,14 +40,14 @@ macro_rules! find_bootp_attr_by_kind { }}; } -pub type BlocklistBootpFields = BlocklistBootpFieldsV0_42; +pub type BlocklistBootpFields = BlocklistBootpFieldsV0_44; impl BlocklistBootpFields { #[must_use] pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} op={:?} htype={:?} hops={:?} xid={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} chaddr={:?} sname={:?} file={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} op={:?} htype={:?} hops={:?} xid={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} chaddr={:?} sname={:?} file={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -55,8 +55,10 @@ impl BlocklistBootpFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -81,12 +83,14 @@ impl BlocklistBootpFields { } #[derive(Serialize, Deserialize)] -pub struct BlocklistBootpFieldsV0_42 { +pub struct BlocklistBootpFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -116,8 +120,10 @@ pub struct BlocklistBootp { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -144,12 +150,14 @@ impl fmt::Display for BlocklistBootp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} op={:?} htype={:?} hops={:?} xid={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} chaddr={:?} sname={:?} file={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} op={:?} htype={:?} hops={:?} xid={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} chaddr={:?} sname={:?} file={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -180,8 +188,10 @@ impl BlocklistBootp { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/common.rs b/src/event/common.rs index 470b7651..ad830e84 100644 --- a/src/event/common.rs +++ b/src/event/common.rs @@ -1606,8 +1606,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 68, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 67, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1640,8 +1642,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, conn_state: "SAF".to_string(), start_time: Utc @@ -1667,8 +1671,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 135, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1694,8 +1700,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 68, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 67, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1736,8 +1744,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1771,8 +1781,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1814,8 +1826,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 88, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1846,8 +1860,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 1883, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1875,8 +1891,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 2049, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1900,8 +1918,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 445, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1928,8 +1948,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 3389, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1952,8 +1974,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 445, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -1986,8 +2010,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 25, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2016,8 +2042,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 22, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2052,8 +2080,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 443, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2096,8 +2126,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 389, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -2141,8 +2173,10 @@ mod tests { sensor: "collector1".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 21, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -2166,8 +2200,10 @@ mod tests { PortScanFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_ports: vec![80, 443, 8000, 8080, 8888, 8443, 9000, 9001, 9002], + resp_country_code: *b"XX", start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -2188,11 +2224,13 @@ mod tests { MultiHostPortScanFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addrs: vec![ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), ], resp_port: 80, + resp_country_codes: vec![*b"XX", *b"XX"], start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -2216,7 +2254,9 @@ mod tests { IpAddr::V4(Ipv4Addr::LOCALHOST), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), ], + orig_country_codes: vec![*b"XX", *b"XX"], resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), + resp_country_code: *b"XX", start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -2238,8 +2278,10 @@ mod tests { sensor: "sensro".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -2273,8 +2315,10 @@ mod tests { FtpBruteForceFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 21, + resp_country_code: *b"XX", proto: 6, user_list: vec!["user1".to_string(), "user_2".to_string()], start_time: Utc @@ -2299,8 +2343,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 443, + resp_country_code: *b"XX", proto: 6, start_time: now, end_time: now, @@ -2314,8 +2360,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2357,8 +2405,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -2399,8 +2449,10 @@ mod tests { LdapBruteForceFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 389, + resp_country_code: *b"XX", proto: 6, user_pw_list: vec![ ("user1".to_string(), "pw1".to_string()), @@ -2425,10 +2477,12 @@ mod tests { RdpBruteForceFields { sensor: String::new(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + orig_country_code: *b"XX", resp_addrs: vec![ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), ], + resp_country_codes: vec![*b"XX", *b"XX"], start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) .unwrap() @@ -2450,8 +2504,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) @@ -2486,8 +2542,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, service: "http".to_string(), start_time: Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(), @@ -2554,8 +2612,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 80, + resp_country_code: *b"XX", proto: 6, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2601,8 +2661,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 1812, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2638,8 +2700,10 @@ mod tests { sensor: "sensor".to_string(), orig_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), orig_port: 10000, + orig_country_code: *b"XX", resp_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), resp_port: 53, + resp_country_code: *b"XX", proto: 17, start_time: Utc .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) @@ -2682,6 +2746,7 @@ mod tests { .timestamp_nanos_opt() .unwrap(), destination_ips: vec![IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))], + resp_country_codes: vec![*b"XX"], count: 1, expected_mean: 0.0, std_deviation: 0.0, diff --git a/src/event/conn.rs b/src/event/conn.rs index de8adeae..e5a1ae49 100644 --- a/src/event/conn.rs +++ b/src/event/conn.rs @@ -2,6 +2,7 @@ use std::{fmt, net::IpAddr, num::NonZeroU8}; use attrievent::attribute::{ConnAttr, RawEventAttrKind}; use chrono::{DateTime, Utc}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use super::{EventCategory, LearningMethod, MEDIUM, TriageScore, common::Match}; @@ -35,14 +36,16 @@ macro_rules! find_conn_attr_by_kind { } pub(crate) use find_conn_attr_by_kind; -pub type PortScanFields = PortScanFieldsV0_42; +pub type PortScanFields = PortScanFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct PortScanFieldsV0_42 { +pub struct PortScanFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_ports: Vec, + pub resp_country_code: [u8; 2], /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, /// Timestamp in nanoseconds since the Unix epoch (UTC). @@ -58,15 +61,17 @@ impl PortScanFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addr={:?} resp_addr={:?} resp_ports={:?} start_time={:?} end_time={:?} proto={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_country_code={:?} resp_addr={:?} resp_ports={:?} resp_country_code={:?} start_time={:?} end_time={:?} proto={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string ), self.sensor, self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), vector_to_string(&self.resp_ports), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), start_time_dt.to_rfc3339(), end_time_dt.to_rfc3339(), self.proto.to_string(), @@ -81,8 +86,10 @@ pub struct PortScan { pub sensor: String, pub time: DateTime, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_ports: Vec, + pub resp_country_code: [u8; 2], pub start_time: DateTime, pub end_time: DateTime, pub proto: u8, @@ -95,10 +102,12 @@ impl fmt::Display for PortScan { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "orig_addr={:?} resp_addr={:?} resp_ports={:?} start_time={:?} end_time={:?} proto={:?} triage_scores={:?}", + "orig_addr={:?} orig_country_code={:?} resp_addr={:?} resp_ports={:?} resp_country_code={:?} start_time={:?} end_time={:?} proto={:?} triage_scores={:?}", self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), vector_to_string(&self.resp_ports), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.start_time.to_rfc3339(), self.end_time.to_rfc3339(), self.proto.to_string(), @@ -113,8 +122,10 @@ impl PortScan { sensor: fields.sensor.clone(), time, orig_addr: fields.orig_addr, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_ports: fields.resp_ports.clone(), + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), end_time: DateTime::from_timestamp_nanos(fields.end_time), @@ -186,14 +197,16 @@ impl Match for PortScan { } } -pub type MultiHostPortScanFields = MultiHostPortScanFieldsV0_42; +pub type MultiHostPortScanFields = MultiHostPortScanFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct MultiHostPortScanFieldsV0_42 { +pub struct MultiHostPortScanFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, - pub resp_port: u16, + pub orig_country_code: [u8; 2], pub resp_addrs: Vec, + pub resp_port: u16, + pub resp_country_codes: Vec<[u8; 2]>, pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -209,15 +222,20 @@ impl MultiHostPortScanFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addr={:?} resp_addrs={:?} resp_port={:?} proto={:?} start_time={:?} end_time={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_country_code={:?} resp_addrs={:?} resp_port={:?} resp_country_codes={:?} proto={:?} start_time={:?} end_time={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string ), self.sensor, self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), vector_to_string(&self.resp_addrs), self.resp_port.to_string(), + self.resp_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.proto.to_string(), start_time_dt.to_rfc3339(), end_time_dt.to_rfc3339(), @@ -232,8 +250,10 @@ pub struct MultiHostPortScan { pub sensor: String, pub time: DateTime, pub orig_addr: IpAddr, - pub resp_port: u16, + pub orig_country_code: [u8; 2], pub resp_addrs: Vec, + pub resp_port: u16, + pub resp_country_codes: Vec<[u8; 2]>, pub proto: u8, pub start_time: DateTime, pub end_time: DateTime, @@ -246,10 +266,15 @@ impl fmt::Display for MultiHostPortScan { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "orig_addr={:?} resp_addrs={:?} resp_port={:?} proto={:?} start_time={:?} end_time={:?} triage_scores={:?}", + "orig_addr={:?} orig_country_code={:?} resp_addrs={:?} resp_port={:?} resp_country_codes={:?} proto={:?} start_time={:?} end_time={:?} triage_scores={:?}", self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), vector_to_string(&self.resp_addrs), self.resp_port.to_string(), + self.resp_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.proto.to_string(), self.start_time.to_rfc3339(), self.end_time.to_rfc3339(), @@ -264,8 +289,10 @@ impl MultiHostPortScan { sensor: fields.sensor.clone(), time, orig_addr: fields.orig_addr, - resp_port: fields.resp_port, + orig_country_code: fields.orig_country_code, resp_addrs: fields.resp_addrs.clone(), + resp_port: fields.resp_port, + resp_country_codes: fields.resp_country_codes.clone(), proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), end_time: DateTime::from_timestamp_nanos(fields.end_time), @@ -338,13 +365,15 @@ impl Match for MultiHostPortScan { } } -pub type ExternalDdosFields = ExternalDdosFieldsV0_42; +pub type ExternalDdosFields = ExternalDdosFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct ExternalDdosFieldsV0_42 { +pub struct ExternalDdosFieldsV0_44 { pub sensor: String, pub orig_addrs: Vec, + pub orig_country_codes: Vec<[u8; 2]>, pub resp_addr: IpAddr, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -360,14 +389,19 @@ impl ExternalDdosFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addrs={:?} resp_addr={:?} proto={:?} start_time={:?} end_time={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addrs={:?} orig_country_codes={:?} resp_addr={:?} resp_country_code={:?} proto={:?} start_time={:?} end_time={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string ), self.sensor, vector_to_string(&self.orig_addrs), + self.orig_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.resp_addr.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), end_time_dt.to_rfc3339(), @@ -382,7 +416,9 @@ pub struct ExternalDdos { pub sensor: String, pub time: DateTime, pub orig_addrs: Vec, + pub orig_country_codes: Vec<[u8; 2]>, pub resp_addr: IpAddr, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub end_time: DateTime, @@ -395,9 +431,14 @@ impl fmt::Display for ExternalDdos { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "orig_addrs={:?} resp_addr={:?} proto={:?} start_time={:?} end_time={:?} triage_scores={:?}", + "orig_addrs={:?} orig_country_codes={:?} resp_addr={:?} resp_country_code={:?} proto={:?} start_time={:?} end_time={:?} triage_scores={:?}", vector_to_string(&self.orig_addrs), + self.orig_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.resp_addr.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.end_time.to_rfc3339(), @@ -412,7 +453,9 @@ impl ExternalDdos { sensor: fields.sensor.clone(), time, orig_addrs: fields.orig_addrs.clone(), + orig_country_codes: fields.orig_country_codes.clone(), resp_addr: fields.resp_addr, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), end_time: DateTime::from_timestamp_nanos(fields.end_time), @@ -484,15 +527,17 @@ impl Match for ExternalDdos { } } -pub type BlocklistConnFields = BlocklistConnFieldsV0_42; +pub type BlocklistConnFields = BlocklistConnFieldsV0_44; #[derive(Deserialize, Serialize)] -pub struct BlocklistConnFieldsV0_42 { +pub struct BlocklistConnFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub conn_state: String, /// Timestamp in nanoseconds since the Unix epoch (UTC). @@ -515,7 +560,7 @@ impl BlocklistConnFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} conn_state={:?} start_time={:?} duration={:?} service={:?} orig_bytes={:?} resp_bytes={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} conn_state={:?} start_time={:?} duration={:?} service={:?} orig_bytes={:?} resp_bytes={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -523,8 +568,10 @@ impl BlocklistConnFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.conn_state, start_time_dt.to_rfc3339(), @@ -547,8 +594,10 @@ pub struct BlocklistConn { pub time: DateTime, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub conn_state: String, pub start_time: DateTime, @@ -569,12 +618,14 @@ impl fmt::Display for BlocklistConn { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} conn_state={:?} start_time={:?} duration={:?} service={:?} orig_bytes={:?} resp_bytes={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} conn_state={:?} start_time={:?} duration={:?} service={:?} orig_bytes={:?} resp_bytes={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.conn_state, self.start_time.to_rfc3339(), @@ -598,8 +649,10 @@ impl BlocklistConn { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, conn_state: fields.conn_state, start_time: DateTime::from_timestamp_nanos(fields.start_time), diff --git a/src/event/dcerpc.rs b/src/event/dcerpc.rs index bebc81ff..f6db45b4 100644 --- a/src/event/dcerpc.rs +++ b/src/event/dcerpc.rs @@ -7,15 +7,17 @@ use serde::{Deserialize, Serialize}; use super::{EventCategory, LearningMethod, MEDIUM, TriageScore, common::Match}; use crate::event::common::{AttrValue, triage_scores_to_string}; -pub type BlocklistDceRpcFields = BlocklistDceRpcFieldsV0_42; +pub type BlocklistDceRpcFields = BlocklistDceRpcFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistDceRpcFieldsV0_42 { +pub struct BlocklistDceRpcFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -37,7 +39,7 @@ impl BlocklistDceRpcFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} rtt={:?} named_pipe={:?} endpoint={:?} operation={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} rtt={:?} named_pipe={:?} endpoint={:?} operation={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -45,8 +47,10 @@ impl BlocklistDceRpcFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -68,8 +72,10 @@ pub struct BlocklistDceRpc { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -90,12 +96,14 @@ impl fmt::Display for BlocklistDceRpc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} rtt={:?} named_pipe={:?} endpoint={:?} operation={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} rtt={:?} named_pipe={:?} endpoint={:?} operation={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -119,8 +127,10 @@ impl BlocklistDceRpc { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/dhcp.rs b/src/event/dhcp.rs index 480a53bb..edbd7431 100644 --- a/src/event/dhcp.rs +++ b/src/event/dhcp.rs @@ -58,15 +58,17 @@ macro_rules! find_dhcp_attr_by_kind { }}; } -pub type BlocklistDhcpFields = BlocklistDhcpFieldsV0_42; +pub type BlocklistDhcpFields = BlocklistDhcpFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistDhcpFieldsV0_42 { +pub struct BlocklistDhcpFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -107,7 +109,7 @@ impl BlocklistDhcpFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} msg_type={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} subnet_mask={:?} router={:?} domain_name_server={:?} req_ip_addr={:?} lease_time={:?} server_id={:?} param_req_list={:?} message={:?} renewal_time={:?} rebinding_time={:?} class_id={:?} client_id_type={:?} client_id={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} msg_type={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} subnet_mask={:?} router={:?} domain_name_server={:?} req_ip_addr={:?} lease_time={:?} server_id={:?} param_req_list={:?} message={:?} renewal_time={:?} rebinding_time={:?} class_id={:?} client_id_type={:?} client_id={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -115,8 +117,10 @@ impl BlocklistDhcpFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -155,8 +159,10 @@ pub struct BlocklistDhcp { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -190,12 +196,14 @@ impl fmt::Display for BlocklistDhcp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} msg_type={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} subnet_mask={:?} router={:?} domain_name_server={:?} req_ip_addr={:?} lease_time={:?} server_id={:?} param_req_list={:?} message={:?} renewal_time={:?} rebinding_time={:?} class_id={:?} client_id_type={:?} client_id={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} msg_type={:?} ciaddr={:?} yiaddr={:?} siaddr={:?} giaddr={:?} subnet_mask={:?} router={:?} domain_name_server={:?} req_ip_addr={:?} lease_time={:?} server_id={:?} param_req_list={:?} message={:?} renewal_time={:?} rebinding_time={:?} class_id={:?} client_id_type={:?} client_id={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -235,8 +243,10 @@ impl BlocklistDhcp { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/dns.rs b/src/event/dns.rs index d41a6ce6..14c47860 100644 --- a/src/event/dns.rs +++ b/src/event/dns.rs @@ -47,15 +47,17 @@ macro_rules! find_dns_attr_by_kind { }}; } -pub type DnsEventFields = DnsEventFieldsV0_42; +pub type DnsEventFields = DnsEventFieldsV0_44; #[derive(Deserialize, Serialize)] -pub struct DnsEventFieldsV0_42 { +pub struct DnsEventFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -85,7 +87,7 @@ impl DnsEventFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -93,8 +95,10 @@ impl DnsEventFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -125,8 +129,10 @@ pub struct DnsCovertChannel { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -155,12 +161,14 @@ impl fmt::Display for DnsCovertChannel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -191,17 +199,19 @@ impl DnsCovertChannel { Self { time, sensor: fields.sensor, + orig_addr: fields.orig_addr, + orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, + resp_addr: fields.resp_addr, + resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, + proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, orig_l2_bytes: fields.orig_l2_bytes, resp_l2_bytes: fields.resp_l2_bytes, - orig_addr: fields.orig_addr, - orig_port: fields.orig_port, - resp_addr: fields.resp_addr, - resp_port: fields.resp_port, - proto: fields.proto, query: fields.query, answer: fields.answer, trans_id: fields.trans_id, @@ -297,8 +307,10 @@ pub struct LockyRansomware { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -327,12 +339,14 @@ impl fmt::Display for LockyRansomware { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -363,17 +377,19 @@ impl LockyRansomware { Self { time, sensor: fields.sensor, + orig_addr: fields.orig_addr, + orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, + resp_addr: fields.resp_addr, + resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, + proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, orig_l2_bytes: fields.orig_l2_bytes, resp_l2_bytes: fields.resp_l2_bytes, - orig_addr: fields.orig_addr, - orig_port: fields.orig_port, - resp_addr: fields.resp_addr, - resp_port: fields.resp_port, - proto: fields.proto, query: fields.query, answer: fields.answer, trans_id: fields.trans_id, @@ -463,15 +479,17 @@ impl Match for LockyRansomware { } } -pub type CryptocurrencyMiningPoolFields = CryptocurrencyMiningPoolFieldsV0_42; +pub type CryptocurrencyMiningPoolFields = CryptocurrencyMiningPoolFieldsV0_44; #[derive(Deserialize, Serialize)] -pub struct CryptocurrencyMiningPoolFieldsV0_42 { +pub struct CryptocurrencyMiningPoolFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -502,7 +520,7 @@ impl CryptocurrencyMiningPoolFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} coins={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} coins={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -510,8 +528,10 @@ impl CryptocurrencyMiningPoolFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -543,8 +563,10 @@ pub struct CryptocurrencyMiningPool { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -574,12 +596,14 @@ impl fmt::Display for CryptocurrencyMiningPool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} coins={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} coins={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -610,17 +634,19 @@ impl CryptocurrencyMiningPool { Self { time, sensor: fields.sensor, + orig_addr: fields.orig_addr, + orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, + resp_addr: fields.resp_addr, + resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, + proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, orig_l2_bytes: fields.orig_l2_bytes, resp_l2_bytes: fields.resp_l2_bytes, - orig_addr: fields.orig_addr, - orig_port: fields.orig_port, - resp_addr: fields.resp_addr, - resp_port: fields.resp_port, - proto: fields.proto, query: fields.query, answer: fields.answer, trans_id: fields.trans_id, @@ -711,15 +737,17 @@ impl Match for CryptocurrencyMiningPool { } } -pub type BlocklistDnsFields = BlocklistDnsFieldsV0_42; +pub type BlocklistDnsFields = BlocklistDnsFieldsV0_44; #[derive(Deserialize, Serialize)] -pub struct BlocklistDnsFieldsV0_42 { +pub struct BlocklistDnsFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -749,7 +777,7 @@ impl BlocklistDnsFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -757,8 +785,10 @@ impl BlocklistDnsFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -789,8 +819,10 @@ pub struct BlocklistDns { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -819,12 +851,14 @@ impl fmt::Display for BlocklistDns { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} query={:?} answer={:?} trans_id={:?} rtt={:?} qclass={:?} qtype={:?} rcode={:?} aa_flag={:?} tc_flag={:?} rd_flag={:?} ra_flag={:?} ttl={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -854,12 +888,14 @@ impl BlocklistDns { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, diff --git a/src/event/ftp.rs b/src/event/ftp.rs index d4cfec2b..61a79faf 100644 --- a/src/event/ftp.rs +++ b/src/event/ftp.rs @@ -6,10 +6,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use super::{EventCategory, LearningMethod, MEDIUM, TriageScore, common::Match}; -use crate::{ - event::common::{AttrValue, triage_scores_to_string}, - types::EventCategoryV0_41, -}; +use crate::event::common::{AttrValue, triage_scores_to_string}; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct FtpCommand { @@ -159,7 +156,7 @@ macro_rules! find_ftp_attr_by_kind { }}; } -pub type FtpBruteForceFields = FtpBruteForceFieldsV0_42; +pub type FtpBruteForceFields = FtpBruteForceFieldsV0_44; impl FtpBruteForceFields { #[must_use] @@ -167,15 +164,17 @@ impl FtpBruteForceFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addr={:?} resp_addr={:?} resp_port={:?} proto={:?} user_list={:?} start_time={:?} end_time={:?} is_internal={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} user_list={:?} start_time={:?} end_time={:?} is_internal={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string ), self.sensor, self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.user_list.join(","), start_time_dt.to_rfc3339(), @@ -187,11 +186,13 @@ impl FtpBruteForceFields { } #[derive(Serialize, Deserialize)] -pub struct FtpBruteForceFieldsV0_42 { +pub struct FtpBruteForceFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub user_list: Vec, /// Timestamp in nanoseconds since the Unix epoch (UTC). @@ -203,45 +204,15 @@ pub struct FtpBruteForceFieldsV0_42 { pub category: Option, } -impl From for FtpBruteForceFieldsV0_42 { - fn from(value: FtpBruteForceFieldsV0_41) -> Self { - Self { - sensor: String::new(), - orig_addr: value.src_addr, - resp_addr: value.dst_addr, - resp_port: value.dst_port, - proto: value.proto, - user_list: value.user_list, - start_time: value.start_time.timestamp_nanos_opt().unwrap_or_default(), - end_time: value.end_time.timestamp_nanos_opt().unwrap_or_default(), - is_internal: value.is_internal, - confidence: value.confidence, - category: value.category.into(), - } - } -} - -#[derive(Serialize, Deserialize)] -pub(crate) struct FtpBruteForceFieldsV0_41 { - pub sensor: String, - pub src_addr: IpAddr, - pub dst_addr: IpAddr, - pub dst_port: u16, - pub proto: u8, - pub user_list: Vec, - pub start_time: DateTime, - pub end_time: DateTime, - pub is_internal: bool, - pub confidence: f32, - pub category: EventCategoryV0_41, -} #[derive(Serialize, Deserialize)] pub struct FtpBruteForce { pub sensor: String, pub time: DateTime, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub user_list: Vec, pub start_time: DateTime, @@ -256,10 +227,12 @@ impl fmt::Display for FtpBruteForce { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "orig_addr={:?} resp_addr={:?} resp_port={:?} proto={:?} user_list={:?} start_time={:?} end_time={:?} is_internal={:?} triage_scores={:?}", + "orig_addr={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} user_list={:?} start_time={:?} end_time={:?} is_internal={:?} triage_scores={:?}", self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.user_list.join(","), self.start_time.to_rfc3339(), @@ -276,8 +249,10 @@ impl FtpBruteForce { sensor: fields.sensor.clone(), time, orig_addr: fields.orig_addr, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, user_list: fields.user_list.clone(), start_time: DateTime::from_timestamp_nanos(fields.start_time), @@ -353,7 +328,7 @@ impl Match for FtpBruteForce { } } -pub type FtpEventFields = FtpEventFieldsV0_42; +pub type FtpEventFields = FtpEventFieldsV0_44; impl FtpEventFields { #[must_use] @@ -367,7 +342,7 @@ impl FtpEventFields { .join(";"); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} user={:?} password={:?} commands={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} user={:?} password={:?} commands={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -375,8 +350,10 @@ impl FtpEventFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -393,12 +370,14 @@ impl FtpEventFields { } #[derive(Debug, Deserialize, Serialize)] -pub struct FtpEventFieldsV0_42 { +pub struct FtpEventFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -420,8 +399,10 @@ pub struct FtpPlainText { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -448,12 +429,14 @@ impl fmt::Display for FtpPlainText { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} user={:?} password={:?} commands={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} user={:?} password={:?} commands={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -474,12 +457,14 @@ impl FtpPlainText { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, @@ -551,8 +536,10 @@ pub struct BlocklistFtp { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -579,12 +566,14 @@ impl fmt::Display for BlocklistFtp { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} user={:?} password={:?} commands={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} user={:?} password={:?} commands={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -605,12 +594,14 @@ impl BlocklistFtp { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, diff --git a/src/event/http.rs b/src/event/http.rs index f035cf54..87e10fb2 100644 --- a/src/event/http.rs +++ b/src/event/http.rs @@ -59,15 +59,17 @@ macro_rules! find_http_attr_by_kind { } pub(super) use find_http_attr_by_kind; -pub type HttpEventFields = HttpEventFieldsV0_42; +pub type HttpEventFields = HttpEventFieldsV0_44; #[derive(Deserialize, Serialize)] -pub struct HttpEventFieldsV0_42 { +pub struct HttpEventFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -105,7 +107,7 @@ impl HttpEventFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -113,8 +115,10 @@ impl HttpEventFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -147,15 +151,17 @@ impl HttpEventFields { } } -pub type RepeatedHttpSessionsFields = RepeatedHttpSessionsFieldsV0_42; +pub type RepeatedHttpSessionsFields = RepeatedHttpSessionsFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct RepeatedHttpSessionsFieldsV0_42 { +pub struct RepeatedHttpSessionsFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -171,7 +177,7 @@ impl RepeatedHttpSessionsFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} end_time={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} end_time={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -179,8 +185,10 @@ impl RepeatedHttpSessionsFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), end_time_dt.to_rfc3339(), @@ -195,8 +203,10 @@ pub struct RepeatedHttpSessions { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub end_time: DateTime, @@ -209,12 +219,14 @@ impl fmt::Display for RepeatedHttpSessions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} end_time={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} end_time={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.end_time.to_rfc3339(), @@ -230,8 +242,10 @@ impl RepeatedHttpSessions { sensor: fields.sensor.clone(), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), end_time: DateTime::from_timestamp_nanos(fields.end_time), @@ -303,18 +317,20 @@ impl Match for RepeatedHttpSessions { } } -pub type HttpThreatFields = HttpThreatFieldsV0_42; +pub type HttpThreatFields = HttpThreatFieldsV0_44; #[derive(Debug, Deserialize, Serialize)] #[allow(clippy::module_name_repetitions)] -pub struct HttpThreatFieldsV0_42 { +pub struct HttpThreatFieldsV0_44 { #[serde(with = "ts_nanoseconds")] pub time: DateTime, pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -357,7 +373,7 @@ impl HttpThreatFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -365,8 +381,10 @@ impl HttpThreatFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -427,8 +445,10 @@ pub struct HttpThreat { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -470,12 +490,14 @@ impl fmt::Display for HttpThreat { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -519,12 +541,14 @@ impl HttpThreat { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, @@ -652,15 +676,17 @@ impl Match for HttpThreat { } } -pub type DgaFields = DgaFieldsV0_42; +pub type DgaFields = DgaFieldsV0_44; #[derive(Debug, Deserialize, Serialize)] -pub struct DgaFieldsV0_42 { +pub struct DgaFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -698,7 +724,7 @@ impl DgaFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -706,8 +732,10 @@ impl DgaFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -747,8 +775,10 @@ pub struct DomainGenerationAlgorithm { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -785,12 +815,14 @@ impl fmt::Display for DomainGenerationAlgorithm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -829,19 +861,21 @@ impl DomainGenerationAlgorithm { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, orig_l2_bytes: fields.orig_l2_bytes, resp_l2_bytes: fields.resp_l2_bytes, - host: fields.host, method: fields.method, + host: fields.host, uri: fields.uri, referer: fields.referer, version: fields.version, @@ -938,8 +972,10 @@ pub struct NonBrowser { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -976,12 +1012,14 @@ impl fmt::Display for NonBrowser { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -1019,17 +1057,19 @@ impl NonBrowser { NonBrowser { time, sensor: fields.sensor.clone(), + orig_addr: fields.orig_addr, + orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, + resp_addr: fields.resp_addr, + resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, + proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, orig_l2_bytes: fields.orig_l2_bytes, resp_l2_bytes: fields.resp_l2_bytes, - orig_addr: fields.orig_addr, - orig_port: fields.orig_port, - resp_addr: fields.resp_addr, - resp_port: fields.resp_port, - proto: fields.proto, method: fields.method.clone(), host: fields.host.clone(), uri: fields.uri.clone(), @@ -1132,8 +1172,10 @@ pub struct BlocklistHttp { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -1170,12 +1212,14 @@ impl fmt::Display for BlocklistHttp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} method={:?} host={:?} uri={:?} referer={:?} version={:?} user_agent={:?} request_len={:?} response_len={:?} status_code={:?} status_msg={:?} username={:?} password={:?} cookie={:?} content_encoding={:?} content_type={:?} cache_control={:?} filenames={:?} mime_types={:?} body={:?} state={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -1213,12 +1257,14 @@ impl BlocklistHttp { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, diff --git a/src/event/kerberos.rs b/src/event/kerberos.rs index bc74c8ba..257ef9e5 100644 --- a/src/event/kerberos.rs +++ b/src/event/kerberos.rs @@ -42,15 +42,17 @@ macro_rules! find_kerberos_attr_by_kind { }}; } -pub type BlocklistKerberosFields = BlocklistKerberosFieldsV0_42; +pub type BlocklistKerberosFields = BlocklistKerberosFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistKerberosFieldsV0_42 { +pub struct BlocklistKerberosFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -77,7 +79,7 @@ impl BlocklistKerberosFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client_time={:?} server_time={:?} error_code={:?} client_realm={:?} cname_type={:?} client_name={:?} realm={:?} sname_type={:?} service_name={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client_time={:?} server_time={:?} error_code={:?} client_realm={:?} cname_type={:?} client_name={:?} realm={:?} sname_type={:?} service_name={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -85,8 +87,10 @@ impl BlocklistKerberosFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -114,8 +118,10 @@ pub struct BlocklistKerberos { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -141,12 +147,14 @@ impl fmt::Display for BlocklistKerberos { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client_time={:?} server_time={:?} error_code={:?} client_realm={:?} cname_type={:?} client_name={:?} realm={:?} sname_type={:?} service_name={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client_time={:?} server_time={:?} error_code={:?} client_realm={:?} cname_type={:?} client_name={:?} realm={:?} sname_type={:?} service_name={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -175,8 +183,10 @@ impl BlocklistKerberos { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/ldap.rs b/src/event/ldap.rs index 015056e2..da4223d4 100644 --- a/src/event/ldap.rs +++ b/src/event/ldap.rs @@ -47,14 +47,16 @@ macro_rules! find_ldap_attr_by_kind { }}; } -pub type LdapBruteForceFields = LdapBruteForceFieldsV0_42; +pub type LdapBruteForceFields = LdapBruteForceFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct LdapBruteForceFieldsV0_42 { +pub struct LdapBruteForceFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub user_pw_list: Vec<(String, String)>, /// Timestamp in nanoseconds since the Unix epoch (UTC). @@ -71,15 +73,17 @@ impl LdapBruteForceFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addr={:?} resp_addr={:?} resp_port={:?} proto={:?} user_pw_list={:?} start_time={:?} end_time={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} user_pw_list={:?} start_time={:?} end_time={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string ), self.sensor, self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), get_user_pw_list(&self.user_pw_list), start_time_dt.to_rfc3339(), @@ -106,8 +110,10 @@ pub struct LdapBruteForce { pub sensor: String, pub time: DateTime, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub user_pw_list: Vec<(String, String)>, pub start_time: DateTime, @@ -121,10 +127,12 @@ impl fmt::Display for LdapBruteForce { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "orig_addr={:?} resp_addr={:?} resp_port={:?} proto={:?} user_pw_list={:?} start_time={:?} end_time={:?} triage_scores={:?}", + "orig_addr={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} user_pw_list={:?} start_time={:?} end_time={:?} triage_scores={:?}", self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), get_user_pw_list(&self.user_pw_list), self.start_time.to_rfc3339(), @@ -140,8 +148,10 @@ impl LdapBruteForce { sensor: fields.sensor.clone(), time, orig_addr: fields.orig_addr, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, user_pw_list: fields.user_pw_list.clone(), start_time: DateTime::from_timestamp_nanos(fields.start_time), @@ -213,15 +223,17 @@ impl Match for LdapBruteForce { } } -pub type LdapEventFields = LdapEventFieldsV0_42; +pub type LdapEventFields = LdapEventFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct LdapEventFieldsV0_42 { +pub struct LdapEventFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -246,7 +258,7 @@ impl LdapEventFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} message_id={:?} version={:?} opcode={:?} result={:?} diagnostic_message={:?} object={:?} argument={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} message_id={:?} version={:?} opcode={:?} result={:?} diagnostic_message={:?} object={:?} argument={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -254,8 +266,10 @@ impl LdapEventFields { self.sensor.clone(), self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -281,8 +295,10 @@ pub struct LdapPlainText { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -306,12 +322,14 @@ impl fmt::Display for LdapPlainText { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} message_id={:?} version={:?} opcode={:?} result={:?} diagnostic_message={:?} object={:?} argument={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} message_id={:?} version={:?} opcode={:?} result={:?} diagnostic_message={:?} object={:?} argument={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -336,12 +354,14 @@ impl LdapPlainText { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, @@ -417,8 +437,10 @@ pub struct BlocklistLdap { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -472,12 +494,14 @@ impl BlocklistLdap { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, diff --git a/src/event/malformed_dns.rs b/src/event/malformed_dns.rs index b0de4ffe..5a3b61ae 100644 --- a/src/event/malformed_dns.rs +++ b/src/event/malformed_dns.rs @@ -31,14 +31,19 @@ macro_rules! find_malformed_dns_attr_by_kind { }}; } +pub type BlocklistMalformedDnsFields = BlocklistMalformedDnsFieldsV0_44; + #[derive(Serialize, Deserialize)] -pub struct BlocklistMalformedDnsFields { +pub struct BlocklistMalformedDnsFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, + /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, pub duration: i64, pub orig_pkts: u64, @@ -66,7 +71,7 @@ impl BlocklistMalformedDnsFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} trans_id={:?} flags={:?} question_count={:?} answer_count={:?} authority_count={:?} additional_count={:?} query_count={:?} resp_count={:?} query_bytes={:?} resp_bytes={:?} query_body={:?} resp_body={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} trans_id={:?} flags={:?} question_count={:?} answer_count={:?} authority_count={:?} additional_count={:?} query_count={:?} resp_count={:?} query_bytes={:?} resp_bytes={:?} query_body={:?} resp_body={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -74,8 +79,10 @@ impl BlocklistMalformedDnsFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -113,8 +120,10 @@ pub struct BlocklistMalformedDns { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -145,12 +154,14 @@ impl fmt::Display for BlocklistMalformedDns { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} trans_id={:?} flags={:?} question_count={:?} answer_count={:?} authority_count={:?} additional_count={:?} query_count={:?} resp_count={:?} query_bytes={:?} resp_bytes={:?} query_body={:?} resp_body={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} trans_id={:?} flags={:?} question_count={:?} answer_count={:?} authority_count={:?} additional_count={:?} query_count={:?} resp_count={:?} query_bytes={:?} resp_bytes={:?} query_body={:?} resp_body={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_str, self.duration.to_string(), @@ -182,8 +193,10 @@ impl BlocklistMalformedDns { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/mqtt.rs b/src/event/mqtt.rs index 2b21f00a..66991040 100644 --- a/src/event/mqtt.rs +++ b/src/event/mqtt.rs @@ -43,15 +43,17 @@ macro_rules! find_mqtt_attr_by_kind { }}; } -pub type BlocklistMqttFields = BlocklistMqttFieldsV0_42; +pub type BlocklistMqttFields = BlocklistMqttFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistMqttFieldsV0_42 { +pub struct BlocklistMqttFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -76,7 +78,7 @@ impl BlocklistMqttFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} version={:?} client_id={:?} connack_reason={:?} subscribe={:?} suback_reason={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} version={:?} client_id={:?} connack_reason={:?} subscribe={:?} suback_reason={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -84,8 +86,10 @@ impl BlocklistMqttFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -110,8 +114,10 @@ pub struct BlocklistMqtt { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -134,12 +140,14 @@ impl fmt::Display for BlocklistMqtt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} version={:?} client_id={:?} connack_reason={:?} subscribe={:?} suback_reason={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} version={:?} client_id={:?} connack_reason={:?} subscribe={:?} suback_reason={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -165,8 +173,10 @@ impl BlocklistMqtt { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/network.rs b/src/event/network.rs index 11262912..888c925c 100644 --- a/src/event/network.rs +++ b/src/event/network.rs @@ -40,8 +40,12 @@ pub struct NetworkThreat { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + #[serde(default)] + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + #[serde(default)] + pub resp_country_code: [u8; 2], pub proto: u8, pub service: String, #[serde(with = "ts_nanoseconds")] @@ -66,7 +70,7 @@ impl NetworkThreat { #[must_use] pub fn syslog_rfc5424(&self) -> String { format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} service={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} content={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} service={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} content={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -74,8 +78,10 @@ impl NetworkThreat { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.service, self.start_time.to_rfc3339(), @@ -99,12 +105,14 @@ impl fmt::Display for NetworkThreat { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} service={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} content={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} service={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} content={:?} db_name={:?} rule_id={:?} matched_to={:?} cluster_id={:?} attack_kind={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.service, self.start_time.to_rfc3339(), diff --git a/src/event/nfs.rs b/src/event/nfs.rs index 01b39456..39e9aed1 100644 --- a/src/event/nfs.rs +++ b/src/event/nfs.rs @@ -35,15 +35,17 @@ macro_rules! find_nfs_attr_by_kind { }}; } -pub type BlocklistNfsFields = BlocklistNfsFieldsV0_42; +pub type BlocklistNfsFields = BlocklistNfsFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistNfsFieldsV0_42 { +pub struct BlocklistNfsFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -63,7 +65,7 @@ impl BlocklistNfsFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} read_files={:?} write_files={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} read_files={:?} write_files={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -71,8 +73,10 @@ impl BlocklistNfsFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -93,8 +97,10 @@ pub struct BlocklistNfs { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -112,12 +118,14 @@ impl fmt::Display for BlocklistNfs { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} read_files={:?} write_files={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} read_files={:?} write_files={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -139,8 +147,10 @@ impl BlocklistNfs { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/ntlm.rs b/src/event/ntlm.rs index 9eb406c5..b9b57a12 100644 --- a/src/event/ntlm.rs +++ b/src/event/ntlm.rs @@ -34,15 +34,17 @@ macro_rules! find_ntlm_attr_by_kind { }}; } -pub type BlocklistNtlmFields = BlocklistNtlmFieldsV0_42; +pub type BlocklistNtlmFields = BlocklistNtlmFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistNtlmFieldsV0_42 { +pub struct BlocklistNtlmFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -65,7 +67,7 @@ impl BlocklistNtlmFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} username={:?} hostname={:?} domainname={:?} success={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} username={:?} hostname={:?} domainname={:?} success={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -73,8 +75,10 @@ impl BlocklistNtlmFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -98,8 +102,10 @@ pub struct BlocklistNtlm { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -120,12 +126,14 @@ impl fmt::Display for BlocklistNtlm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} username={:?} hostname={:?} domainname={:?} success={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} protocol={:?} username={:?} hostname={:?} domainname={:?} success={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -149,8 +157,10 @@ impl BlocklistNtlm { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/radius.rs b/src/event/radius.rs index 80a9a346..4ac944bb 100644 --- a/src/event/radius.rs +++ b/src/event/radius.rs @@ -43,14 +43,19 @@ macro_rules! find_radius_attr_by_kind { }}; } +pub type BlocklistRadiusFields = BlocklistRadiusFieldsV0_44; + #[derive(Serialize, Deserialize)] -pub struct BlocklistRadiusFields { +pub struct BlocklistRadiusFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, + /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, pub duration: i64, pub orig_pkts: u64, @@ -80,7 +85,7 @@ impl BlocklistRadiusFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} id={:?} code={:?} resp_code={:?} auth={:?} resp_auth={:?} user_name={:?} user_passwd={:?} chap_passwd={:?} nas_ip={:?} nas_port={:?} state={:?} nas_id={:?} nas_port_type={:?} message={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} id={:?} code={:?} resp_code={:?} auth={:?} resp_auth={:?} user_name={:?} user_passwd={:?} chap_passwd={:?} nas_ip={:?} nas_port={:?} state={:?} nas_id={:?} nas_port_type={:?} message={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -88,8 +93,10 @@ impl BlocklistRadiusFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -121,8 +128,10 @@ pub struct BlocklistRadius { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -155,12 +164,14 @@ impl fmt::Display for BlocklistRadius { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} id={:?} code={:?} resp_code={:?} auth={:?} resp_auth={:?} user_name={:?} user_passwd={:?} chap_passwd={:?} nas_ip={:?} nas_port={:?} state={:?} nas_id={:?} nas_port_type={:?} message={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} id={:?} code={:?} resp_code={:?} auth={:?} resp_auth={:?} user_name={:?} user_passwd={:?} chap_passwd={:?} nas_ip={:?} nas_port={:?} state={:?} nas_id={:?} nas_port_type={:?} message={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_str, self.duration.to_string(), @@ -194,8 +205,10 @@ impl BlocklistRadius { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/rdp.rs b/src/event/rdp.rs index c5af6de8..cf2f5434 100644 --- a/src/event/rdp.rs +++ b/src/event/rdp.rs @@ -4,6 +4,7 @@ use std::{fmt, net::IpAddr, num::NonZeroU8}; use attrievent::attribute::{RawEventAttrKind, RdpAttr}; use chrono::{DateTime, Utc}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use super::{ @@ -35,13 +36,15 @@ macro_rules! find_rdp_attr_by_kind { }}; } -pub type RdpBruteForceFields = RdpBruteForceFieldsV0_42; +pub type RdpBruteForceFields = RdpBruteForceFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct RdpBruteForceFieldsV0_42 { +pub struct RdpBruteForceFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addrs: Vec, + pub resp_country_codes: Vec<[u8; 2]>, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, /// Timestamp in nanoseconds since the Unix epoch (UTC). @@ -57,14 +60,19 @@ impl RdpBruteForceFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} orig_addr={:?} resp_addrs={:?} start_time={:?} end_time={:?} proto={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_country_code={:?} resp_addrs={:?} resp_country_codes={:?} start_time={:?} end_time={:?} proto={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string ), self.sensor, self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), vector_to_string(&self.resp_addrs), + self.resp_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), start_time_dt.to_rfc3339(), end_time_dt.to_rfc3339(), self.proto.to_string(), @@ -78,7 +86,9 @@ pub struct RdpBruteForce { pub sensor: String, pub time: DateTime, pub orig_addr: IpAddr, + pub orig_country_code: [u8; 2], pub resp_addrs: Vec, + pub resp_country_codes: Vec<[u8; 2]>, pub start_time: DateTime, pub end_time: DateTime, pub proto: u8, @@ -91,9 +101,14 @@ impl fmt::Display for RdpBruteForce { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "orig_addr={:?} resp_addrs={:?} start_time={:?} end_time={:?} proto={:?} triage_scores={:?}", + "orig_addr={:?} orig_country_code={:?} resp_addrs={:?} resp_country_codes={:?} start_time={:?} end_time={:?} proto={:?} triage_scores={:?}", self.orig_addr.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), vector_to_string(&self.resp_addrs), + self.resp_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.start_time.to_rfc3339(), self.end_time.to_rfc3339(), self.proto.to_string(), @@ -108,7 +123,9 @@ impl RdpBruteForce { sensor: fields.sensor.clone(), time, orig_addr: fields.orig_addr, + orig_country_code: fields.orig_country_code, resp_addrs: fields.resp_addrs.clone(), + resp_country_codes: fields.resp_country_codes.clone(), start_time: DateTime::from_timestamp_nanos(fields.start_time), end_time: DateTime::from_timestamp_nanos(fields.end_time), proto: fields.proto, @@ -180,15 +197,17 @@ impl Match for RdpBruteForce { } } -pub type BlocklistRdpFields = BlocklistRdpFieldsV0_42; +pub type BlocklistRdpFields = BlocklistRdpFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistRdpFieldsV0_42 { +pub struct BlocklistRdpFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -207,7 +226,7 @@ impl BlocklistRdpFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} cookie={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} cookie={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -215,8 +234,10 @@ impl BlocklistRdpFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -235,8 +256,10 @@ pub struct BlocklistRdp { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -253,12 +276,14 @@ impl fmt::Display for BlocklistRdp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} cookie={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} cookie={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -279,8 +304,10 @@ impl BlocklistRdp { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/smb.rs b/src/event/smb.rs index 73e7de8b..4e166683 100644 --- a/src/event/smb.rs +++ b/src/event/smb.rs @@ -40,15 +40,17 @@ macro_rules! find_smb_attr_by_kind { }}; } -pub type BlocklistSmbFields = BlocklistSmbFieldsV0_42; +pub type BlocklistSmbFields = BlocklistSmbFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistSmbFieldsV0_42 { +pub struct BlocklistSmbFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -77,7 +79,7 @@ impl BlocklistSmbFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} command={:?} path={:?} service={:?} file_name={:?} file_size={:?} resource_type={:?} fid={:?} create_time={:?} access_time={:?} write_time={:?} change_time={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} command={:?} path={:?} service={:?} file_name={:?} file_size={:?} resource_type={:?} fid={:?} create_time={:?} access_time={:?} write_time={:?} change_time={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -85,8 +87,10 @@ impl BlocklistSmbFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -116,8 +120,10 @@ pub struct BlocklistSmb { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -144,12 +150,14 @@ impl fmt::Display for BlocklistSmb { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} command={:?} path={:?} service={:?} file_name={:?} file_size={:?} resource_type={:?} fid={:?} create_time={:?} access_time={:?} write_time={:?} change_time={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} command={:?} path={:?} service={:?} file_name={:?} file_size={:?} resource_type={:?} fid={:?} create_time={:?} access_time={:?} write_time={:?} change_time={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -179,8 +187,10 @@ impl BlocklistSmb { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/smtp.rs b/src/event/smtp.rs index 077aa8de..9242484d 100644 --- a/src/event/smtp.rs +++ b/src/event/smtp.rs @@ -36,15 +36,17 @@ macro_rules! find_smtp_attr_by_kind { }}; } -pub type BlocklistSmtpFields = BlocklistSmtpFieldsV0_42; +pub type BlocklistSmtpFields = BlocklistSmtpFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistSmtpFieldsV0_42 { +pub struct BlocklistSmtpFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -69,7 +71,7 @@ impl BlocklistSmtpFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} mailfrom={:?} date={:?} from={:?} to={:?} subject={:?} agent={:?} state={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} mailfrom={:?} date={:?} from={:?} to={:?} subject={:?} agent={:?} state={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -77,8 +79,10 @@ impl BlocklistSmtpFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -104,8 +108,10 @@ pub struct BlocklistSmtp { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -128,12 +134,14 @@ impl fmt::Display for BlocklistSmtp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} mailfrom={:?} date={:?} from={:?} to={:?} subject={:?} agent={:?} state={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} mailfrom={:?} date={:?} from={:?} to={:?} subject={:?} agent={:?} state={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -160,8 +168,10 @@ impl BlocklistSmtp { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/ssh.rs b/src/event/ssh.rs index bb5ee8bc..0b909b02 100644 --- a/src/event/ssh.rs +++ b/src/event/ssh.rs @@ -44,15 +44,17 @@ macro_rules! find_ssh_attr_by_kind { }}; } -pub type BlocklistSshFields = BlocklistSshFieldsV0_42; +pub type BlocklistSshFields = BlocklistSshFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistSshFieldsV0_42 { +pub struct BlocklistSshFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -83,7 +85,7 @@ impl BlocklistSshFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client={:?} server={:?} cipher_alg={:?} mac_alg={:?} compression_alg={:?} kex_alg={:?} host_key_alg={:?} hassh_algorithms={:?} hassh={:?} hassh_server_algorithms={:?} hassh_server={:?} client_shka={:?} server_shka={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client={:?} server={:?} cipher_alg={:?} mac_alg={:?} compression_alg={:?} kex_alg={:?} host_key_alg={:?} hassh_algorithms={:?} hassh={:?} hassh_server_algorithms={:?} hassh_server={:?} client_shka={:?} server_shka={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -91,8 +93,10 @@ impl BlocklistSshFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -124,8 +128,10 @@ pub struct BlocklistSsh { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -154,12 +160,14 @@ impl fmt::Display for BlocklistSsh { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client={:?} server={:?} cipher_alg={:?} mac_alg={:?} compression_alg={:?} kex_alg={:?} host_key_alg={:?} hassh_algorithms={:?} hassh={:?} hassh_server_algorithms={:?} hassh_server={:?} client_shka={:?} server_shka={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} client={:?} server={:?} cipher_alg={:?} mac_alg={:?} compression_alg={:?} kex_alg={:?} host_key_alg={:?} hassh_algorithms={:?} hassh={:?} hassh_server_algorithms={:?} hassh_server={:?} client_shka={:?} server_shka={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -192,8 +200,10 @@ impl BlocklistSsh { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, diff --git a/src/event/tls.rs b/src/event/tls.rs index fb16bc1d..e9b1794e 100644 --- a/src/event/tls.rs +++ b/src/event/tls.rs @@ -68,15 +68,17 @@ macro_rules! find_tls_attr_by_kind { }}; } -pub type BlocklistTlsFields = BlocklistTlsFieldsV0_42; +pub type BlocklistTlsFields = BlocklistTlsFieldsV0_44; #[derive(Serialize, Deserialize)] -pub struct BlocklistTlsFieldsV0_42 { +pub struct BlocklistTlsFieldsV0_44 { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, @@ -115,7 +117,7 @@ impl BlocklistTlsFields { pub fn syslog_rfc5424(&self) -> String { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); format!( - "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} server_name={:?} alpn_protocol={:?} ja3={:?} version={:?} client_cipher_suites={:?} client_extensions={:?} cipher={:?} extensions={:?} ja3s={:?} serial={:?} subject_country={:?} subject_org_name={:?} subject_common_name={:?} validity_not_before={:?} validity_not_after={:?} subject_alt_name={:?} issuer_country={:?} issuer_org_name={:?} issuer_org_unit_name={:?} issuer_common_name={:?} last_alert={:?} confidence={:?}", + "category={:?} sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} server_name={:?} alpn_protocol={:?} ja3={:?} version={:?} client_cipher_suites={:?} client_extensions={:?} cipher={:?} extensions={:?} ja3s={:?} serial={:?} subject_country={:?} subject_org_name={:?} subject_common_name={:?} validity_not_before={:?} validity_not_after={:?} subject_alt_name={:?} issuer_country={:?} issuer_org_name={:?} issuer_org_unit_name={:?} issuer_common_name={:?} last_alert={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -123,8 +125,10 @@ impl BlocklistTlsFields { self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), start_time_dt.to_rfc3339(), self.duration.to_string(), @@ -164,8 +168,10 @@ pub struct BlocklistTls { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -203,12 +209,14 @@ impl fmt::Display for BlocklistTls { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} server_name={:?} alpn_protocol={:?} ja3={:?} version={:?} client_cipher_suites={:?} client_extensions={:?} cipher={:?} extensions={:?} ja3s={:?} serial={:?} subject_country={:?} subject_org_name={:?} subject_common_name={:?} validity_not_before={:?} validity_not_after={:?} subject_alt_name={:?} issuer_country={:?} issuer_org_name={:?} issuer_org_unit_name={:?} issuer_common_name={:?} last_alert={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} server_name={:?} alpn_protocol={:?} ja3={:?} version={:?} client_cipher_suites={:?} client_extensions={:?} cipher={:?} extensions={:?} ja3s={:?} serial={:?} subject_country={:?} subject_org_name={:?} subject_common_name={:?} validity_not_before={:?} validity_not_after={:?} subject_alt_name={:?} issuer_country={:?} issuer_org_name={:?} issuer_org_unit_name={:?} issuer_common_name={:?} last_alert={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -250,8 +258,10 @@ impl BlocklistTls { sensor: fields.sensor, orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, @@ -342,8 +352,10 @@ pub struct SuspiciousTlsTraffic { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -381,12 +393,14 @@ impl fmt::Display for SuspiciousTlsTraffic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} orig_addr={:?} orig_port={:?} resp_addr={:?} resp_port={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} server_name={:?} alpn_protocol={:?} ja3={:?} version={:?} client_cipher_suites={:?} client_extensions={:?} cipher={:?} extensions={:?} ja3s={:?} serial={:?} subject_country={:?} subject_org_name={:?} subject_common_name={:?} validity_not_before={:?} validity_not_after={:?} subject_alt_name={:?} issuer_country={:?} issuer_org_name={:?} issuer_org_unit_name={:?} issuer_common_name={:?} last_alert={:?} confidence={:?} triage_scores={:?}", + "sensor={:?} orig_addr={:?} orig_port={:?} orig_country_code={:?} resp_addr={:?} resp_port={:?} resp_country_code={:?} proto={:?} start_time={:?} duration={:?} orig_pkts={:?} resp_pkts={:?} orig_l2_bytes={:?} resp_l2_bytes={:?} server_name={:?} alpn_protocol={:?} ja3={:?} version={:?} client_cipher_suites={:?} client_extensions={:?} cipher={:?} extensions={:?} ja3s={:?} serial={:?} subject_country={:?} subject_org_name={:?} subject_common_name={:?} validity_not_before={:?} validity_not_after={:?} subject_alt_name={:?} issuer_country={:?} issuer_org_name={:?} issuer_org_unit_name={:?} issuer_common_name={:?} last_alert={:?} confidence={:?} triage_scores={:?}", self.sensor, self.orig_addr.to_string(), self.orig_port.to_string(), + std::str::from_utf8(&self.orig_country_code).unwrap_or("XX"), self.resp_addr.to_string(), self.resp_port.to_string(), + std::str::from_utf8(&self.resp_country_code).unwrap_or("XX"), self.proto.to_string(), self.start_time.to_rfc3339(), self.duration.to_string(), @@ -426,12 +440,14 @@ impl SuspiciousTlsTraffic { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, diff --git a/src/event/tor.rs b/src/event/tor.rs index 6290f825..2a73641d 100644 --- a/src/event/tor.rs +++ b/src/event/tor.rs @@ -19,8 +19,10 @@ pub struct TorConnection { pub sensor: String, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -100,17 +102,19 @@ impl TorConnection { TorConnection { time, sensor: fields.sensor.clone(), + orig_addr: fields.orig_addr, + orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, + resp_addr: fields.resp_addr, + resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, + proto: fields.proto, start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, orig_pkts: fields.orig_pkts, resp_pkts: fields.resp_pkts, orig_l2_bytes: fields.orig_l2_bytes, resp_l2_bytes: fields.resp_l2_bytes, - orig_addr: fields.orig_addr, - orig_port: fields.orig_port, - resp_addr: fields.resp_addr, - resp_port: fields.resp_port, - proto: fields.proto, method: fields.method.clone(), host: fields.host.clone(), uri: fields.uri.clone(), @@ -208,8 +212,10 @@ pub struct TorConnectionConn { pub time: DateTime, pub orig_addr: IpAddr, pub orig_port: u16, + pub orig_country_code: [u8; 2], pub resp_addr: IpAddr, pub resp_port: u16, + pub resp_country_code: [u8; 2], pub proto: u8, pub start_time: DateTime, pub duration: i64, @@ -257,14 +263,16 @@ impl TorConnectionConn { Self { time, sensor: fields.sensor, - start_time: DateTime::from_timestamp_nanos(fields.start_time), orig_addr: fields.orig_addr, orig_port: fields.orig_port, + orig_country_code: fields.orig_country_code, resp_addr: fields.resp_addr, resp_port: fields.resp_port, + resp_country_code: fields.resp_country_code, proto: fields.proto, - conn_state: fields.conn_state, + start_time: DateTime::from_timestamp_nanos(fields.start_time), duration: fields.duration, + conn_state: fields.conn_state, service: fields.service, orig_bytes: fields.orig_bytes, resp_bytes: fields.resp_bytes, @@ -349,8 +357,10 @@ mod tests { .unwrap(); BlocklistConnFields { sensor: "test-sensor".to_string(), + orig_country_code: *b"XX", orig_addr: "192.168.1.100".parse().unwrap(), orig_port: 12345, + resp_country_code: *b"XX", resp_addr: "198.51.100.1".parse().unwrap(), resp_port: 443, proto: 6, diff --git a/src/event/unusual_destination_pattern.rs b/src/event/unusual_destination_pattern.rs index f48f7c8e..be539ad0 100644 --- a/src/event/unusual_destination_pattern.rs +++ b/src/event/unusual_destination_pattern.rs @@ -2,17 +2,23 @@ use std::{fmt, net::IpAddr, num::NonZeroU8}; use attrievent::attribute::{ConnAttr, RawEventAttrKind}; use chrono::{DateTime, Utc}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use super::{EventCategory, LearningMethod, MEDIUM, TriageScore, common::Match}; use crate::event::common::{AttrValue, triage_scores_to_string}; +pub type UnusualDestinationPatternFields = UnusualDestinationPatternFieldsV0_44; + #[derive(Serialize, Deserialize)] -pub struct UnusualDestinationPatternFields { +pub struct UnusualDestinationPatternFieldsV0_44 { pub sensor: String, + /// Timestamp in nanoseconds since the Unix epoch (UTC). pub start_time: i64, + /// Timestamp in nanoseconds since the Unix epoch (UTC). pub end_time: i64, pub destination_ips: Vec, + pub resp_country_codes: Vec<[u8; 2]>, pub count: usize, pub expected_mean: f64, pub std_deviation: f64, @@ -27,7 +33,7 @@ impl UnusualDestinationPatternFields { let start_time_dt = DateTime::from_timestamp_nanos(self.start_time); let end_time_dt = DateTime::from_timestamp_nanos(self.end_time); format!( - "category={:?} sensor={:?} start_time={:?} end_time={:?} destination_ips={:?} count={:?} expected_mean={:?} std_deviation={:?} z_score={:?} confidence={:?}", + "category={:?} sensor={:?} start_time={:?} end_time={:?} destination_ips={:?} resp_country_codes={:?} count={:?} expected_mean={:?} std_deviation={:?} z_score={:?} confidence={:?}", self.category.as_ref().map_or_else( || "Unspecified".to_string(), std::string::ToString::to_string @@ -36,6 +42,10 @@ impl UnusualDestinationPatternFields { start_time_dt.to_rfc3339(), end_time_dt.to_rfc3339(), format_ip_vec(&self.destination_ips), + self.resp_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.count.to_string(), self.expected_mean.to_string(), self.std_deviation.to_string(), @@ -59,6 +69,7 @@ pub struct UnusualDestinationPattern { pub start_time: DateTime, pub end_time: DateTime, pub destination_ips: Vec, + pub resp_country_codes: Vec<[u8; 2]>, pub count: usize, pub expected_mean: f64, pub std_deviation: f64, @@ -72,11 +83,15 @@ impl fmt::Display for UnusualDestinationPattern { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "sensor={:?} start_time={:?} end_time={:?} destination_ips={:?} count={:?} expected_mean={:?} std_deviation={:?} z_score={:?} triage_scores={:?}", + "sensor={:?} start_time={:?} end_time={:?} destination_ips={:?} resp_country_codes={:?} count={:?} expected_mean={:?} std_deviation={:?} z_score={:?} triage_scores={:?}", self.sensor, self.start_time.to_rfc3339(), self.end_time.to_rfc3339(), format_ip_vec(&self.destination_ips), + self.resp_country_codes + .iter() + .map(|code| std::str::from_utf8(code).unwrap_or("XX")) + .join(","), self.count.to_string(), self.expected_mean.to_string(), self.std_deviation.to_string(), @@ -94,6 +109,7 @@ impl UnusualDestinationPattern { start_time: DateTime::from_timestamp_nanos(fields.start_time), end_time: DateTime::from_timestamp_nanos(fields.end_time), destination_ips: fields.destination_ips, + resp_country_codes: fields.resp_country_codes, count: fields.count, expected_mean: fields.expected_mean, std_deviation: fields.std_deviation, diff --git a/src/lib.rs b/src/lib.rs index 3f1628e6..65db043e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ pub use self::top_n::{ ClusterTrend, ElementCount, LineSegment, Regression, StructuredColumnType, TopTrendsByColumn, }; pub use self::types::{EventCategory, HostNetworkGroup, Qualifier, Status}; -pub use self::util::find_ip_country; +pub use self::util::{country_code_to_bytes, find_ip_country}; const DEFAULT_STATES: &str = "states.db"; const EXCLUSIVE: bool = true; diff --git a/src/migrate.rs b/src/migrate.rs index 81c3998e..b8729d45 100644 --- a/src/migrate.rs +++ b/src/migrate.rs @@ -10,8 +10,19 @@ fn main() -> Result<()> { let config = Config::load_config(parse().as_deref())?; println!("Starting migration process..."); + + let locator = config + .ip2location + .as_ref() + .map(|path| { + println!("Loading ip2location database from {}...", path.display()); + ip2location::DB::from_file(path).context("failed to load ip2location database") + }) + .transpose()?; + println!("Migrating data directory..."); - migrate_data_dir(&config.data_dir, &config.backup_dir).context("migration failed")?; + migrate_data_dir(&config.data_dir, &config.backup_dir, locator.as_ref()) + .context("migration failed")?; Ok(()) } @@ -57,6 +68,7 @@ fn bin() -> &'static str { pub struct Config { data_dir: PathBuf, backup_dir: PathBuf, + ip2location: Option, } impl Config { @@ -83,6 +95,7 @@ impl Config { Ok(Self { data_dir: config.data_dir, backup_dir: config.backup_dir, + ip2location: config.ip2location, }) } } @@ -91,4 +104,5 @@ impl Config { struct ConfigParser { data_dir: PathBuf, backup_dir: PathBuf, + ip2location: Option, } diff --git a/src/migration.rs b/src/migration.rs index e1517fc5..1a2ed52c 100644 --- a/src/migration.rs +++ b/src/migration.rs @@ -5,6 +5,7 @@ mod migration_structures; use std::{ fs::{File, create_dir_all}, io::{Read, Write}, + net::IpAddr, path::{Path, PathBuf}, }; @@ -14,7 +15,7 @@ use semver::{Version, VersionReq}; use tracing::info; use crate::{ - AllowNetwork, BlockNetwork, Customer, + AllowNetwork, BlockNetwork, Customer, EventKind, migration::migration_structures::{AllowNetworkV0_42, BlockNetworkV0_42}, tables::NETWORK_TAGS, }; @@ -107,13 +108,28 @@ const COMPATIBLE_VERSION_REQ: &str = ">=0.44.0-alpha.2,<0.44.0-alpha.3"; /// Migration is supported between released versions only. The prelease versions (alpha, beta, /// etc.) should be assumed to be incompatible with each other. /// +/// # Arguments +/// +/// * `data_dir` - Path to the data directory containing the database +/// * `backup_dir` - Path to the backup directory +/// * `locator` - Optional IP geolocation database for resolving country codes during migration. +/// If `None`, country code fields will be set to "ZZ" (unknown) for migrated records. +/// /// # Errors /// /// Returns an error if the data directory doesn't exist and cannot be created, /// or if the data directory exists but is in the format incompatible with the /// current version. -pub fn migrate_data_dir>(data_dir: P, backup_dir: P) -> Result<()> { - type Migration = (VersionReq, Version, fn(&Path) -> anyhow::Result<()>); +pub fn migrate_data_dir>( + data_dir: P, + backup_dir: P, + locator: Option<&ip2location::DB>, +) -> Result<()> { + type Migration = ( + VersionReq, + Version, + fn(&Path, &Path, Option<&ip2location::DB>) -> anyhow::Result<()>, + ); let data_dir = data_dir.as_ref(); let backup_dir = backup_dir.as_ref(); @@ -165,7 +181,7 @@ pub fn migrate_data_dir>(data_dir: P, backup_dir: P) -> Result<() .find(|(req, _to, _m)| req.matches(&version)) { info!("Migrating database to {to}"); - m(data_dir)?; + m(data_dir, backup_dir, locator)?; version = to.clone(); if compatible.matches(&version) { create_version_file(&backup).context("failed to update VERSION")?; @@ -225,7 +241,11 @@ fn map_names_v0_42_without_account_policy() -> Vec<&'static str> { .collect() } -fn migrate_0_42_to_0_43(data_dir: &Path) -> Result<()> { +fn migrate_0_42_to_0_43( + data_dir: &Path, + _backup_dir: &Path, + _locator: Option<&ip2location::DB>, +) -> Result<()> { let db_path = data_dir.join("states.db"); // Step 1: Drop "account policy" column family if it exists (from 0.42) @@ -240,13 +260,23 @@ fn migrate_0_42_to_0_43(data_dir: &Path) -> Result<()> { Ok(()) } -fn migrate_0_43_to_0_44(data_dir: &Path) -> Result<()> { - // Migrate network tags to customer-scoped format +fn migrate_0_43_to_0_44( + data_dir: &Path, + backup_dir: &Path, + locator: Option<&ip2location::DB>, +) -> Result<()> { + // Step 1: Migrate network tags to customer-scoped format + // This requires direct database access for low-level meta column family operations migrate_network_tags_to_customer_scoped(data_dir)?; - // Migrate Network table to enforce global name uniqueness + // Step 2: Migrate Network table to enforce global name uniqueness migrate_network_cf(data_dir)?; + // Step 3: Migrate event fields to add country codes using Store abstraction + let store = super::Store::new(data_dir, backup_dir) + .context("Failed to open Store for event migration")?; + migrate_event_country_codes(&store, locator)?; + Ok(()) } @@ -825,6 +855,520 @@ fn migrate_network_cf_inner( Ok(()) } +/// Migrates event fields from `V0_43` format (without country codes) to `V0_44` format +/// (with country codes). If a locator is provided, country codes are resolved from +/// IP addresses. Otherwise, country codes are set to "ZZ" (unknown). +fn migrate_event_country_codes( + store: &super::Store, + locator: Option<&ip2location::DB>, +) -> Result<()> { + use num_traits::FromPrimitive; + + use crate::migration::migration_structures::{ + BlocklistBootpFieldsV0_43, BlocklistConnFieldsV0_43, BlocklistDceRpcFieldsV0_43, + BlocklistDhcpFieldsV0_43, BlocklistDnsFieldsV0_43, BlocklistKerberosFieldsV0_43, + BlocklistMalformedDnsFieldsV0_43, BlocklistMqttFieldsV0_43, BlocklistNfsFieldsV0_43, + BlocklistNtlmFieldsV0_43, BlocklistRadiusFieldsV0_43, BlocklistRdpFieldsV0_43, + BlocklistSmbFieldsV0_43, BlocklistSmtpFieldsV0_43, BlocklistSshFieldsV0_43, + BlocklistTlsFieldsV0_43, CryptocurrencyMiningPoolFieldsV0_43, DgaFieldsV0_43, + DnsEventFieldsV0_43, ExternalDdosFieldsV0_43, FtpBruteForceFieldsV0_43, + FtpEventFieldsV0_43, HttpEventFieldsV0_43, HttpThreatFieldsV0_43, + LdapBruteForceFieldsV0_43, LdapEventFieldsV0_43, MultiHostPortScanFieldsV0_43, + NetworkThreatV0_43, PortScanFieldsV0_43, RdpBruteForceFieldsV0_43, + RepeatedHttpSessionsFieldsV0_43, UnusualDestinationPatternFieldsV0_43, + }; + + info!("Migrating event fields to add country codes"); + + let events = store.events(); + + // Collect all events that need migration using raw iterator + let entries_to_migrate: Vec<(Vec, Vec)> = events + .raw_iter_forward() + .map(|(key, value)| (key.to_vec(), value.to_vec())) + .collect(); + + if entries_to_migrate.is_empty() { + info!("No events to migrate"); + return Ok(()); + } + + info!("Found {} events to migrate", entries_to_migrate.len()); + + let mut migrated_count = 0usize; + let mut skipped_count = 0usize; + + for (key, value) in &entries_to_migrate { + // Extract the event kind from the key (bits 32-63 of the 128-bit key) + let key_i128 = if key.len() >= 16 { + let Ok(key_bytes) = key[..16].try_into() else { + skipped_count += 1; + continue; + }; + i128::from_be_bytes(key_bytes) + } else { + skipped_count += 1; + continue; + }; + let kind_value = ((key_i128 >> 32) & 0xFFFF_FFFF) as i32; + + let Some(kind) = EventKind::from_i32(kind_value) else { + skipped_count += 1; + continue; + }; + + // Migrate the fields based on event kind + // Events store raw field bytes directly (not EventMessage wrapper) + let new_fields: Vec = match kind { + EventKind::DnsCovertChannel | EventKind::LockyRansomware => { + migrate_event_with_country_code::( + value, locator, + )? + } + EventKind::HttpThreat => migrate_event_with_country_code::< + HttpThreatFieldsV0_43, + HttpThreatFields, + >(value, locator)?, + EventKind::RdpBruteForce => migrate_event_with_country_code::< + RdpBruteForceFieldsV0_43, + RdpBruteForceFields, + >(value, locator)?, + EventKind::RepeatedHttpSessions => migrate_event_with_country_code::< + RepeatedHttpSessionsFieldsV0_43, + RepeatedHttpSessionsFields, + >(value, locator)?, + EventKind::TorConnection | EventKind::NonBrowser | EventKind::BlocklistHttp => { + migrate_event_with_country_code::( + value, locator, + )? + } + EventKind::TorConnectionConn | EventKind::BlocklistConn => { + migrate_event_with_country_code::( + value, locator, + )? + } + EventKind::DomainGenerationAlgorithm => { + migrate_event_with_country_code::(value, locator)? + } + EventKind::FtpBruteForce => migrate_event_with_country_code::< + FtpBruteForceFieldsV0_43, + FtpBruteForceFields, + >(value, locator)?, + EventKind::FtpPlainText | EventKind::BlocklistFtp => migrate_event_with_country_code::< + FtpEventFieldsV0_43, + FtpEventFields, + >(value, locator)?, + EventKind::PortScan => migrate_event_with_country_code::< + PortScanFieldsV0_43, + PortScanFields, + >(value, locator)?, + EventKind::MultiHostPortScan => migrate_event_with_country_code::< + MultiHostPortScanFieldsV0_43, + MultiHostPortScanFields, + >(value, locator)?, + EventKind::ExternalDdos => migrate_event_with_country_code::< + ExternalDdosFieldsV0_43, + ExternalDdosFields, + >(value, locator)?, + EventKind::LdapBruteForce => migrate_event_with_country_code::< + LdapBruteForceFieldsV0_43, + LdapBruteForceFields, + >(value, locator)?, + EventKind::LdapPlainText | EventKind::BlocklistLdap => { + migrate_event_with_country_code::( + value, locator, + )? + } + EventKind::CryptocurrencyMiningPool => migrate_event_with_country_code::< + CryptocurrencyMiningPoolFieldsV0_43, + CryptocurrencyMiningPoolFields, + >(value, locator)?, + EventKind::BlocklistBootp => migrate_event_with_country_code::< + BlocklistBootpFieldsV0_43, + BlocklistBootpFields, + >(value, locator)?, + EventKind::BlocklistDceRpc => migrate_event_with_country_code::< + BlocklistDceRpcFieldsV0_43, + BlocklistDceRpcFields, + >(value, locator)?, + EventKind::BlocklistDhcp => migrate_event_with_country_code::< + BlocklistDhcpFieldsV0_43, + BlocklistDhcpFields, + >(value, locator)?, + EventKind::BlocklistDns => migrate_event_with_country_code::< + BlocklistDnsFieldsV0_43, + BlocklistDnsFields, + >(value, locator)?, + EventKind::BlocklistKerberos => migrate_event_with_country_code::< + BlocklistKerberosFieldsV0_43, + BlocklistKerberosFields, + >(value, locator)?, + EventKind::BlocklistMalformedDns => migrate_event_with_country_code::< + BlocklistMalformedDnsFieldsV0_43, + BlocklistMalformedDnsFields, + >(value, locator)?, + EventKind::BlocklistMqtt => migrate_event_with_country_code::< + BlocklistMqttFieldsV0_43, + BlocklistMqttFields, + >(value, locator)?, + EventKind::BlocklistNfs => migrate_event_with_country_code::< + BlocklistNfsFieldsV0_43, + BlocklistNfsFields, + >(value, locator)?, + EventKind::BlocklistNtlm => migrate_event_with_country_code::< + BlocklistNtlmFieldsV0_43, + BlocklistNtlmFields, + >(value, locator)?, + EventKind::BlocklistRadius => migrate_event_with_country_code::< + BlocklistRadiusFieldsV0_43, + BlocklistRadiusFields, + >(value, locator)?, + EventKind::BlocklistRdp => migrate_event_with_country_code::< + BlocklistRdpFieldsV0_43, + BlocklistRdpFields, + >(value, locator)?, + EventKind::BlocklistSmb => migrate_event_with_country_code::< + BlocklistSmbFieldsV0_43, + BlocklistSmbFields, + >(value, locator)?, + EventKind::BlocklistSmtp => migrate_event_with_country_code::< + BlocklistSmtpFieldsV0_43, + BlocklistSmtpFields, + >(value, locator)?, + EventKind::BlocklistSsh => migrate_event_with_country_code::< + BlocklistSshFieldsV0_43, + BlocklistSshFields, + >(value, locator)?, + EventKind::BlocklistTls | EventKind::SuspiciousTlsTraffic => { + migrate_event_with_country_code::( + value, locator, + )? + } + EventKind::UnusualDestinationPattern => migrate_event_with_country_code::< + UnusualDestinationPatternFieldsV0_43, + UnusualDestinationPatternFields, + >(value, locator)?, + EventKind::NetworkThreat => migrate_event_with_country_code::< + NetworkThreatV0_43, + NetworkThreat, + >(value, locator)?, + // These event types don't have country code fields or use different structures + EventKind::WindowsThreat | EventKind::ExtraThreat => { + // Skip migration for these types - they don't have the standard + // orig_addr/resp_addr fields or use different serialization + skipped_count += 1; + continue; + } + }; + + // Update the event with migrated fields + events + .update( + (key.as_slice(), value.as_slice()), + (key.as_slice(), new_fields.as_slice()), + ) + .context("failed to update migrated event")?; + migrated_count += 1; + } + + info!( + "Successfully migrated {} events ({} skipped)", + migrated_count, skipped_count + ); + Ok(()) +} + +/// Trait for applying country code resolution to migrated event fields. +/// +/// Types implementing this trait can have their country code fields populated +/// from IP addresses using an ip2location database. +trait ResolveCountryCodes { + /// Resolves country codes from IP addresses using the provided locator. + fn resolve_country_codes(&mut self, locator: &ip2location::DB); +} + +/// Looks up the country code for an IP address using the ip2location database. +/// Returns the 2-letter country code as bytes, or "XX" if the lookup fails or +/// returns an invalid code. +fn lookup_country_code(locator: &ip2location::DB, addr: IpAddr) -> [u8; 2] { + crate::util::country_code_to_bytes(&crate::util::find_ip_country(locator, addr)) +} + +fn lookup_country_codes(locator: &ip2location::DB, addrs: I) -> Vec<[u8; 2]> +where + I: IntoIterator, +{ + addrs + .into_iter() + .map(|addr| lookup_country_code(locator, addr)) + .collect() +} + +macro_rules! impl_resolve_country_codes { + ( + $ty:ty, + orig: single ($orig_addr:ident => $orig_cc:ident), + resp: single ($resp_addr:ident => $resp_cc:ident) + $(,)? + ) => { + impl ResolveCountryCodes for $ty { + fn resolve_country_codes(&mut self, locator: &ip2location::DB) { + self.$orig_cc = lookup_country_code(locator, self.$orig_addr); + self.$resp_cc = lookup_country_code(locator, self.$resp_addr); + } + } + }; + ( + $ty:ty, + orig: single ($orig_addr:ident => $orig_cc:ident), + resp: multi ($resp_addrs:ident => $resp_ccs:ident) + $(,)? + ) => { + impl ResolveCountryCodes for $ty { + fn resolve_country_codes(&mut self, locator: &ip2location::DB) { + self.$orig_cc = lookup_country_code(locator, self.$orig_addr); + self.$resp_ccs = lookup_country_codes(locator, self.$resp_addrs.iter().copied()); + } + } + }; + ( + $ty:ty, + orig: multi ($orig_addrs:ident => $orig_ccs:ident), + resp: single ($resp_addr:ident => $resp_cc:ident) + $(,)? + ) => { + impl ResolveCountryCodes for $ty { + fn resolve_country_codes(&mut self, locator: &ip2location::DB) { + self.$orig_ccs = lookup_country_codes(locator, self.$orig_addrs.iter().copied()); + self.$resp_cc = lookup_country_code(locator, self.$resp_addr); + } + } + }; + ( + $ty:ty, + orig: multi ($orig_addrs:ident => $orig_ccs:ident), + resp: multi ($resp_addrs:ident => $resp_ccs:ident) + $(,)? + ) => { + impl ResolveCountryCodes for $ty { + fn resolve_country_codes(&mut self, locator: &ip2location::DB) { + self.$orig_ccs = lookup_country_codes(locator, self.$orig_addrs.iter().copied()); + self.$resp_ccs = lookup_country_codes(locator, self.$resp_addrs.iter().copied()); + } + } + }; +} + +// ============================================================================= +// ResolveCountryCodes implementations for event field types +// ============================================================================= + +use crate::event::{ + BlocklistBootpFields, BlocklistConnFields, BlocklistDceRpcFields, BlocklistDhcpFields, + BlocklistDnsFields, BlocklistKerberosFields, BlocklistMalformedDnsFields, BlocklistMqttFields, + BlocklistNfsFields, BlocklistNtlmFields, BlocklistRadiusFields, BlocklistRdpFields, + BlocklistSmbFields, BlocklistSmtpFields, BlocklistSshFields, BlocklistTlsFields, + CryptocurrencyMiningPoolFields, DgaFields, DnsEventFields, ExternalDdosFields, + FtpBruteForceFields, FtpEventFields, HttpEventFields, HttpThreatFields, LdapBruteForceFields, + LdapEventFields, MultiHostPortScanFields, NetworkThreat, PortScanFields, RdpBruteForceFields, + RepeatedHttpSessionsFields, UnusualDestinationPatternFields, +}; + +impl_resolve_country_codes!( + PortScanFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + MultiHostPortScanFields, + orig: single (orig_addr => orig_country_code), + resp: multi (resp_addrs => resp_country_codes), +); +impl_resolve_country_codes!( + ExternalDdosFields, + orig: multi (orig_addrs => orig_country_codes), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistConnFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + DnsEventFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + CryptocurrencyMiningPoolFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistDnsFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + HttpEventFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + RepeatedHttpSessionsFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + HttpThreatFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + DgaFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + RdpBruteForceFields, + orig: single (orig_addr => orig_country_code), + resp: multi (resp_addrs => resp_country_codes), +); +impl_resolve_country_codes!( + BlocklistRdpFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + FtpBruteForceFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + FtpEventFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + LdapBruteForceFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + LdapEventFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistSshFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistTlsFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistKerberosFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistSmtpFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistNfsFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistDhcpFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistDceRpcFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistNtlmFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistSmbFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistMqttFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistBootpFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistRadiusFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + BlocklistMalformedDnsFields, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); +impl_resolve_country_codes!( + NetworkThreat, + orig: single (orig_addr => orig_country_code), + resp: single (resp_addr => resp_country_code), +); + +impl ResolveCountryCodes for UnusualDestinationPatternFields { + fn resolve_country_codes(&mut self, locator: &ip2location::DB) { + self.resp_country_codes = self + .destination_ips + .iter() + .map(|addr| lookup_country_code(locator, *addr)) + .collect(); + } +} + +/// Helper function to migrate event fields from old format to new format +fn migrate_event_with_country_code( + old_data: &[u8], + locator: Option<&ip2location::DB>, +) -> Result> +where + O: serde::de::DeserializeOwned, + N: serde::Serialize + serde::de::DeserializeOwned + From + ResolveCountryCodes, +{ + // First try to deserialize as the new format (already migrated) + if bincode::deserialize::(old_data).is_ok() { + // Already in new format, no migration needed + return Ok(old_data.to_vec()); + } + + // Deserialize as old format and convert + let old_fields: O = + bincode::deserialize(old_data).context("failed to deserialize old event fields")?; + + let mut new_fields: N = old_fields.into(); + + // Apply country code resolution if locator is provided + if let Some(loc) = locator { + new_fields.resolve_country_codes(loc); + } + + bincode::serialize(&new_fields).context("failed to serialize new event fields") +} + /// Recursively creates `path` if not existed, creates the VERSION file /// under `path` if missing with current version number. Returns VERSION /// file path with VERSION number written on file. @@ -883,9 +1427,13 @@ mod tests { use std::io::Write; use std::path::Path; + use chrono::{TimeZone, Utc}; use semver::{Version, VersionReq}; + use super::migrate_event_country_codes; + use super::migration_structures::DnsEventFieldsV0_43; use super::{COMPATIBLE_VERSION_REQ, create_version_file, migrate_data_dir, read_version_file}; + use crate::event::{DnsEventFields, EventKind, EventMessage}; use crate::tables::NETWORK_TAGS; use crate::test::{DbGuard, acquire_db_permit}; use crate::{Indexable, Store}; @@ -913,7 +1461,7 @@ mod tests { write_version(backup_dir.path(), current_version); // This should succeed without calling any migration - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); assert!(result.is_ok()); // VERSION should remain unchanged @@ -938,7 +1486,7 @@ mod tests { write_version(data_dir.path(), &data_version.to_string()); write_version(backup_dir.path(), &backup_version.to_string()); - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); assert!(result.is_err()); let err = result.unwrap_err().to_string(); @@ -955,7 +1503,7 @@ mod tests { // Don't write any VERSION files - directories are empty - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); // Should succeed (empty dir gets current version) assert!(result.is_ok()); @@ -982,7 +1530,7 @@ mod tests { // Also need a file in backup to prevent it from being treated as empty write_version(backup_dir.path(), env!("CARGO_PKG_VERSION")); - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); assert!(result.is_err()); let err = result.unwrap_err().to_string(); @@ -1000,7 +1548,7 @@ mod tests { assert!(!data_dir.exists()); assert!(!backup_dir.exists()); - let result = migrate_data_dir(&data_dir, &backup_dir); + let result = migrate_data_dir(&data_dir, &backup_dir, None); // Should succeed assert!(result.is_ok()); @@ -1024,7 +1572,7 @@ mod tests { write_version(data_dir.path(), "0.30.0"); write_version(backup_dir.path(), "0.30.0"); - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); assert!(result.is_err()); let err = result.unwrap_err().to_string(); @@ -1114,7 +1662,7 @@ mod tests { write_version(data_dir.path(), &version.to_string()); write_version(backup_dir.path(), &version.to_string()); - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); assert!(result.is_ok(), "Migration should succeed from {version}"); let data_version = read_version_file(&data_dir.path().join("VERSION")).unwrap(); @@ -1145,7 +1693,7 @@ mod tests { write_version(backup_dir.path(), "0.42.0"); // Run the migration - let result = migrate_data_dir(data_dir.path(), backup_dir.path()); + let result = migrate_data_dir(data_dir.path(), backup_dir.path(), None); assert!(result.is_ok(), "Migration should succeed"); // Verify database opens with the current column families @@ -2194,4 +2742,65 @@ mod tests { .unwrap(); assert_eq!(id2, 2, "Second available id should fill the gap at 2"); } + + #[test] + fn migrate_event_country_codes_sets_default_country_codes() { + let schema = TestSchema::new(); + let old_fields = DnsEventFieldsV0_43 { + sensor: "collector1".to_string(), + orig_addr: std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), + orig_port: 10000, + resp_addr: std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 2)), + resp_port: 53, + proto: 17, + start_time: Utc + .with_ymd_and_hms(1970, 1, 1, 0, 1, 1) + .unwrap() + .timestamp_nanos_opt() + .unwrap(), + duration: 0, + orig_pkts: 0, + resp_pkts: 0, + orig_l2_bytes: 0, + resp_l2_bytes: 0, + query: "foo.com".to_string(), + answer: vec!["10.10.10.10".to_string()], + trans_id: 123, + rtt: 1, + qclass: 0, + qtype: 0, + rcode: 0, + aa_flag: false, + tc_flag: false, + rd_flag: false, + ra_flag: true, + ttl: vec![120], + confidence: 0.9, + category: Some(crate::EventCategory::CommandAndControl), + }; + + let event_time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(); + let serialized = bincode::serialize(&old_fields).unwrap(); + let message = EventMessage { + time: event_time, + kind: EventKind::DnsCovertChannel, + fields: serialized, + }; + let event_db = schema.store.events(); + event_db.put(&message).unwrap(); + + migrate_event_country_codes(&schema.store, None).unwrap(); + + let event_db = schema.store.events(); + let mut iter = event_db.raw_iter_forward(); + let (_, value) = iter.next().expect("migrated event"); + let migrated: DnsEventFields = bincode::deserialize(&value).unwrap(); + + assert_eq!(migrated.orig_country_code, *b"ZZ"); + assert_eq!(migrated.resp_country_code, *b"ZZ"); + assert!(iter.next().is_none()); + drop(iter); + + let _ = schema.close(); + } } diff --git a/src/migration/migration_structures.rs b/src/migration/migration_structures.rs index 1f8c4bdf..f056f32e 100644 --- a/src/migration/migration_structures.rs +++ b/src/migration/migration_structures.rs @@ -4,12 +4,14 @@ //! and must not be modified. They are used to migrate data from //! old formats to new formats. +use std::net::IpAddr; + use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::PeriodForSearch; use crate::event::{FilterEndpoint, FlowKind, LearningMethod}; -use crate::types::HostNetworkGroup; +use crate::types::{EventCategory, HostNetworkGroup}; /// Filter value structure from version 0.41.x /// @@ -104,3 +106,1854 @@ pub(crate) struct NetworkValueV0_43 { pub(crate) tag_ids: Vec, pub(crate) creation_time: DateTime, } + +// ============================================================================= +// Event Fields V0_43 structures (before orig_country_code/resp_country_code) +// These structs represent event field schemas from version 0.43.x without +// country code fields. From 0.44.x, country code fields were added. +// ============================================================================= + +/// FTP Command structure for `V0_43` migration +#[derive(Clone, Deserialize, Serialize)] +pub(crate) struct FtpCommandV0_43 { + pub command: String, + pub reply_code: String, + pub reply_msg: String, +} + +/// Port scan fields from version 0.43.x (before country codes were added) +#[derive(Serialize, Deserialize)] +pub(crate) struct PortScanFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub resp_addr: IpAddr, + pub resp_ports: Vec, + pub start_time: i64, + pub end_time: i64, + pub proto: u8, + pub confidence: f32, + pub category: Option, +} + +/// Multi-host port scan fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct MultiHostPortScanFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub resp_port: u16, + pub resp_addrs: Vec, + pub proto: u8, + pub start_time: i64, + pub end_time: i64, + pub confidence: f32, + pub category: Option, +} + +/// External `DDoS` fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct ExternalDdosFieldsV0_43 { + pub sensor: String, + pub orig_addrs: Vec, + pub resp_addr: IpAddr, + pub proto: u8, + pub start_time: i64, + pub end_time: i64, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist connection fields from version 0.43.x +#[derive(Deserialize, Serialize)] +pub(crate) struct BlocklistConnFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub conn_state: String, + pub start_time: i64, + pub duration: i64, + pub service: String, + pub orig_bytes: u64, + pub resp_bytes: u64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub confidence: f32, + pub category: Option, +} + +/// DNS event fields from version 0.43.x +#[allow(clippy::struct_excessive_bools)] +#[derive(Deserialize, Serialize)] +pub(crate) struct DnsEventFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub query: String, + pub answer: Vec, + pub trans_id: u16, + pub rtt: i64, + pub qclass: u16, + pub qtype: u16, + pub rcode: u16, + pub aa_flag: bool, + pub tc_flag: bool, + pub rd_flag: bool, + pub ra_flag: bool, + pub ttl: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Cryptocurrency mining pool fields from version 0.43.x +#[allow(clippy::struct_excessive_bools)] +#[derive(Deserialize, Serialize)] +pub(crate) struct CryptocurrencyMiningPoolFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub query: String, + pub answer: Vec, + pub trans_id: u16, + pub rtt: i64, + pub qclass: u16, + pub qtype: u16, + pub rcode: u16, + pub aa_flag: bool, + pub tc_flag: bool, + pub rd_flag: bool, + pub ra_flag: bool, + pub ttl: Vec, + pub coins: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist DNS fields from version 0.43.x +#[allow(clippy::struct_excessive_bools)] +#[derive(Deserialize, Serialize)] +pub(crate) struct BlocklistDnsFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub query: String, + pub answer: Vec, + pub trans_id: u16, + pub rtt: i64, + pub qclass: u16, + pub qtype: u16, + pub rcode: u16, + pub aa_flag: bool, + pub tc_flag: bool, + pub rd_flag: bool, + pub ra_flag: bool, + pub ttl: Vec, + pub confidence: f32, + pub category: Option, +} + +/// HTTP event fields from version 0.43.x +#[derive(Deserialize, Serialize)] +pub(crate) struct HttpEventFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub method: String, + pub host: String, + pub uri: String, + pub referer: String, + pub version: String, + pub user_agent: String, + pub request_len: usize, + pub response_len: usize, + pub status_code: u16, + pub status_msg: String, + pub username: String, + pub password: String, + pub cookie: String, + pub content_encoding: String, + pub content_type: String, + pub cache_control: String, + pub filenames: Vec, + pub mime_types: Vec, + pub body: Vec, + pub state: String, + pub confidence: f32, + pub category: Option, +} + +/// Repeated HTTP sessions fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct RepeatedHttpSessionsFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub end_time: i64, + pub confidence: f32, + pub category: Option, +} + +/// HTTP threat fields from version 0.43.x +#[derive(Deserialize, Serialize)] +pub(crate) struct HttpThreatFieldsV0_43 { + #[serde(with = "chrono::serde::ts_nanoseconds")] + pub time: DateTime, + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub method: String, + pub host: String, + pub uri: String, + pub referer: String, + pub version: String, + pub user_agent: String, + pub request_len: usize, + pub response_len: usize, + pub status_code: u16, + pub status_msg: String, + pub username: String, + pub password: String, + pub cookie: String, + pub content_encoding: String, + pub content_type: String, + pub cache_control: String, + pub filenames: Vec, + pub mime_types: Vec, + pub body: Vec, + pub state: String, + pub db_name: String, + pub rule_id: u32, + pub matched_to: String, + pub cluster_id: Option, + pub attack_kind: String, + pub confidence: f32, + pub category: Option, +} + +/// DGA fields from version 0.43.x +#[derive(Deserialize, Serialize)] +pub(crate) struct DgaFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub method: String, + pub host: String, + pub uri: String, + pub referer: String, + pub version: String, + pub user_agent: String, + pub request_len: usize, + pub response_len: usize, + pub status_code: u16, + pub status_msg: String, + pub username: String, + pub password: String, + pub cookie: String, + pub content_encoding: String, + pub content_type: String, + pub cache_control: String, + pub filenames: Vec, + pub mime_types: Vec, + pub body: Vec, + pub state: String, + pub confidence: f32, + pub category: Option, +} + +/// RDP brute force fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct RdpBruteForceFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub resp_addrs: Vec, + pub start_time: i64, + pub end_time: i64, + pub proto: u8, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist RDP fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistRdpFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub cookie: String, + pub confidence: f32, + pub category: Option, +} + +/// FTP brute force fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct FtpBruteForceFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub user_list: Vec, + pub start_time: i64, + pub end_time: i64, + pub is_internal: bool, + pub confidence: f32, + pub category: Option, +} + +/// FTP event fields from version 0.43.x +#[derive(Deserialize, Serialize)] +pub(crate) struct FtpEventFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub user: String, + pub password: String, + pub commands: Vec, + pub confidence: f32, + pub category: Option, +} + +/// LDAP brute force fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct LdapBruteForceFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub user_pw_list: Vec<(String, String)>, + pub start_time: i64, + pub end_time: i64, + pub confidence: f32, + pub category: Option, +} + +/// LDAP event fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct LdapEventFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub message_id: u32, + pub version: u8, + pub opcode: Vec, + pub result: Vec, + pub diagnostic_message: Vec, + pub object: Vec, + pub argument: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist SSH fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistSshFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub client: String, + pub server: String, + pub cipher_alg: String, + pub mac_alg: String, + pub compression_alg: String, + pub kex_alg: String, + pub host_key_alg: String, + pub hassh_algorithms: String, + pub hassh: String, + pub hassh_server_algorithms: String, + pub hassh_server: String, + pub client_shka: String, + pub server_shka: String, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist TLS fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistTlsFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub server_name: String, + pub alpn_protocol: String, + pub ja3: String, + pub version: String, + pub client_cipher_suites: Vec, + pub client_extensions: Vec, + pub cipher: u16, + pub extensions: Vec, + pub ja3s: String, + pub serial: String, + pub subject_country: String, + pub subject_org_name: String, + pub subject_common_name: String, + pub validity_not_before: i64, + pub validity_not_after: i64, + pub subject_alt_name: String, + pub issuer_country: String, + pub issuer_org_name: String, + pub issuer_org_unit_name: String, + pub issuer_common_name: String, + pub last_alert: u8, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist Kerberos fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistKerberosFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub client_time: i64, + pub server_time: i64, + pub error_code: u32, + pub client_realm: String, + pub cname_type: u8, + pub client_name: Vec, + pub realm: String, + pub sname_type: u8, + pub service_name: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist SMTP fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistSmtpFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub mailfrom: String, + pub date: String, + pub from: String, + pub to: String, + pub subject: String, + pub agent: String, + pub state: String, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist NFS fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistNfsFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub read_files: Vec, + pub write_files: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist DHCP fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistDhcpFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub msg_type: u8, + pub ciaddr: IpAddr, + pub yiaddr: IpAddr, + pub siaddr: IpAddr, + pub giaddr: IpAddr, + pub subnet_mask: IpAddr, + pub router: Vec, + pub domain_name_server: Vec, + pub req_ip_addr: IpAddr, + pub lease_time: u32, + pub server_id: IpAddr, + pub param_req_list: Vec, + pub message: String, + pub renewal_time: u32, + pub rebinding_time: u32, + pub class_id: Vec, + pub client_id_type: u8, + pub client_id: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist DCE-RPC fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistDceRpcFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub rtt: i64, + pub named_pipe: String, + pub endpoint: String, + pub operation: String, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist NTLM fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistNtlmFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub protocol: String, + pub username: String, + pub hostname: String, + pub domainname: String, + pub success: String, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist SMB fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistSmbFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub command: u8, + pub path: String, + pub service: String, + pub file_name: String, + pub file_size: u64, + pub resource_type: u16, + pub fid: u16, + pub create_time: i64, + pub access_time: i64, + pub write_time: i64, + pub change_time: i64, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist MQTT fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistMqttFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub protocol: String, + pub version: u8, + pub client_id: String, + pub connack_reason: u8, + pub subscribe: Vec, + pub suback_reason: Vec, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist BOOTP fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistBootpFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub op: u8, + pub htype: u8, + pub hops: u8, + pub xid: u32, + pub ciaddr: IpAddr, + pub yiaddr: IpAddr, + pub siaddr: IpAddr, + pub giaddr: IpAddr, + pub chaddr: Vec, + pub sname: String, + pub file: String, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist Radius fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistRadiusFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub id: u8, + pub code: u8, + pub resp_code: u8, + pub auth: String, + pub resp_auth: String, + pub user_name: Vec, + pub user_passwd: Vec, + pub chap_passwd: Vec, + pub nas_ip: IpAddr, + pub nas_port: u32, + pub state: Vec, + pub nas_id: Vec, + pub nas_port_type: u32, + pub message: String, + pub confidence: f32, + pub category: Option, +} + +/// Blocklist malformed DNS fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct BlocklistMalformedDnsFieldsV0_43 { + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub start_time: i64, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub trans_id: u16, + pub flags: u16, + pub question_count: u16, + pub answer_count: u16, + pub authority_count: u16, + pub additional_count: u16, + pub query_count: u32, + pub resp_count: u32, + pub query_bytes: u64, + pub resp_bytes: u64, + pub query_body: Vec>, + pub resp_body: Vec>, + pub confidence: f32, + pub category: Option, +} + +/// Unusual destination pattern fields from version 0.43.x +#[derive(Serialize, Deserialize)] +pub(crate) struct UnusualDestinationPatternFieldsV0_43 { + pub sensor: String, + pub start_time: i64, + pub end_time: i64, + pub destination_ips: Vec, + pub count: usize, + pub expected_mean: f64, + pub std_deviation: f64, + pub z_score: f64, + pub confidence: f32, + pub category: Option, +} + +// ============================================================================= +// From trait implementations for V0_43 -> V0_44 conversions +// These enable migrating old event fields to new format with country codes +// ============================================================================= + +use crate::event::ExternalDdosFields; +use crate::event::{ + BlocklistBootpFields, BlocklistConnFields, BlocklistDceRpcFields, BlocklistDhcpFields, + BlocklistDnsFields, BlocklistKerberosFields, BlocklistMalformedDnsFields, BlocklistMqttFields, + BlocklistNfsFields, BlocklistNtlmFields, BlocklistRadiusFields, BlocklistRdpFields, + BlocklistSmbFields, BlocklistSmtpFields, BlocklistSshFields, BlocklistTlsFields, + CryptocurrencyMiningPoolFields, DgaFields, DnsEventFields, FtpBruteForceFields, FtpCommand, + FtpEventFields, HttpEventFields, HttpThreatFields, LdapBruteForceFields, LdapEventFields, + MultiHostPortScanFields, PortScanFields, RdpBruteForceFields, RepeatedHttpSessionsFields, + UnusualDestinationPatternFields, +}; + +impl From for PortScanFields { + fn from(old: PortScanFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_country_code: *b"ZZ", + resp_ports: old.resp_ports, + start_time: old.start_time, + end_time: old.end_time, + proto: old.proto, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for MultiHostPortScanFields { + fn from(old: MultiHostPortScanFieldsV0_43) -> Self { + let resp_country_codes = vec![*b"ZZ"; old.resp_addrs.len()]; + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_country_code: *b"ZZ", + resp_addrs: old.resp_addrs, + resp_country_codes, + resp_port: old.resp_port, + proto: old.proto, + start_time: old.start_time, + end_time: old.end_time, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for ExternalDdosFields { + fn from(old: ExternalDdosFieldsV0_43) -> Self { + let orig_country_codes = vec![*b"ZZ"; old.orig_addrs.len()]; + Self { + sensor: old.sensor, + orig_addrs: old.orig_addrs, + orig_country_codes, + resp_addr: old.resp_addr, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + end_time: old.end_time, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistConnFields { + fn from(old: BlocklistConnFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + conn_state: old.conn_state, + start_time: old.start_time, + duration: old.duration, + service: old.service, + orig_bytes: old.orig_bytes, + resp_bytes: old.resp_bytes, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for DnsEventFields { + fn from(old: DnsEventFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + query: old.query, + answer: old.answer, + trans_id: old.trans_id, + rtt: old.rtt, + qclass: old.qclass, + qtype: old.qtype, + rcode: old.rcode, + aa_flag: old.aa_flag, + tc_flag: old.tc_flag, + rd_flag: old.rd_flag, + ra_flag: old.ra_flag, + ttl: old.ttl, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for CryptocurrencyMiningPoolFields { + fn from(old: CryptocurrencyMiningPoolFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + query: old.query, + answer: old.answer, + trans_id: old.trans_id, + rtt: old.rtt, + qclass: old.qclass, + qtype: old.qtype, + rcode: old.rcode, + aa_flag: old.aa_flag, + tc_flag: old.tc_flag, + rd_flag: old.rd_flag, + ra_flag: old.ra_flag, + ttl: old.ttl, + coins: old.coins, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistDnsFields { + fn from(old: BlocklistDnsFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + query: old.query, + answer: old.answer, + trans_id: old.trans_id, + rtt: old.rtt, + qclass: old.qclass, + qtype: old.qtype, + rcode: old.rcode, + aa_flag: old.aa_flag, + tc_flag: old.tc_flag, + rd_flag: old.rd_flag, + ra_flag: old.ra_flag, + ttl: old.ttl, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for HttpEventFields { + fn from(old: HttpEventFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + method: old.method, + host: old.host, + uri: old.uri, + referer: old.referer, + version: old.version, + user_agent: old.user_agent, + request_len: old.request_len, + response_len: old.response_len, + status_code: old.status_code, + status_msg: old.status_msg, + username: old.username, + password: old.password, + cookie: old.cookie, + content_encoding: old.content_encoding, + content_type: old.content_type, + cache_control: old.cache_control, + filenames: old.filenames, + mime_types: old.mime_types, + body: old.body, + state: old.state, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for RepeatedHttpSessionsFields { + fn from(old: RepeatedHttpSessionsFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + end_time: old.end_time, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for HttpThreatFields { + fn from(old: HttpThreatFieldsV0_43) -> Self { + Self { + time: old.time, + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + method: old.method, + host: old.host, + uri: old.uri, + referer: old.referer, + version: old.version, + user_agent: old.user_agent, + request_len: old.request_len, + response_len: old.response_len, + status_code: old.status_code, + status_msg: old.status_msg, + username: old.username, + password: old.password, + cookie: old.cookie, + content_encoding: old.content_encoding, + content_type: old.content_type, + cache_control: old.cache_control, + filenames: old.filenames, + mime_types: old.mime_types, + body: old.body, + state: old.state, + db_name: old.db_name, + rule_id: old.rule_id, + matched_to: old.matched_to, + cluster_id: old.cluster_id, + attack_kind: old.attack_kind, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for DgaFields { + fn from(old: DgaFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + method: old.method, + host: old.host, + uri: old.uri, + referer: old.referer, + version: old.version, + user_agent: old.user_agent, + request_len: old.request_len, + response_len: old.response_len, + status_code: old.status_code, + status_msg: old.status_msg, + username: old.username, + password: old.password, + cookie: old.cookie, + content_encoding: old.content_encoding, + content_type: old.content_type, + cache_control: old.cache_control, + filenames: old.filenames, + mime_types: old.mime_types, + body: old.body, + state: old.state, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for RdpBruteForceFields { + fn from(old: RdpBruteForceFieldsV0_43) -> Self { + let resp_country_codes = vec![*b"ZZ"; old.resp_addrs.len()]; + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_country_code: *b"ZZ", + resp_addrs: old.resp_addrs, + resp_country_codes, + start_time: old.start_time, + end_time: old.end_time, + proto: old.proto, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistRdpFields { + fn from(old: BlocklistRdpFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + cookie: old.cookie, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for FtpBruteForceFields { + fn from(old: FtpBruteForceFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + user_list: old.user_list, + start_time: old.start_time, + end_time: old.end_time, + is_internal: old.is_internal, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for FtpCommand { + fn from(old: FtpCommandV0_43) -> Self { + use std::net::{IpAddr, Ipv4Addr}; + Self { + command: old.command, + reply_code: old.reply_code, + reply_msg: old.reply_msg, + // New fields added after V0_43 - use sensible defaults + data_passive: false, + data_orig_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED), + data_resp_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED), + data_resp_port: 0, + file: String::new(), + file_size: 0, + file_id: String::new(), + } + } +} + +impl From for FtpEventFields { + fn from(old: FtpEventFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + user: old.user, + password: old.password, + commands: old.commands.into_iter().map(Into::into).collect(), + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for LdapBruteForceFields { + fn from(old: LdapBruteForceFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + user_pw_list: old.user_pw_list, + start_time: old.start_time, + end_time: old.end_time, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for LdapEventFields { + fn from(old: LdapEventFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + message_id: old.message_id, + version: old.version, + opcode: old.opcode, + result: old.result, + diagnostic_message: old.diagnostic_message, + object: old.object, + argument: old.argument, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistSshFields { + fn from(old: BlocklistSshFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + client: old.client, + server: old.server, + cipher_alg: old.cipher_alg, + mac_alg: old.mac_alg, + compression_alg: old.compression_alg, + kex_alg: old.kex_alg, + host_key_alg: old.host_key_alg, + hassh_algorithms: old.hassh_algorithms, + hassh: old.hassh, + hassh_server_algorithms: old.hassh_server_algorithms, + hassh_server: old.hassh_server, + client_shka: old.client_shka, + server_shka: old.server_shka, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistTlsFields { + fn from(old: BlocklistTlsFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + server_name: old.server_name, + alpn_protocol: old.alpn_protocol, + ja3: old.ja3, + version: old.version, + client_cipher_suites: old.client_cipher_suites, + client_extensions: old.client_extensions, + cipher: old.cipher, + extensions: old.extensions, + ja3s: old.ja3s, + serial: old.serial, + subject_country: old.subject_country, + subject_org_name: old.subject_org_name, + subject_common_name: old.subject_common_name, + validity_not_before: old.validity_not_before, + validity_not_after: old.validity_not_after, + subject_alt_name: old.subject_alt_name, + issuer_country: old.issuer_country, + issuer_org_name: old.issuer_org_name, + issuer_org_unit_name: old.issuer_org_unit_name, + issuer_common_name: old.issuer_common_name, + last_alert: old.last_alert, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistKerberosFields { + fn from(old: BlocklistKerberosFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + client_time: old.client_time, + server_time: old.server_time, + error_code: old.error_code, + client_realm: old.client_realm, + cname_type: old.cname_type, + client_name: old.client_name, + realm: old.realm, + sname_type: old.sname_type, + service_name: old.service_name, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistSmtpFields { + fn from(old: BlocklistSmtpFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + mailfrom: old.mailfrom, + date: old.date, + from: old.from, + to: old.to, + subject: old.subject, + agent: old.agent, + state: old.state, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistNfsFields { + fn from(old: BlocklistNfsFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + read_files: old.read_files, + write_files: old.write_files, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistDhcpFields { + fn from(old: BlocklistDhcpFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + msg_type: old.msg_type, + ciaddr: old.ciaddr, + yiaddr: old.yiaddr, + siaddr: old.siaddr, + giaddr: old.giaddr, + subnet_mask: old.subnet_mask, + router: old.router, + domain_name_server: old.domain_name_server, + req_ip_addr: old.req_ip_addr, + lease_time: old.lease_time, + server_id: old.server_id, + param_req_list: old.param_req_list, + message: old.message, + renewal_time: old.renewal_time, + rebinding_time: old.rebinding_time, + class_id: old.class_id, + client_id_type: old.client_id_type, + client_id: old.client_id, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistDceRpcFields { + fn from(old: BlocklistDceRpcFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + rtt: old.rtt, + named_pipe: old.named_pipe, + endpoint: old.endpoint, + operation: old.operation, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistNtlmFields { + fn from(old: BlocklistNtlmFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + protocol: old.protocol, + username: old.username, + hostname: old.hostname, + domainname: old.domainname, + success: old.success, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistSmbFields { + fn from(old: BlocklistSmbFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + command: old.command, + path: old.path, + service: old.service, + file_name: old.file_name, + file_size: old.file_size, + resource_type: old.resource_type, + fid: old.fid, + create_time: old.create_time, + access_time: old.access_time, + write_time: old.write_time, + change_time: old.change_time, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistMqttFields { + fn from(old: BlocklistMqttFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + protocol: old.protocol, + version: old.version, + client_id: old.client_id, + connack_reason: old.connack_reason, + subscribe: old.subscribe, + suback_reason: old.suback_reason, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistBootpFields { + fn from(old: BlocklistBootpFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + op: old.op, + htype: old.htype, + hops: old.hops, + xid: old.xid, + ciaddr: old.ciaddr, + yiaddr: old.yiaddr, + siaddr: old.siaddr, + giaddr: old.giaddr, + chaddr: old.chaddr, + sname: old.sname, + file: old.file, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistRadiusFields { + fn from(old: BlocklistRadiusFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + id: old.id, + code: old.code, + resp_code: old.resp_code, + auth: old.auth, + resp_auth: old.resp_auth, + user_name: old.user_name, + user_passwd: old.user_passwd, + chap_passwd: old.chap_passwd, + nas_ip: old.nas_ip, + nas_port: old.nas_port, + state: old.state, + nas_id: old.nas_id, + nas_port_type: old.nas_port_type, + message: old.message, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for BlocklistMalformedDnsFields { + fn from(old: BlocklistMalformedDnsFieldsV0_43) -> Self { + Self { + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + trans_id: old.trans_id, + flags: old.flags, + question_count: old.question_count, + answer_count: old.answer_count, + authority_count: old.authority_count, + additional_count: old.additional_count, + query_count: old.query_count, + resp_count: old.resp_count, + query_bytes: old.query_bytes, + resp_bytes: old.resp_bytes, + query_body: old.query_body, + resp_body: old.resp_body, + confidence: old.confidence, + category: old.category, + } + } +} + +impl From for UnusualDestinationPatternFields { + fn from(old: UnusualDestinationPatternFieldsV0_43) -> Self { + let resp_country_codes = vec![*b"ZZ"; old.destination_ips.len()]; + Self { + sensor: old.sensor, + start_time: old.start_time, + end_time: old.end_time, + destination_ips: old.destination_ips, + resp_country_codes, + count: old.count, + expected_mean: old.expected_mean, + std_deviation: old.std_deviation, + z_score: old.z_score, + confidence: old.confidence, + category: old.category, + } + } +} + +/// Network threat from version 0.43.x (before country codes were added) +#[derive(Deserialize, Serialize)] +pub(crate) struct NetworkThreatV0_43 { + #[serde(with = "chrono::serde::ts_nanoseconds")] + pub time: DateTime, + pub sensor: String, + pub orig_addr: IpAddr, + pub orig_port: u16, + pub resp_addr: IpAddr, + pub resp_port: u16, + pub proto: u8, + pub service: String, + #[serde(with = "chrono::serde::ts_nanoseconds")] + pub start_time: DateTime, + pub duration: i64, + pub orig_pkts: u64, + pub resp_pkts: u64, + pub orig_l2_bytes: u64, + pub resp_l2_bytes: u64, + pub content: String, + pub db_name: String, + pub rule_id: u32, + pub matched_to: String, + pub cluster_id: Option, + pub attack_kind: String, + pub confidence: f32, + pub category: Option, + pub triage_scores: Option>, +} + +use crate::event::NetworkThreat; + +impl From for NetworkThreat { + fn from(old: NetworkThreatV0_43) -> Self { + Self { + time: old.time, + sensor: old.sensor, + orig_addr: old.orig_addr, + orig_port: old.orig_port, + orig_country_code: *b"ZZ", + resp_addr: old.resp_addr, + resp_port: old.resp_port, + resp_country_code: *b"ZZ", + proto: old.proto, + service: old.service, + start_time: old.start_time, + duration: old.duration, + orig_pkts: old.orig_pkts, + resp_pkts: old.resp_pkts, + orig_l2_bytes: old.orig_l2_bytes, + resp_l2_bytes: old.resp_l2_bytes, + content: old.content, + db_name: old.db_name, + rule_id: old.rule_id, + matched_to: old.matched_to, + cluster_id: old.cluster_id, + attack_kind: old.attack_kind, + confidence: old.confidence, + category: old.category, + triage_scores: old.triage_scores, + } + } +} diff --git a/src/util.rs b/src/util.rs index 9f64a052..8ce9d30b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,6 +2,9 @@ use std::net::IpAddr; +/// The unknown country code, used when lookup fails or returns an invalid code. +pub const UNKNOWN_COUNTRY_CODE: &str = "XX"; + /// Looks up the country code for the given IP address. /// /// # Arguments @@ -11,7 +14,8 @@ use std::net::IpAddr; /// /// # Returns /// -/// Returns the two-letter country code for the IP address, or "XX" if the lookup fails. +/// Returns the two-letter country code for the IP address, or "XX" if the lookup fails +/// or the returned code is not a valid two-letter alphabetic code. #[must_use] pub fn find_ip_country(locator: &ip2location::DB, addr: IpAddr) -> String { locator @@ -19,7 +23,26 @@ pub fn find_ip_country(locator: &ip2location::DB, addr: IpAddr) -> String { .map(|r| get_record_country_short_name(&r)) .ok() .flatten() - .unwrap_or_else(|| "XX".to_string()) + .filter(|code| is_valid_country_code(code)) + .unwrap_or_else(|| UNKNOWN_COUNTRY_CODE.to_string()) +} + +/// Validates that the given country code is a valid two-letter alphabetic code. +fn is_valid_country_code(code: &str) -> bool { + let bytes = code.as_bytes(); + bytes.len() == 2 && bytes[0].is_ascii_alphabetic() && bytes[1].is_ascii_alphabetic() +} + +/// Converts a country code string to a 2-byte array. +/// Returns `[b'X', b'X']` if the code is invalid. +#[must_use] +pub fn country_code_to_bytes(code: &str) -> [u8; 2] { + let bytes = code.as_bytes(); + if bytes.len() == 2 { + [bytes[0], bytes[1]] + } else { + *b"XX" + } } fn get_record_country_short_name(record: &ip2location::Record) -> Option {