bubble up parsing errors

This commit is contained in:
Pierre Dubouilh
2021-10-17 18:59:56 +02:00
parent 70cbeae0ed
commit 6741f48285
6 changed files with 121 additions and 30 deletions
Generated
+21
View File
@@ -51,6 +51,7 @@ dependencies = [
"lazy_static", "lazy_static",
"linemux", "linemux",
"regex", "regex",
"thiserror",
"tokio", "tokio",
] ]
@@ -398,6 +399,26 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.12.0" version = "1.12.0"
+2 -1
View File
@@ -11,4 +11,5 @@ linemux = "0.2"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] } tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
regex = "1.5.4" regex = "1.5.4"
clap = "2.33.3" clap = "2.33.3"
thiserror = "1.0.26"
+38 -17
View File
@@ -1,28 +1,30 @@
use std::net::IpAddr; use crate::utils::Error;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::net::IpAddr;
// TODO: allow user-provided list // TODO: allow user-provided list
// TODO: match different error-levels (10 404, but only 5 401, etc...) // TODO: match different error-levels (10 404, but only 5 401, etc...)
lazy_static! { lazy_static! {
static ref BAD_STATUSES: [&'static str; 2] = ["401", "429"]; static ref BAD_STATUSES: [u32; 2] = [401, 429];
} }
pub fn parse(line: &str) -> Option<IpAddr> { pub fn parse(line: &str) -> Result<Option<IpAddr>, Error> {
// TODO: Use a proper parser ? // TODO: Use a proper parser ?
let elts: Vec<&str> = line.split_whitespace().collect(); let elts: Vec<&str> = line.split_whitespace().collect();
let ip = elts[0].parse::<IpAddr>().ok();
let http_code = elts[elts.len() - 2] as &str;
BAD_STATUSES.iter().find_map( let ip_str = elts[0];
|bad_status| { let ip = ip_str.parse::<IpAddr>().or(Err(Error::CantParse))?;
if http_code == *bad_status {
ip let http_code_str = elts[elts.len() - 2] as &str;
} else { let http_code = http_code_str.parse::<u32>().or(Err(Error::CantParse))?;
None
} for status in BAD_STATUSES.iter() {
}, if *status == http_code {
) return Ok(Some(ip));
}
}
Ok(None)
} }
#[cfg(test)] #[cfg(test)]
@@ -36,7 +38,10 @@ mod tests {
"8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 429 923", "8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 429 923",
]; ];
vectors.iter().for_each(|e| assert!(parse(*e).is_some())) vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_some());
})
} }
#[test] #[test]
@@ -46,6 +51,22 @@ mod tests {
"8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 404 923", "8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 404 923",
]; ];
vectors.iter().for_each(|e| assert!(parse(*e).is_none())) vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_none());
})
}
#[test]
fn malformed() {
let vectors = [
"8.8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 200 923",
"8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\"",
];
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.is_err());
})
} }
} }
+11 -4
View File
@@ -6,6 +6,7 @@ mod utils;
mod jail; mod jail;
use crate::jail::Jail; use crate::jail::Jail;
use crate::utils::Error;
async fn run() -> Option<()> { async fn run() -> Option<()> {
let args = utils::cli().get_matches(); let args = utils::cli().get_matches();
@@ -36,12 +37,18 @@ async fn run() -> Option<()> {
let payload = line.line(); let payload = line.line();
let path = line.source().display().to_string(); let path = line.source().display().to_string();
if do_sshd && path.ends_with(path_sshd) { let res = if do_sshd && path.ends_with(path_sshd) {
sshd::parse(payload).and_then(|ip| jail.probe(ip)); sshd::parse(payload)
} else if do_clf && path.ends_with(path_clf) { } else if do_clf && path.ends_with(path_clf) {
clf::parse(payload).and_then(|ip| jail.probe(ip)); clf::parse(payload)
} else { } else {
eprintln!("! unknown logline: {}", path); Err(Error::UnknownError)
};
if let Ok(Some(ip)) = res {
jail.probe(ip);
} else {
eprintln!("! error processing logline: {}", path);
} }
} }
+40 -8
View File
@@ -1,7 +1,9 @@
use crate::utils::Error;
use std::net::IpAddr; use std::net::IpAddr;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use std::str::FromStr;
struct Rule { struct Rule {
matcher: String, matcher: String,
@@ -15,7 +17,7 @@ lazy_static! {
extractor: Regex::new(r"(from.)(.*)(.port)").unwrap(), extractor: Regex::new(r"(from.)(.*)(.port)").unwrap(),
}, },
Rule { Rule {
matcher: "Invalid user".to_string(), matcher: "Invalid user ".to_string(),
extractor: Regex::new(r"(from.)(.*)").unwrap(), extractor: Regex::new(r"(from.)(.*)").unwrap(),
}, },
Rule { Rule {
@@ -25,16 +27,27 @@ lazy_static! {
]; ];
} }
pub fn parse(line: &str) -> Option<IpAddr> { pub fn parse(line: &str) -> Result<Option<IpAddr>, Error> {
let ret = SSHD_BAD.iter().find_map(|rule| { let hits = SSHD_BAD.iter().find_map(|rule| {
if line.contains(&rule.matcher) { if line.contains(&rule.matcher) {
rule.extractor.captures(line)?.get(2) rule.extractor.captures(line)
} else { } else {
None None
} }
})?; });
ret.as_str().parse::<IpAddr>().ok() if hits.is_none() {
return Ok(None);
}
let ip = hits
.and_then(|c| c.get(2))
.and_then(|m| IpAddr::from_str(m.as_str()).ok());
match ip {
Some(ip) => Ok(Some(ip)),
None => Err(Error::CantParse),
}
} }
#[cfg(test)] #[cfg(test)]
@@ -49,7 +62,10 @@ mod tests {
"Sep 26 06:25:32 livecompute sshd[23254]: Invalid user neal from 35.184.211.144" "Sep 26 06:25:32 livecompute sshd[23254]: Invalid user neal from 35.184.211.144"
]; ];
vectors.iter().for_each(|e| assert!(parse(*e).is_some())) vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_some());
})
} }
#[test] #[test]
@@ -60,6 +76,22 @@ mod tests {
"Sep 26 06:25:32 livecompute sshd[23254]: very good user neal from 35.184.211.144" "Sep 26 06:25:32 livecompute sshd[23254]: very good user neal from 35.184.211.144"
]; ];
vectors.iter().for_each(|e| assert!(parse(*e).is_none())) vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_none());
})
}
#[test]
fn malformed() {
let vectors = [
"Sep 26 06:25:19 livecompute sshd[23246]: Failed password for root from 179.124.36.195.232 port 41883 ssh2",
"Sep 26 06:26:14 livecompute sshd[23292]: pam_unix(sshd:auth): authentication failure; logname= u =0 tty=ssh ruser= rhost=",
];
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.is_err());
})
} }
} }
+9
View File
@@ -1,5 +1,14 @@
use clap::{App, Arg}; use clap::{App, Arg};
#[derive(Debug, thiserror::Error)]
#[allow(clippy::large_enum_variant)]
pub enum Error {
#[error("cant parse")]
CantParse,
#[error("general error")]
UnknownError,
}
pub fn cli() -> App<'static, 'static> { pub fn cli() -> App<'static, 'static> {
App::new("ban internets scanner fast 🍶") App::new("ban internets scanner fast 🍶")
.version("v0.0.1") .version("v0.0.1")