From e3211d8c8e84a93b7634d11bcbc5583c6ec60e31 Mon Sep 17 00:00:00 2001 From: Pierre Dubouilh Date: Wed, 3 Aug 2022 01:25:01 +0200 Subject: [PATCH] switch to ipset-sys it's a perf boost, but we drastically reduce portability as it will drag a libipset and a libc dependency along. --- Cargo.lock | 199 ++++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 1 + Makefile | 24 ++++--- src/jail.rs | 98 ++++++++++---------------- src/main.rs | 8 +-- 5 files changed, 240 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 040405c..d7daaa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,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" @@ -55,6 +79,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "ipset-sys", "lazy_static", "linemux", "regex", @@ -67,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" @@ -94,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", ] @@ -104,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", @@ -155,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" @@ -164,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" @@ -184,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" @@ -210,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" @@ -234,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]] @@ -265,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" @@ -294,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" @@ -322,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", ] @@ -364,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" @@ -373,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" @@ -387,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" @@ -421,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", @@ -448,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" @@ -459,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" diff --git a/Cargo.toml b/Cargo.toml index 25f875d..527a837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ lazy_static = "1.4.0" regex = "1.5.4" clap = "2.33.3" anyhow = "1.0.44" +ipset-sys = "0.1.2" \ No newline at end of file diff --git a/Makefile b/Makefile index 077aef0..4036146 100644 --- a/Makefile +++ b/Makefile @@ -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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/src/jail.rs b/src/jail.rs index 1038c09..260752a 100644 --- a/src/jail.rs +++ b/src/jail.rs @@ -4,6 +4,7 @@ use std::process::Command; use std::sync::Mutex; use anyhow::*; +use ipset_sys::IpsetSys; use crate::utils::JailStatus; @@ -11,6 +12,7 @@ pub struct Jail { jailtime: u32, allowance: u8, remand: Mutex>, + ipset: IpsetSys, } const JAIL_NAME: &str = "blockfast_jail"; @@ -18,82 +20,48 @@ const JAIL_NAME: &str = "blockfast_jail"; const ERR_MSG: &str = "error using ipset/iptables, maybe it's not installed, this program isn't running as root ?"; -fn ipset_init() -> Result<()> { - 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 - ); +impl Jail { + pub fn new(allowance: u8, jailtime: u32) -> Result { + 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 + ); - let args0: Vec<&str> = init0.split_whitespace().collect(); - let args1: Vec<&str> = init1.split_whitespace().collect(); - let args2: Vec<&str> = init2.split_whitespace().collect(); + let args1: Vec<&str> = init0.split_whitespace().collect(); + let args2: Vec<&str> = init1.split_whitespace().collect(); - // create - let out = Command::new("sudo").args(args0).output()?; - if out.status.code() != Some(0) { - let already_exists = - std::str::from_utf8(&out.stderr)?.contains("set with the same name already exists"); + // init ipset + let mut ipset = IpsetSys::init()?; + let init_cmd = format!("create {} hash:ip timeout 0", JAIL_NAME); + let _ = ipset.run(&init_cmd); - if already_exists { - return Ok(()); - } else { + // setup input + let out = Command::new("sudo").args(args1).output()?; + if out.status.code() != Some(0) { eprintln!("{:?}", out); bail!(ERR_MSG); } - } - // 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(()) -} - -fn ipset_block(jailtime: u32, ip: IpAddr) -> Result<()> { - 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()?; - if out.status.code() != Some(0) { - eprintln!("{:?}", out); - bail!("error executing ipset ban"); - } - - Ok(()) -} - -impl Jail { - pub fn new(allowance: u8, jailtime: u32) -> Result { - ipset_init()?; + // 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) -> Result { + pub fn incr(&mut self, ip: IpAddr) -> Result { let should_ban = { let mut locked_map = self.remand.lock().map_err(|_| anyhow!("cant lock"))?; @@ -109,7 +77,13 @@ impl Jail { }; if should_ban { - ipset_block(self.jailtime, 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) diff --git a/src/main.rs b/src/main.rs index 17a4800..434270b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn judge( path_clf: &str, payload: &str, path: &str, - jail: &Jail, + jail: &mut Jail, ) -> Result { let do_sshd = !path_sshd.is_empty(); let do_clf = !path_clf.is_empty(); @@ -35,7 +35,7 @@ fn judge( ParsingStatus::BadEntry(ip) => ip, }; - match jail.probe(ip)? { + match jail.incr(ip)? { JailStatus::Remand => Ok(Judgment::Remand), JailStatus::Jailed(ip) => Ok(Judgment::Bad(target, ip)), } @@ -52,7 +52,7 @@ async fn run() -> Result<()> { let allowance_str = args.value_of("allowance").unwrap_or(""); let allowance = allowance_str.parse().context("parsing allowance")?; - let jail = Jail::new(allowance, jailtime)?; + let mut jail = Jail::new(allowance, jailtime)?; eprintln!( "+ jail setup, offences allowed: {}, jailtime {}s", allowance, jailtime @@ -76,7 +76,7 @@ async fn run() -> Result<()> { let payload = line.line(); let path = line.source().display().to_string(); - match judge(path_sshd, path_clf, payload, &path, &jail) { + match judge(path_sshd, path_clf, payload, &path, &mut jail) { Err(err) => eprintln!("! ERR {:?} - file {}", err, path), Ok(Judgment::Good) => {} Ok(Judgment::Remand) => {}