mirror of
https://github.com/pldubouilh/blockfast.git
synced 2026-08-28 12:56:35 -04:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3211d8c8e | ||
|
|
89e6d2bff1 | ||
|
|
4d2aee5178 | ||
|
|
283653a775 | ||
|
|
6741f48285 |
Generated
+191
-15
@@ -20,6 +20,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -37,6 +43,30 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"cfg-if 0.1.10",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -47,7 +77,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
name = "blockfast"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ipset-sys",
|
||||
"lazy_static",
|
||||
"linemux",
|
||||
"regex",
|
||||
@@ -60,12 +92,44 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "0.29.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
@@ -87,7 +151,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
@@ -97,17 +161,30 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
@@ -148,6 +225,12 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@@ -157,6 +240,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.9.4"
|
||||
@@ -177,6 +269,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipset-sys"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99f246b363e81a3c4b1d0ba74d675555774a6dd420dd136a83fddd0aeec20b14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.4"
|
||||
@@ -203,12 +305,28 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linemux"
|
||||
version = "0.2.3"
|
||||
@@ -227,7 +345,7 @@ version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -258,6 +376,16 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "5.0.0-pre.13"
|
||||
@@ -287,14 +415,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.12"
|
||||
@@ -315,18 +449,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -357,6 +497,12 @@ version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
@@ -366,6 +512,12 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.4"
|
||||
@@ -380,15 +532,24 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.78"
|
||||
version = "1.0.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4eac2e6c19f5c3abc0c229bea31ff0b9b091c7b14990e8924b92902a303a0c0"
|
||||
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
@@ -414,9 +575,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.4.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb"
|
||||
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -441,6 +602,12 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
@@ -452,6 +619,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
+3
-1
@@ -11,4 +11,6 @@ linemux = "0.2"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.5.4"
|
||||
clap = "2.33.3"
|
||||
clap = "2.33.3"
|
||||
anyhow = "1.0.44"
|
||||
ipset-sys = "0.1.2"
|
||||
@@ -1,23 +1,29 @@
|
||||
build:
|
||||
build::
|
||||
cargo build
|
||||
cargo clippy
|
||||
cargo fmt
|
||||
|
||||
run:
|
||||
cargo run -- -s=/tmp/sshdtest -c=/tmp/clftest
|
||||
run:: build
|
||||
sudo target/debug/blockfast -s=/tmp/sshdtest -c=/tmp/clftest
|
||||
|
||||
watch:
|
||||
watch::
|
||||
ls src/*.rs | entr -rc -- make run
|
||||
|
||||
test:
|
||||
test::
|
||||
cargo test
|
||||
|
||||
watch-test:
|
||||
watch-test::
|
||||
ls src/*.rs | entr -rc -- make test
|
||||
|
||||
release:
|
||||
release::
|
||||
cargo build --target x86_64-unknown-linux-musl --release
|
||||
|
||||
ci: test
|
||||
ci:: test
|
||||
cargo fmt --all -- --check
|
||||
cargo clippy -- -D warnings
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
hit-sshd-bad::
|
||||
echo "Sep 26 06:26:14 livecompute sshd[23292]: pam_unix(sshd:auth): authentication failure; logname= u =0 tty=ssh ruser= rhost=5.101.107.191" >> /tmp/sshdtest
|
||||
|
||||
hit-clftest-bad::
|
||||
echo "8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 200 923" >> /tmp/clftest
|
||||
+45
-17
@@ -1,28 +1,31 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::utils::ParsingStatus;
|
||||
use anyhow::Result;
|
||||
use lazy_static::lazy_static;
|
||||
use std::net::IpAddr;
|
||||
|
||||
// TODO: allow user-provided list
|
||||
// TODO: match different error-levels (10 404, but only 5 401, etc...)
|
||||
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<ParsingStatus> {
|
||||
// TODO: Use a proper parser ?
|
||||
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(
|
||||
|bad_status| {
|
||||
if http_code == *bad_status {
|
||||
ip
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
let ip_str = elts[0];
|
||||
let ip = ip_str.parse::<IpAddr>()?;
|
||||
|
||||
let http_code_str = elts[elts.len() - 2] as &str;
|
||||
let http_code = http_code_str.parse::<u32>()?;
|
||||
|
||||
for status in BAD_STATUSES.iter() {
|
||||
if *status == http_code {
|
||||
return Ok(ParsingStatus::BadEntry(ip));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ParsingStatus::OkEntry)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -36,7 +39,13 @@ mod tests {
|
||||
"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).unwrap();
|
||||
match ret {
|
||||
ParsingStatus::BadEntry(_) => {}
|
||||
_ => panic!("bad parsing"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -46,6 +55,25 @@ mod tests {
|
||||
"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).unwrap();
|
||||
match ret {
|
||||
ParsingStatus::OkEntry => {}
|
||||
_ => panic!("bad parsing"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[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());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+53
-88
@@ -1,108 +1,69 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::panic::panic_any;
|
||||
use std::process::Command;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::*;
|
||||
use ipset_sys::IpsetSys;
|
||||
|
||||
use crate::utils::JailStatus;
|
||||
|
||||
pub struct Jail {
|
||||
jailtime: u32,
|
||||
allowance: u8,
|
||||
remand: Mutex<HashMap<IpAddr, u8>>,
|
||||
ipset: IpsetSys,
|
||||
}
|
||||
|
||||
const JAIL_NAME: &str = "blockfast_jail";
|
||||
const GENERAL_PANIC_MSG: &str =
|
||||
|
||||
const ERR_MSG: &str =
|
||||
"error using ipset/iptables, maybe it's not installed, this program isn't running as root ?";
|
||||
|
||||
fn ipset_init() -> Option<()> {
|
||||
let init0 = format!("ipset create {} hash:ip timeout 0", JAIL_NAME);
|
||||
let init1 = format!(
|
||||
"iptables -I INPUT 1 -m set -j DROP --match-set {} src",
|
||||
JAIL_NAME
|
||||
);
|
||||
let init2 = format!(
|
||||
"iptables -I FORWARD 1 -m set -j DROP --match-set {} src",
|
||||
JAIL_NAME
|
||||
);
|
||||
|
||||
let args0: Vec<&str> = init0.split_whitespace().collect();
|
||||
let args1: Vec<&str> = init1.split_whitespace().collect();
|
||||
let args2: Vec<&str> = init2.split_whitespace().collect();
|
||||
|
||||
// create
|
||||
let out = Command::new("sudo").args(args0).output().ok()?;
|
||||
|
||||
if out.status.code()? != 0 {
|
||||
let already_exists = std::str::from_utf8(&out.stderr)
|
||||
.ok()?
|
||||
.contains("set with the same name already exists");
|
||||
|
||||
if already_exists {
|
||||
return None;
|
||||
} else {
|
||||
eprintln!("{:?}", out);
|
||||
panic_any(GENERAL_PANIC_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
// setup input
|
||||
let out_input = Command::new("sudo").args(args1).output().ok()?;
|
||||
|
||||
if out_input.status.code()? != 0 {
|
||||
eprintln!("{:?}", out_input);
|
||||
panic_any(GENERAL_PANIC_MSG);
|
||||
}
|
||||
|
||||
// setup fwd
|
||||
let out_fwd = Command::new("sudo").args(args2).output().ok()?;
|
||||
|
||||
if out_fwd.status.code()? != 0 {
|
||||
eprintln!("{:?}", out_fwd);
|
||||
panic_any(GENERAL_PANIC_MSG);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn ipset_block(jailtime: u32, ip: IpAddr) -> Option<()> {
|
||||
let sentence = format!(
|
||||
"ipset add {} {} timeout {}",
|
||||
JAIL_NAME,
|
||||
ip.to_string(),
|
||||
jailtime
|
||||
);
|
||||
let sentence_sl: Vec<&str> = sentence.split_whitespace().collect();
|
||||
|
||||
let out = Command::new("sudo").args(sentence_sl).output().ok()?;
|
||||
|
||||
if out.status.code()? != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
impl Jail {
|
||||
pub fn new(allowance: u8, jailtime: u32) -> Jail {
|
||||
if ipset_init().is_some() {
|
||||
panic_any(GENERAL_PANIC_MSG);
|
||||
};
|
||||
|
||||
eprintln!(
|
||||
"+ jail setup, allowing {} offences, jailtime: {}s",
|
||||
allowance, jailtime
|
||||
pub fn new(allowance: u8, jailtime: u32) -> Result<Jail> {
|
||||
let init0 = format!(
|
||||
"iptables -I INPUT 1 -m set -j DROP --match-set {} src",
|
||||
JAIL_NAME
|
||||
);
|
||||
let init1 = format!(
|
||||
"iptables -I FORWARD 1 -m set -j DROP --match-set {} src",
|
||||
JAIL_NAME
|
||||
);
|
||||
|
||||
Jail {
|
||||
let args1: Vec<&str> = init0.split_whitespace().collect();
|
||||
let args2: Vec<&str> = init1.split_whitespace().collect();
|
||||
|
||||
// init ipset
|
||||
let mut ipset = IpsetSys::init()?;
|
||||
let init_cmd = format!("create {} hash:ip timeout 0", JAIL_NAME);
|
||||
let _ = ipset.run(&init_cmd);
|
||||
|
||||
// setup input
|
||||
let out = Command::new("sudo").args(args1).output()?;
|
||||
if out.status.code() != Some(0) {
|
||||
eprintln!("{:?}", out);
|
||||
bail!(ERR_MSG);
|
||||
}
|
||||
|
||||
// setup fwd
|
||||
let out = Command::new("sudo").args(args2).output()?;
|
||||
if out.status.code() != Some(0) {
|
||||
eprintln!("{:?}", out);
|
||||
bail!(ERR_MSG);
|
||||
}
|
||||
|
||||
Ok(Jail {
|
||||
allowance,
|
||||
jailtime,
|
||||
ipset,
|
||||
remand: Mutex::new(HashMap::new()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn probe(&self, ip: IpAddr) -> Option<()> {
|
||||
pub fn incr(&mut self, ip: IpAddr) -> Result<JailStatus> {
|
||||
let should_ban = {
|
||||
let mut locked_map = self.remand.lock().ok()?;
|
||||
let mut locked_map = self.remand.lock().map_err(|_| anyhow!("cant lock"))?;
|
||||
|
||||
// TODO: set time of last offence, and add grace
|
||||
let hits = *locked_map.entry(ip).and_modify(|e| *e += 1).or_insert(1);
|
||||
@@ -116,12 +77,16 @@ impl Jail {
|
||||
};
|
||||
|
||||
if should_ban {
|
||||
match ipset_block(self.jailtime, ip) {
|
||||
Some(_) => eprintln!("~ {} going to jail", ip),
|
||||
None => eprintln!("! ERR {} going to jail", ip),
|
||||
}
|
||||
let sentence = format!(
|
||||
"add {} {} timeout {}",
|
||||
JAIL_NAME,
|
||||
ip.to_string(),
|
||||
self.jailtime
|
||||
);
|
||||
self.ipset.run(&sentence)?;
|
||||
Ok(JailStatus::Jailed(ip))
|
||||
} else {
|
||||
Ok(JailStatus::Remand)
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
+63
-21
@@ -1,3 +1,4 @@
|
||||
use anyhow::*;
|
||||
use linemux::MuxedLines;
|
||||
|
||||
mod clf;
|
||||
@@ -6,29 +7,68 @@ mod utils;
|
||||
|
||||
mod jail;
|
||||
use crate::jail::Jail;
|
||||
use crate::utils::*;
|
||||
|
||||
async fn run() -> Option<()> {
|
||||
fn judge(
|
||||
path_sshd: &str,
|
||||
path_clf: &str,
|
||||
payload: &str,
|
||||
path: &str,
|
||||
jail: &mut Jail,
|
||||
) -> Result<Judgment> {
|
||||
let do_sshd = !path_sshd.is_empty();
|
||||
let do_clf = !path_clf.is_empty();
|
||||
let mut target = "";
|
||||
|
||||
let ret_parse = if do_sshd && path.ends_with(path_sshd) {
|
||||
target = "sshd";
|
||||
sshd::parse(payload)
|
||||
} else if do_clf && path.ends_with(path_clf) {
|
||||
target = "clf ";
|
||||
clf::parse(payload)
|
||||
} else {
|
||||
Err(anyhow!("cant locate file !"))
|
||||
};
|
||||
|
||||
let ip = match ret_parse? {
|
||||
ParsingStatus::OkEntry => return Ok(Judgment::Good),
|
||||
ParsingStatus::BadEntry(ip) => ip,
|
||||
};
|
||||
|
||||
match jail.incr(ip)? {
|
||||
JailStatus::Remand => Ok(Judgment::Remand),
|
||||
JailStatus::Jailed(ip) => Ok(Judgment::Bad(target, ip)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn run() -> Result<()> {
|
||||
let args = utils::cli().get_matches();
|
||||
let mut lines = MuxedLines::new().ok()?;
|
||||
let mut lines = MuxedLines::new()?;
|
||||
|
||||
// jail
|
||||
let jailtime: u32 = args.value_of("jailtime")?.parse().ok()?;
|
||||
let allowance: u8 = args.value_of("allowance")?.parse().ok()?;
|
||||
let jail = Jail::new(allowance, jailtime);
|
||||
let jailtime_str = args.value_of("jailtime").unwrap_or("");
|
||||
let jailtime = jailtime_str.parse().context("parsing jailtime")?;
|
||||
|
||||
let allowance_str = args.value_of("allowance").unwrap_or("");
|
||||
let allowance = allowance_str.parse().context("parsing allowance")?;
|
||||
|
||||
let mut jail = Jail::new(allowance, jailtime)?;
|
||||
eprintln!(
|
||||
"+ jail setup, offences allowed: {}, jailtime {}s",
|
||||
allowance, jailtime
|
||||
);
|
||||
|
||||
// sshd
|
||||
let path_sshd = args.value_of("sshd_logpath").unwrap_or("");
|
||||
let do_sshd = !path_sshd.is_empty();
|
||||
if do_sshd {
|
||||
lines.add_file(path_sshd).await.ok()?;
|
||||
if !path_sshd.is_empty() {
|
||||
lines.add_file(path_sshd).await?;
|
||||
eprintln!("+ starting with sshd parsing at {}", path_sshd);
|
||||
}
|
||||
|
||||
// common log format
|
||||
let path_clf = args.value_of("clf_logpath").unwrap_or("");
|
||||
let do_clf = !path_clf.is_empty();
|
||||
if do_clf {
|
||||
lines.add_file(path_clf).await.ok()?;
|
||||
if !path_clf.is_empty() {
|
||||
lines.add_file(path_clf).await?;
|
||||
eprintln!("+ starting with clf parsing at {}", path_clf);
|
||||
}
|
||||
|
||||
@@ -36,22 +76,24 @@ async fn run() -> Option<()> {
|
||||
let payload = line.line();
|
||||
let path = line.source().display().to_string();
|
||||
|
||||
if do_sshd && path.ends_with(path_sshd) {
|
||||
sshd::parse(payload).and_then(|ip| jail.probe(ip));
|
||||
} else if do_clf && path.ends_with(path_clf) {
|
||||
clf::parse(payload).and_then(|ip| jail.probe(ip));
|
||||
} else {
|
||||
eprintln!("! unknown logline: {}", path);
|
||||
}
|
||||
match judge(path_sshd, path_clf, payload, &path, &mut jail) {
|
||||
Err(err) => eprintln!("! ERR {:?} - file {}", err, path),
|
||||
Ok(Judgment::Good) => {}
|
||||
Ok(Judgment::Remand) => {}
|
||||
Ok(Judgment::Bad(target, ip)) => {
|
||||
eprintln!("~ too many infraction, {} jailtime for: {}", target, ip)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Some(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let _ = run().await;
|
||||
eprintln!("! ERR");
|
||||
let ret = run().await;
|
||||
let _ = ret.map_err(|e| eprintln!("! ERROR {:?}", e));
|
||||
eprintln!("\n");
|
||||
let _ = utils::cli().print_help();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+49
-10
@@ -1,7 +1,10 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use anyhow::*;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::utils::ParsingStatus;
|
||||
|
||||
struct Rule {
|
||||
matcher: String,
|
||||
@@ -15,7 +18,7 @@ lazy_static! {
|
||||
extractor: Regex::new(r"(from.)(.*)(.port)").unwrap(),
|
||||
},
|
||||
Rule {
|
||||
matcher: "Invalid user".to_string(),
|
||||
matcher: "Invalid user ".to_string(),
|
||||
extractor: Regex::new(r"(from.)(.*)").unwrap(),
|
||||
},
|
||||
Rule {
|
||||
@@ -25,16 +28,27 @@ lazy_static! {
|
||||
];
|
||||
}
|
||||
|
||||
pub fn parse(line: &str) -> Option<IpAddr> {
|
||||
let ret = SSHD_BAD.iter().find_map(|rule| {
|
||||
pub fn parse(line: &str) -> Result<ParsingStatus> {
|
||||
let hits = SSHD_BAD.iter().find_map(|rule| {
|
||||
if line.contains(&rule.matcher) {
|
||||
rule.extractor.captures(line)?.get(2)
|
||||
rule.extractor.captures(line)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
});
|
||||
|
||||
ret.as_str().parse::<IpAddr>().ok()
|
||||
if hits.is_none() {
|
||||
return Ok(ParsingStatus::OkEntry);
|
||||
}
|
||||
|
||||
let ip = hits
|
||||
.and_then(|c| c.get(2))
|
||||
.and_then(|m| IpAddr::from_str(m.as_str()).ok());
|
||||
|
||||
match ip {
|
||||
Some(ip) => Ok(ParsingStatus::BadEntry(ip)),
|
||||
None => Err(anyhow!("cant parse sshd entry")),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -49,7 +63,13 @@ mod tests {
|
||||
"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).unwrap();
|
||||
match ret {
|
||||
ParsingStatus::BadEntry(_) => {}
|
||||
_ => panic!("bad parsing"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -60,6 +80,25 @@ mod tests {
|
||||
"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).unwrap();
|
||||
match ret {
|
||||
ParsingStatus::OkEntry => {}
|
||||
_ => panic!("bad parsing"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[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());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
use clap::{App, Arg};
|
||||
use std::net::IpAddr;
|
||||
|
||||
pub enum ParsingStatus {
|
||||
OkEntry,
|
||||
BadEntry(IpAddr),
|
||||
}
|
||||
pub enum Judgment {
|
||||
Good,
|
||||
Remand,
|
||||
Bad(&'static str, IpAddr),
|
||||
}
|
||||
|
||||
pub enum JailStatus {
|
||||
Remand,
|
||||
Jailed(IpAddr),
|
||||
}
|
||||
|
||||
pub fn cli() -> App<'static, 'static> {
|
||||
App::new("ban internets scanner fast 🍶")
|
||||
|
||||
Reference in New Issue
Block a user