From 576c28d81a102e38c664b21ebc59b0cd7cfacde6 Mon Sep 17 00:00:00 2001 From: Paul Lecuq Date: Thu, 28 Nov 2024 20:08:10 +0100 Subject: [PATCH 01/20] replace nftnl by rustables --- Cargo.lock | 293 ++++++++++++++++++++++++++++++++++++++++++--------- Cargo.toml | 4 +- src/fw.rs | 98 +++++------------ src/ipblc.rs | 38 +++---- 4 files changed, 285 insertions(+), 148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 663457c..a23b0af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,35 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -159,6 +188,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -186,6 +224,17 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.21" @@ -281,6 +330,22 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -378,12 +443,27 @@ dependencies = [ "syn", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "1.1.0" @@ -651,11 +731,11 @@ dependencies = [ "git-version", "ipnet", "lazy_static", - "mnl", - "nftnl", - "nix", + "nix 0.29.0", "regex", "reqwest", + "rustables", + "rustables-macros", "sd-notify", "serde", "serde_json", @@ -669,6 +749,12 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -696,12 +782,34 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[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.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "litemap" version = "0.7.4" @@ -730,12 +838,27 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -758,46 +881,16 @@ dependencies = [ ] [[package]] -name = "mnl" -version = "0.2.2" +name = "nix" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a5469630da93e1813bb257964c0ccee3b26b6879dd858039ddec35cc8681ed" -dependencies = [ - "libc", - "log", - "mnl-sys", -] - -[[package]] -name = "mnl-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9750685b201e1ecfaaf7aa5d0387829170fa565989cc481b49080aa155f70457" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "nftnl" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a7491dd91b71643f65546389f25506da70723d1f1ec8c8d6d20444d1c23f27" -dependencies = [ - "bitflags", - "log", - "nftnl-sys", -] - -[[package]] -name = "nftnl-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193f2c2a70e6421534c3f3b75eaaed4e4b9df45281b3d94f5bc8c32fb346cbb" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ + "bitflags 1.3.2", + "cc", "cfg-if", "libc", - "pkg-config", + "memoffset", ] [[package]] @@ -806,12 +899,22 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -865,6 +968,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -883,12 +992,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - [[package]] name = "ppv-lite86" version = "0.2.20" @@ -898,6 +1001,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -907,6 +1020,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "quinn" version = "0.11.6" @@ -917,7 +1043,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.0.0", "rustls", "socket2", "thiserror 2.0.3", @@ -935,7 +1061,7 @@ dependencies = [ "getrandom", "rand", "ring", - "rustc-hash", + "rustc-hash 2.0.0", "rustls", "rustls-pki-types", "slab", @@ -1004,7 +1130,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -1093,18 +1219,67 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustables" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177fda7beae62153f2ee2d0d3f2c7cf37da4b31a7240483fa1e06f7ccc25fb1c" +dependencies = [ + "bindgen", + "bitflags 1.3.2", + "ipnetwork", + "libc", + "log", + "nix 0.23.2", + "regex", + "rustables-macros", + "thiserror 1.0.69", +] + +[[package]] +name = "rustables-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "698b79177cbf57522a1dcc118ac31393dc2a7ccc9bdb3625a6601ff5c27a3085" +dependencies = [ + "once_cell", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.23.18" @@ -1194,7 +1369,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -1688,6 +1863,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -1821,6 +2008,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yoke" version = "0.7.5" diff --git a/Cargo.toml b/Cargo.toml index 6a8e7e3..59c4b98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,11 @@ clap = { version = "4.5", features = ["string"] } git-version = "0.3" ipnet = "2.10" lazy_static = "1.5" -mnl = "0.2" -nftnl = "0.7" nix = { version = "0.29", features = ["hostname", "inotify"] } regex = "1.11" reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +rustables = "0.8.5" +rustables-macros = "0.1.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sd-notify = { version = "0.4" } diff --git a/src/fw.rs b/src/fw.rs index 81d5302..d5aa021 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,13 +1,12 @@ -use crate::ip::IpData; -use crate::ipblc::PKG_NAME; +use crate::{ip::IpData, ipblc::PKG_NAME}; -use nftnl::{nft_expr, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table}; use std::{ - ffi::CString, io::Error, net::{Ipv4Addr, Ipv6Addr}, }; +use rustables::*; + pub enum FwTableType { IPv4, IPv6, @@ -21,37 +20,22 @@ pub fn fwglobalinit<'a>() -> ((Batch, Table), (Batch, Table)) { macro_rules! initrules { ($batch:expr, $table:expr, $chain:ident) => { - $chain.set_hook(nftnl::Hook::In, 1); - $chain.set_policy(nftnl::Policy::Accept); + $chain.set_hook(Hook::new(HookClass::In, 1)); - $batch.add(&$chain, nftnl::MsgType::Add); - $batch.add(&Rule::new(&$chain), nftnl::MsgType::Del); - - let mut rule = Rule::new(&$chain); - rule.add_expr(&nft_expr!(ct state)); - rule.add_expr(&nft_expr!(bitwise mask 4u32, xor 0u32)); - rule.add_expr(&nft_expr!(cmp != 0u32)); - rule.add_expr(&nft_expr!(counter)); - rule.add_expr(&nft_expr!(verdict accept)); - $batch.add(&rule, nftnl::MsgType::Add); + $batch.add(&$chain, MsgType::Add); + $batch.add(&Rule::new(&$chain).unwrap(), MsgType::Del); }; } macro_rules! createrules { ($ipdata:ident, $chain:ident, $batch:ident, $t:ty, $ip_t:ident) => { - let mut rule = Rule::new(&$chain); let ip = $ipdata.ip.parse::<$t>().unwrap(); - - rule.add_expr(&nft_expr!(payload $ip_t saddr)); - rule.add_expr(&nft_expr!(cmp == ip)); - rule.add_expr(&nft_expr!(ct state)); - rule.add_expr(&nft_expr!(bitwise mask 10u32, xor 0u32)); - rule.add_expr(&nft_expr!(cmp != 0u32)); - rule.add_expr(&nft_expr!(counter)); - rule.add_expr(&nft_expr!(verdict drop)); - - $batch.add(&rule, nftnl::MsgType::Add); - } + Rule::new(&$chain) + .unwrap() + .saddr(ip.into()) + .drop() + .add_to_batch(&mut $batch); + }; } fn fwinit(t: FwTableType) -> (Batch, Table) { @@ -60,38 +44,32 @@ fn fwinit(t: FwTableType) -> (Batch, Table) { match t { FwTableType::IPv4 => { table_name = format!("{PKG_NAME}4"); - table = Table::new( - &CString::new(format!("{table_name}")).unwrap(), - ProtoFamily::Ipv4, - ); + table = Table::new(ProtocolFamily::Ipv4).with_name(table_name) } FwTableType::IPv6 => { table_name = format!("{PKG_NAME}6"); - table = Table::new( - &CString::new(format!("{table_name}")).unwrap(), - ProtoFamily::Ipv6, - ); + table = Table::new(ProtocolFamily::Ipv6).with_name(table_name); } } let mut batch = Batch::new(); - batch.add(&table, nftnl::MsgType::Add); - batch.add(&table, nftnl::MsgType::Del); - - batch.add(&table, nftnl::MsgType::Add); + batch.add(&table, MsgType::Add); (batch, table) } -pub fn fwblock( +pub fn fwblock<'a>( ips_add_all: &Vec, ret: &mut Vec, fwlen: &mut usize, ) -> std::result::Result<(), Error> { let ((mut batch4, table4), (mut batch6, table6)) = fwglobalinit(); - let mut chain4 = Chain::new(&CString::new(PKG_NAME).unwrap(), &table4); - let mut chain6 = Chain::new(&CString::new(PKG_NAME).unwrap(), &table6); - + let mut chain4 = Chain::new(&table4) + .with_policy(ChainPolicy::Accept) + .with_name(PKG_NAME); + let mut chain6 = Chain::new(&table6) + .with_policy(ChainPolicy::Accept) + .with_name(PKG_NAME); initrules!(batch4, table4, chain4); initrules!(batch6, table6, chain6); @@ -124,14 +102,14 @@ pub fn fwblock( // validate and send batch for b in [batch4, batch6] { - let bf = b.finalize(); - match send_and_process(&bf) { + match b.send() { Ok(_) => {} Err(e) => { println!("error sending batch: {e}"); } }; } + if fwlen != &mut ips_add_all.len() { ret.push(format!( "{length} ip in firewall", @@ -141,31 +119,3 @@ pub fn fwblock( *fwlen = ips_add_all.len(); Ok(()) } - -fn send_and_process(batch: &FinalizedBatch) -> std::result::Result<(), Error> { - let seq: u32 = 2; - let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; - socket.send_all(batch)?; - let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize]; - while let Some(message) = socket_recv(&socket, &mut buffer[..])? { - match mnl::cb_run(message, seq, socket.portid())? { - mnl::CbResult::Stop => { - break; - } - mnl::CbResult::Ok => (), - } - } - Ok(()) -} - -fn socket_recv<'a>( - socket: &mnl::Socket, - buf: &'a mut [u8], -) -> std::result::Result, Error> { - let ret = socket.recv(buf)?; - if ret > 0 { - Ok(Some(&buf[..ret])) - } else { - Ok(None) - } -} diff --git a/src/ipblc.rs b/src/ipblc.rs index ceef99b..e11b880 100644 --- a/src/ipblc.rs +++ b/src/ipblc.rs @@ -1,5 +1,5 @@ use crate::config::{Context, GIT_VERSION}; -use crate::fw::{fwblock, fwglobalinit}; +use crate::fw::fwblock; use crate::ip::{filter, IpData, IpEvent}; use crate::ipevent; use crate::monitoring::apiserver; @@ -40,7 +40,6 @@ pub async fn run() { let mut last_cfg_reload: DateTime = Local::now().trunc_subsecs(0); log_with_systemd!(format!("Launching {}, version {}", PKG_NAME, pkgversion)); - fwglobalinit(); let ctxapi = Arc::clone(&ctxarc); apiserver(&ctxapi).await.unwrap(); @@ -125,7 +124,21 @@ pub async fn run() { }; let ctxclone = Arc::clone(&ctxarc); - handle_fwblock(ctxclone, &mut ret, &mut fwlen).await; + let tounblock = { + let mut ctx = ctxclone.write().await; + ctx.gc_blocklist().await + }; + let toblock = { + let ctx = ctxclone.read().await; + ctx.get_blocklist_toblock().await + }; + // apply firewall blocking + match fwblock(&toblock, &mut ret, &mut fwlen) { + Ok(_) => {} + Err(e) => { + println!("err: {e}, unable to push firewall rules, use super user") + } + }; // log lines if ret.len() > 0 { @@ -176,25 +189,6 @@ async fn handle_cfg_reload( }; } -async fn handle_fwblock(ctxclone: Arc>, ret: &mut Vec, fwlen: &mut usize) { - { - let mut ctx = ctxclone.write().await; - ctx.gc_blocklist().await; - } - let toblock = { - let ctx = ctxclone.read().await; - ctx.get_blocklist_toblock().await - }; - - // apply firewall blocking - match fwblock(&toblock, ret, fwlen) { - Ok(_) => {} - Err(e) => { - println!("err: {e}, unable to push firewall rules, use super user") - } - }; -} - async fn watchfiles(inoarc: Arc>) -> Receiver { let (bltx, blrx): (Sender, Receiver) = channel(BL_CHAN_SIZE); tokio::spawn(async move { -- 2.39.5 From c38834f18c4e868a35b301599eb522ed1c4949b4 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Dec 2024 19:49:54 +0100 Subject: [PATCH 02/20] chore: wip code --- src/config.rs | 11 ++-- src/fw.rs | 153 +++++++++++++++++++++++++++++++------------------- src/ip.rs | 1 + src/ipblc.rs | 23 +++++++- 4 files changed, 125 insertions(+), 63 deletions(-) diff --git a/src/config.rs b/src/config.rs index 75da5ae..1313e68 100644 --- a/src/config.rs +++ b/src/config.rs @@ -155,11 +155,11 @@ impl Context { pub async fn get_blocklist_toblock(&self) -> Vec { let mut res: Vec = vec![]; - for (_, block) in self.blocklist.iter() { - match self.cfg.sets.get(&block.ipdata.src) { + for (_, ipblock) in self.blocklist.iter() { + match self.cfg.sets.get(&ipblock.ipdata.src) { Some(set) => { - if block.tryfail >= set.tryfail { - res.push(block.ipdata.clone()); + if ipblock.tryfail >= set.tryfail && !ipblock.blocked { + res.push(ipblock.ipdata.clone()); } } None => {} @@ -176,6 +176,7 @@ impl Context { .unwrap() .with_timezone(&chrono::Local); let blocktime = set.blocktime; + let blocked = false; if ipevent.mode == "file".to_string() && gethostname(true) == ipevent.hostname { let block = self.blocklist @@ -185,6 +186,7 @@ impl Context { tryfail: 0, starttime, blocktime, + blocked, }); block.tryfail += 1; block.blocktime = blocktime; @@ -199,6 +201,7 @@ impl Context { tryfail: set.tryfail, starttime, blocktime, + blocked, }); } } diff --git a/src/fw.rs b/src/fw.rs index d5aa021..92c8bdf 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -12,23 +12,24 @@ pub enum FwTableType { IPv6, } -pub fn fwglobalinit<'a>() -> ((Batch, Table), (Batch, Table)) { - let (batch4, table4) = fwinit(FwTableType::IPv4); - let (batch6, table6) = fwinit(FwTableType::IPv6); - ((batch4, table4), (batch6, table6)) +pub enum FwAction { + Add, + Delete, } macro_rules! initrules { - ($batch:expr, $table:expr, $chain:ident) => { + ($batch:expr, $table:expr, $chain:ident, $reset:expr) => { $chain.set_hook(Hook::new(HookClass::In, 1)); $batch.add(&$chain, MsgType::Add); - $batch.add(&Rule::new(&$chain).unwrap(), MsgType::Del); + if $reset { + $batch.add(&Rule::new(&$chain).unwrap(), MsgType::Del); + } }; } -macro_rules! createrules { - ($ipdata:ident, $chain:ident, $batch:ident, $t:ty, $ip_t:ident) => { +macro_rules! makerules { + ($ipdata:ident, $chain:ident, $batch:ident, $t:ty, $ip_t:ident,$action:ty) => { let ip = $ipdata.ip.parse::<$t>().unwrap(); Rule::new(&$chain) .unwrap() @@ -38,66 +39,46 @@ macro_rules! createrules { }; } -fn fwinit(t: FwTableType) -> (Batch, Table) { +pub fn fwglobalinit(t: FwTableType, reset: bool) -> (Batch, Chain) { let table_name: String; let table: Table; + let mut chain: Chain; match t { FwTableType::IPv4 => { table_name = format!("{PKG_NAME}4"); - table = Table::new(ProtocolFamily::Ipv4).with_name(table_name) + table = Table::new(ProtocolFamily::Ipv4).with_name(table_name); + chain = Chain::new(&table) + .with_policy(ChainPolicy::Accept) + .with_name(PKG_NAME); } FwTableType::IPv6 => { table_name = format!("{PKG_NAME}6"); table = Table::new(ProtocolFamily::Ipv6).with_name(table_name); + chain = Chain::new(&table) + .with_policy(ChainPolicy::Accept) + .with_name(PKG_NAME); } } let mut batch = Batch::new(); batch.add(&table, MsgType::Add); - (batch, table) + initrules!(batch, table, chain, reset); + + (batch, chain) } -pub fn fwblock<'a>( - ips_add_all: &Vec, - ret: &mut Vec, - fwlen: &mut usize, -) -> std::result::Result<(), Error> { - let ((mut batch4, table4), (mut batch6, table6)) = fwglobalinit(); +pub fn fwblock<'a>(ip_add: &IpData) -> std::result::Result<(), error::QueryError> { + let (mut batch4, chain4) = fwglobalinit(FwTableType::IPv4, false); + let (mut batch6, chain6) = fwglobalinit(FwTableType::IPv6, false); - let mut chain4 = Chain::new(&table4) - .with_policy(ChainPolicy::Accept) - .with_name(PKG_NAME); - let mut chain6 = Chain::new(&table6) - .with_policy(ChainPolicy::Accept) - .with_name(PKG_NAME); - initrules!(batch4, table4, chain4); - initrules!(batch6, table6, chain6); - - let mut factor = 1; - if ips_add_all.len() > 100 { - factor = (ips_add_all.len() / 10) as usize - } - - let ips_add_tmp: Vec = ips_add_all.clone().iter().map(|x| x.clone()).collect(); - let mut ips_add_iter = ips_add_tmp.chunks(factor); - let mut ips_add: Vec<&[IpData]> = vec![]; - while let Some(x) = ips_add_iter.next() { - ips_add.push(x); - } - - // build and add rules - for ipdata_group in ips_add.clone() { - for ipdata in ipdata_group { - match ipdata.t { - 4 => { - createrules!(ipdata, chain4, batch4, Ipv4Addr, ipv4); - } - 6 => { - createrules!(ipdata, chain6, batch6, Ipv6Addr, ipv6); - } - _ => {} - } + match ip_add.t { + 4 => { + makerules!(ip_add, chain4, batch4, Ipv4Addr, ipv4, FwAction::Add); } + 6 => { + makerules!(ip_add, chain6, batch6, Ipv6Addr, ipv6, FwAction::Add); + } + _ => {} } // validate and send batch @@ -106,16 +87,74 @@ pub fn fwblock<'a>( Ok(_) => {} Err(e) => { println!("error sending batch: {e}"); + return Err(e); } }; } - if fwlen != &mut ips_add_all.len() { - ret.push(format!( - "{length} ip in firewall", - length = ips_add_all.len() - )); - } - *fwlen = ips_add_all.len(); Ok(()) } + +pub fn fwunblock<'a>(ips_del: &Vec) -> std::result::Result<(), Error> { + let (mut batch4, chain4) = fwglobalinit(FwTableType::IPv4, false); + let (mut batch6, chain6) = fwglobalinit(FwTableType::IPv6, false); + + // to implement + /*for ip_del in ips_del { + match ip_del.t { + 4 => { + makerules!(ip_del, chain4, batch4, Ipv4Addr, ipv4, FwAction::Del); + } + 6 => { + makerules!(ip_del, chain6, batch6, Ipv6Addr, ipv6, FwAction::Del); + } + _ => {} + } + }*/ + Ok(()) +} + +pub fn get_current_rules(table_name: &str, chain_name: &str) -> Result<(), Error> { + let get_table = || -> Result, Error> { + let tables = list_tables().unwrap(); + for table in tables { + if let Some(name) = table.get_name() { + println!("Found table {}", name); + + if *name == table_name { + return Ok(Some(table)); + } + } + } + + Ok(None) + }; + + let get_chain = |table: &Table| -> Result, Error> { + let chains = list_chains_for_table(table).unwrap(); + for chain in chains { + if let Some(name) = chain.get_name() { + println!("Found chain {}", name); + + if *name == chain_name { + return Ok(Some(chain)); + } + } + } + + Ok(None) + }; + + let table = get_table()?.expect("no table?"); + let chain = get_chain(&table)?.expect("no chain?"); + + let rules = list_rules_for_chain(&chain).unwrap(); + for mut rule in rules { + println!("{:?}", rule); + } + Ok(()) +} + +pub fn fw_rules_count() -> i64 { + 0 +} diff --git a/src/ip.rs b/src/ip.rs index 510fbe7..93a705b 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -51,6 +51,7 @@ pub struct BlockIpData { pub tryfail: i64, pub blocktime: i64, pub starttime: DateTime, + pub blocked: bool, } #[derive(Clone, Debug, Serialize, Deserialize, Eq)] diff --git a/src/ipblc.rs b/src/ipblc.rs index e11b880..9f5d004 100644 --- a/src/ipblc.rs +++ b/src/ipblc.rs @@ -1,5 +1,5 @@ use crate::config::{Context, GIT_VERSION}; -use crate::fw::fwblock; +use crate::fw::*; use crate::ip::{filter, IpData, IpEvent}; use crate::ipevent; use crate::monitoring::apiserver; @@ -33,6 +33,10 @@ pub async fn run() { let inotify = Inotify::init(InitFlags::empty()).unwrap(); let globalctx = Context::new(&inotify).await; let ctxarc = Arc::new(RwLock::new(globalctx)); + let (batch4, _) = fwglobalinit(FwTableType::IPv4, true); + let (batch6, _) = fwglobalinit(FwTableType::IPv6, true); + batch4.send().unwrap(); + batch6.send().unwrap(); let mut fwlen: usize = 0; @@ -132,8 +136,23 @@ pub async fn run() { let ctx = ctxclone.read().await; ctx.get_blocklist_toblock().await }; + // apply firewall blocking - match fwblock(&toblock, &mut ret, &mut fwlen) { + for b in toblock { + match fwblock(&b) { + Ok(_) => { + let mut ctx = ctxclone.write().await; + if let Some(x) = ctx.blocklist.get_mut(&b.ip) { + x.blocked = true; + } + } + Err(e) => { + println!("err: {e}, unable to push firewall rules, use super user") + } + }; + } + get_current_rules("ipblc4", "ipblc").unwrap(); + match fwunblock(&tounblock) { Ok(_) => {} Err(e) => { println!("err: {e}, unable to push firewall rules, use super user") -- 2.39.5 From cbb2f27f5c4639dd5eeb5439ed53c4fe7129842a Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Dec 2024 19:50:32 +0100 Subject: [PATCH 03/20] chore: add test code in examples --- examples/fw.rs | 65 ++++++++++++++++++++++ examples/fw2.rs | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 examples/fw.rs create mode 100644 examples/fw2.rs diff --git a/examples/fw.rs b/examples/fw.rs new file mode 100644 index 0000000..c861b70 --- /dev/null +++ b/examples/fw.rs @@ -0,0 +1,65 @@ +use nftnl::{nft_expr, set::Set, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table}; +use std::{ffi::CString, io::*, net::Ipv4Addr}; + +fn main() -> std::result::Result<(), Error> { + let table_name = format!("ipblc4"); + let table = Table::new( + &CString::new(format!("{table_name}")).unwrap(), + ProtoFamily::Ipv4, + ); + + let mut batch = Batch::new(); + + batch.add(&table, nftnl::MsgType::Add); + batch.add(&table, nftnl::MsgType::Del); + + batch.add(&table, nftnl::MsgType::Add); + + let mut chain = Chain::new(&CString::new("test").unwrap(), &table); + chain.set_hook(nftnl::Hook::In, 1); + chain.set_policy(nftnl::Policy::Accept); + + batch.add(&chain, nftnl::MsgType::Add); + batch.add(&Rule::new(&chain), nftnl::MsgType::Del); + + let mut rule = Rule::new(&chain); + + rule.add_expr(&nft_expr!(ct state)); + rule.add_expr(&nft_expr!(bitwise mask 4u32, xor 0u32)); + rule.add_expr(&nft_expr!(cmp != 0u32)); + rule.add_expr(&nft_expr!(counter)); + rule.add_expr(&nft_expr!(verdict accept)); + + batch.add(&rule, nftnl::MsgType::Add); + let finalized_batch = batch.finalize(); + send_and_process(&finalized_batch)?; + Ok(()) +} + +fn send_and_process(batch: &FinalizedBatch) -> std::result::Result<(), Error> { + let seq: u32 = 2; + let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; + socket.send_all(batch)?; + let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize]; + while let Some(message) = socket_recv(&socket, &mut buffer[..])? { + match mnl::cb_run(message, seq, socket.portid())? { + mnl::CbResult::Stop => { + break; + } + mnl::CbResult::Ok => (), + } + } + Ok(()) +} + +fn socket_recv<'a>( + socket: &mnl::Socket, + buf: &'a mut [u8], +) -> std::result::Result, Error> { + let ret = socket.recv(buf)?; + if ret > 0 { + Ok(Some(&buf[..ret])) + } else { + Ok(None) + } +} diff --git a/examples/fw2.rs b/examples/fw2.rs new file mode 100644 index 0000000..66c4c3b --- /dev/null +++ b/examples/fw2.rs @@ -0,0 +1,143 @@ +use rustables::*; +use rustables::{expr::*, set::*, Batch, Chain, MsgType, ProtocolFamily, Rule, Table}; +use std::{ffi::*, io::*, net::*}; + +const TABLE_NAME: &str = "ipblc4"; +const CHAIN_NAME: &str = "ipblc"; + +fn main() -> Result<()> { + /*let name = "blabla"; + let mut batch = Batch::new(); + let table = Table::new(ProtocolFamily::Ipv4).with_name(name); + batch.add(&table, MsgType::Add); + let mut chain = Chain::new(&table).with_name(name); + batch.add(&chain, MsgType::Add); + + let toadd1: Ipv4Addr = "9.9.9.8".parse().unwrap(); + let toadd2: Ipv4Addr = "9.9.9.1".parse().unwrap(); + + let mut setbuilder: SetBuilder = SetBuilder::new("s1", &table).unwrap(); + setbuilder.add(&toadd1); + setbuilder.add(&toadd2); + let (mut set, setelem) = setbuilder.finish(); + batch.add(&setelem, MsgType::Add); + //batch.add(&set, MsgType::Add); + set.family = ProtocolFamily::Ipv4; + set.id = Some(5); + set.flags = Some(0); + set.userdata = Some("test".into()); + println!("{:?}", setelem);*/ + + let get_table = || -> Result> { + let tables = list_tables().unwrap(); + for table in tables { + if let Some(name) = table.get_name() { + println!("Found table {}", name); + + if name == TABLE_NAME { + return Ok(Some(table)); + } + } + } + + Ok(None) + }; + + let get_chain = |table: &Table| -> Result> { + let chains = list_chains_for_table(table).unwrap(); + for chain in chains { + if let Some(name) = chain.get_name() { + println!("Found chain {}", name); + + if name == CHAIN_NAME { + return Ok(Some(chain)); + } + } + } + + Ok(None) + }; + + let table = get_table().unwrap().expect("no table?"); + let chain = get_chain(&table).unwrap().expect("no chain?"); + + let ip: IpAddr = "184.73.167.217".parse().unwrap(); + + let cmprule = Rule::new(&chain).unwrap().saddr(ip).drop(); + println!("{:?}", cmprule); + + let mut gexpr = RawExpression::default(); + for e in cmprule.get_expressions().unwrap().iter() { + if let Some(ExpressionVariant::Cmp(ip)) = e.get_data() { + gexpr = e.clone(); + } + } + + let rules = list_rules_for_chain(&chain).unwrap(); + for mut rule in rules { + let handle = rule.get_handle().unwrap(); + println!("handle {}", handle); + let exprs = rule.get_expressions().unwrap(); + for expr in exprs.iter() { + if let Some(ExpressionVariant::Cmp(ip)) = expr.get_data() { + if expr.clone() == gexpr { + println!("{:?}", expr.get_data()); + println!("test"); + break; + } + } + //if expr.get_data() + //if expr. + } + } + + //let mut set: Set = nft_set!( + // &CString::new("blabla").unwrap(), + // 32, + // &table, + // ProtoFamily::Ipv4 //ProtoFamily::Ipv4; + // //[&toadd1,&toadd2,] + //); + ////println!("{:?}", set.0); + //set.add(&toadd1); + //set.add(&toadd2); + //batch.add(&set, MsgType::Add); + + //let mut rule = Rule::new(&chain) + // .unwrap() + // .with_expr( + // HighLevelPayload::Network(NetworkHeaderField::IPv4(IPv4HeaderField::Saddr)).build(), + // ) + // .with_expr(Lookup::new(&set).unwrap()) + // .with_expr(Immediate::new_verdict(VerdictKind::Accept)); + //println!("{:?}", rule); + //batch.add(&rule, rustables::MsgType::Add); + + //match batch.send() { + // Ok(o) => {} + // Err(e) => { + // println!("{e}"); + // } + //} + + //rule.add_expr(&nft_expr!(payload ipv4 saddr)); + //#[rustfmt::skip] + //rule.add_expr(&nft_expr!(lookup &set)); + //rule.add_expr(&nft_expr!(ct state)); + //rule.add_expr(&nft_expr!(verdict drop)); + //batch.add(&rule, MsgType::Add); + //let finalized_batch = batch.finalize(); + //send_and_process(&finalized_batch)?; + + Ok(()) +} + +#[allow(dead_code)] +#[derive(Debug)] +struct Error(String); + +impl From for Error { + fn from(error: T) -> Self { + Error(error.to_string()) + } +} -- 2.39.5 From d13ae3b02325bb1ad7e50f78978e35db8196ef93 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:13:28 +0100 Subject: [PATCH 04/20] updated .drone.yml --- .drone.yml | 85 ++++++++++-------------------------------------------- 1 file changed, 15 insertions(+), 70 deletions(-) diff --git a/.drone.yml b/.drone.yml index aa697c5..086e2dd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,14 +8,15 @@ platform: arch: amd64 steps: - - name: build and test + - &build-test + name: build and test image: rust:1 pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 - - chmod +x /usr/bin/sccache + - apt-get install -y libnftnl-dev libmnl-dev libclang-dev + - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x ${RUSTC_WRAPPER} - cargo build --verbose --all - cargo test --verbose --all environment: @@ -31,14 +32,15 @@ steps: event: exclude: - tag - - name: release + - &release + name: release image: rust:1 pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 - - chmod +x /usr/bin/sccache + - apt-get install -y libnftnl-dev libmnl-dev libclang-dev + - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x ${RUSTC_WRAPPER} - cargo build --release --verbose --all - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc @@ -54,7 +56,8 @@ steps: when: event: - tag - - name: publish + - &publish + name: publish image: plugins/gitea-release settings: base_url: https://git.paulbsd.com @@ -87,67 +90,9 @@ platform: arch: arm64 steps: - - name: build and test - image: rust:1 - pull: always - commands: - - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 - - chmod +x /usr/bin/sccache - - cargo build --verbose --all - - cargo test --verbose --all - environment: - RUSTC_WRAPPER: /usr/bin/sccache - SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com - SCCACHE_WEBDAV_KEY_PREFIX: sccache - volumes: - - name: cargo - path: /usr/local/cargo/registry - - name: apt - path: /var/cache/apt - when: - event: - exclude: - - tag - - name: release - image: rust:1 - pull: always - commands: - - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 - - chmod +x /usr/bin/sccache - - cargo build --release --verbose --all - - cd target/release - - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc - environment: - RUSTC_WRAPPER: /usr/bin/sccache - SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com - SCCACHE_WEBDAV_KEY_PREFIX: sccache - volumes: - - name: cargo - path: /usr/local/cargo/registry - - name: apt - path: /var/cache/apt - when: - event: - - tag - - name: publish - image: plugins/gitea-release - settings: - base_url: https://git.paulbsd.com - api_key: - from_secret: gitea_token - files: "target/release/*.tar.gz" - #checksum: - # - sha256 - # - sha512 - environment: - PLUGIN_TITLE: "" - when: - event: - - tag + - *build_test + - *release + - *publish volumes: - name: cargo host: -- 2.39.5 From a7301744464605c3fca5cf509096cfd29c053e73 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:14:08 +0100 Subject: [PATCH 05/20] updated .drone.yml --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 086e2dd..e82d36f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,7 @@ platform: arch: amd64 steps: - - &build-test + - &build_test name: build and test image: rust:1 pull: always -- 2.39.5 From c0d53383ca79a741d951624753304dab848623d7 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:16:58 +0100 Subject: [PATCH 06/20] updated .drone.yml --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index e82d36f..2f237d1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,7 @@ platform: arch: amd64 steps: - - &build_test + - step: &build_test name: build and test image: rust:1 pull: always -- 2.39.5 From 68ffbfed9c6cc5b385d21961a157596ad02cce36 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:18:41 +0100 Subject: [PATCH 07/20] Revert "updated .drone.yml" This reverts commit c0d53383ca79a741d951624753304dab848623d7. --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 2f237d1..e82d36f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,7 @@ platform: arch: amd64 steps: - - step: &build_test + - &build_test name: build and test image: rust:1 pull: always -- 2.39.5 From 7145964d08a618a299d3757df6ba1f22bf534333 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:18:56 +0100 Subject: [PATCH 08/20] Revert "updated .drone.yml" This reverts commit a7301744464605c3fca5cf509096cfd29c053e73. --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index e82d36f..086e2dd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,7 @@ platform: arch: amd64 steps: - - &build_test + - &build-test name: build and test image: rust:1 pull: always -- 2.39.5 From dd11d6bc41eab06f4eb7a7fa3e723f9a7457104f Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:18:59 +0100 Subject: [PATCH 09/20] Revert "updated .drone.yml" This reverts commit d13ae3b02325bb1ad7e50f78978e35db8196ef93. --- .drone.yml | 85 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/.drone.yml b/.drone.yml index 086e2dd..aa697c5 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,15 +8,14 @@ platform: arch: amd64 steps: - - &build-test - name: build and test + - name: build and test image: rust:1 pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - - chmod +x ${RUSTC_WRAPPER} + - apt-get install -y libnftnl-dev libmnl-dev + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 + - chmod +x /usr/bin/sccache - cargo build --verbose --all - cargo test --verbose --all environment: @@ -32,15 +31,14 @@ steps: event: exclude: - tag - - &release - name: release + - name: release image: rust:1 pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - - chmod +x ${RUSTC_WRAPPER} + - apt-get install -y libnftnl-dev libmnl-dev + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 + - chmod +x /usr/bin/sccache - cargo build --release --verbose --all - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc @@ -56,8 +54,7 @@ steps: when: event: - tag - - &publish - name: publish + - name: publish image: plugins/gitea-release settings: base_url: https://git.paulbsd.com @@ -90,9 +87,67 @@ platform: arch: arm64 steps: - - *build_test - - *release - - *publish + - name: build and test + image: rust:1 + pull: always + commands: + - apt-get update -y + - apt-get install -y libnftnl-dev libmnl-dev + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 + - chmod +x /usr/bin/sccache + - cargo build --verbose --all + - cargo test --verbose --all + environment: + RUSTC_WRAPPER: /usr/bin/sccache + SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com + SCCACHE_WEBDAV_KEY_PREFIX: sccache + volumes: + - name: cargo + path: /usr/local/cargo/registry + - name: apt + path: /var/cache/apt + when: + event: + exclude: + - tag + - name: release + image: rust:1 + pull: always + commands: + - apt-get update -y + - apt-get install -y libnftnl-dev libmnl-dev + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 + - chmod +x /usr/bin/sccache + - cargo build --release --verbose --all + - cd target/release + - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc + environment: + RUSTC_WRAPPER: /usr/bin/sccache + SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com + SCCACHE_WEBDAV_KEY_PREFIX: sccache + volumes: + - name: cargo + path: /usr/local/cargo/registry + - name: apt + path: /var/cache/apt + when: + event: + - tag + - name: publish + image: plugins/gitea-release + settings: + base_url: https://git.paulbsd.com + api_key: + from_secret: gitea_token + files: "target/release/*.tar.gz" + #checksum: + # - sha256 + # - sha512 + environment: + PLUGIN_TITLE: "" + when: + event: + - tag volumes: - name: cargo host: -- 2.39.5 From 044fa2b1f19455c0e9cf18dd683e4471f9146829 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:29:00 +0100 Subject: [PATCH 10/20] updated .drone.yml --- .drone.yml | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/.drone.yml b/.drone.yml index aa697c5..0e26d7d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -13,11 +13,11 @@ steps: pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 - - chmod +x /usr/bin/sccache - - cargo build --verbose --all - - cargo test --verbose --all + - apt-get install -y libnftnl-dev libmnl-dev libclang-dev + - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x ${RUSTC_WRAPPER} + - cargo b -v --all + - cargo t -v --all environment: RUSTC_WRAPPER: /usr/bin/sccache SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com @@ -36,10 +36,10 @@ steps: pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 - - chmod +x /usr/bin/sccache - - cargo build --release --verbose --all + - apt-get install -y libnftnl-dev libmnl-dev libclang-dev + - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x ${RUSTC_WRAPPER} + - cargo b -r -v --all - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc environment: @@ -61,9 +61,6 @@ steps: api_key: from_secret: gitea_token files: "target/release/*.tar.gz" - #checksum: - # - sha256 - # - sha512 environment: PLUGIN_TITLE: "" when: @@ -92,11 +89,11 @@ steps: pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 - - chmod +x /usr/bin/sccache - - cargo build --verbose --all - - cargo test --verbose --all + - apt-get install -y libnftnl-dev libmnl-dev libclang-dev + - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x ${RUSTC_WRAPPER} + - cargo b -v --all + - cargo t -v --all environment: RUSTC_WRAPPER: /usr/bin/sccache SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com @@ -115,10 +112,10 @@ steps: pull: always commands: - apt-get update -y - - apt-get install -y libnftnl-dev libmnl-dev - - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 - - chmod +x /usr/bin/sccache - - cargo build --release --verbose --all + - apt-get install -y libnftnl-dev libmnl-dev libclang-dev + - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x ${RUSTC_WRAPPER} + - cargo b -r -v --all - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc environment: @@ -140,9 +137,6 @@ steps: api_key: from_secret: gitea_token files: "target/release/*.tar.gz" - #checksum: - # - sha256 - # - sha512 environment: PLUGIN_TITLE: "" when: -- 2.39.5 From b5f195fa69dfbae738fa24f0e775122a74ce3ab5 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:34:56 +0100 Subject: [PATCH 11/20] updated .drone.yml --- .drone.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.drone.yml b/.drone.yml index 0e26d7d..a2b0365 100644 --- a/.drone.yml +++ b/.drone.yml @@ -14,8 +14,8 @@ steps: commands: - apt-get update -y - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - - chmod +x ${RUSTC_WRAPPER} + - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x $${RUSTC_WRAPPER} - cargo b -v --all - cargo t -v --all environment: @@ -37,8 +37,8 @@ steps: commands: - apt-get update -y - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - - chmod +x ${RUSTC_WRAPPER} + - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x $${RUSTC_WRAPPER} - cargo b -r -v --all - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc @@ -90,8 +90,8 @@ steps: commands: - apt-get update -y - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - - chmod +x ${RUSTC_WRAPPER} + - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x $${RUSTC_WRAPPER} - cargo b -v --all - cargo t -v --all environment: @@ -113,8 +113,8 @@ steps: commands: - apt-get update -y - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - - curl -o ${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - - chmod +x ${RUSTC_WRAPPER} + - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} + - chmod +x $${RUSTC_WRAPPER} - cargo b -r -v --all - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc -- 2.39.5 From e968539df56ea661b1f801f897f19acca6a757d2 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 2 Dec 2024 20:37:24 +0100 Subject: [PATCH 12/20] updated .drone.yml --- .drone.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.drone.yml b/.drone.yml index a2b0365..66b88a8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -16,8 +16,8 @@ steps: - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - chmod +x $${RUSTC_WRAPPER} - - cargo b -v --all - - cargo t -v --all + - cargo b -v + - cargo t -v environment: RUSTC_WRAPPER: /usr/bin/sccache SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com @@ -39,7 +39,7 @@ steps: - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - chmod +x $${RUSTC_WRAPPER} - - cargo b -r -v --all + - cargo b -r -v - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc environment: @@ -92,8 +92,8 @@ steps: - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - chmod +x $${RUSTC_WRAPPER} - - cargo b -v --all - - cargo t -v --all + - cargo b -v + - cargo t -v environment: RUSTC_WRAPPER: /usr/bin/sccache SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com @@ -115,7 +115,7 @@ steps: - apt-get install -y libnftnl-dev libmnl-dev libclang-dev - curl -o $${RUSTC_WRAPPER} https://assets.paulbsd.com/sccache_linux_${DRONE_STAGE_ARCH} - chmod +x $${RUSTC_WRAPPER} - - cargo b -r -v --all + - cargo b -r -v - cd target/release - tar -czvf ipblc-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz ipblc environment: -- 2.39.5 From 8ac9c88ce6ff267b16078b2d2d0b670dc53a48a2 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 8 Dec 2024 22:37:37 +0100 Subject: [PATCH 13/20] feat: add working revision with atomic replace --- src/config.rs | 52 +++++++++++---- src/fw.rs | 167 +++++++++++++++++++++++++++++----------------- src/ip.rs | 12 ++-- src/ipblc.rs | 57 ++++++++++------ src/monitoring.rs | 4 +- src/utils.rs | 5 +- src/websocket.rs | 9 ++- 7 files changed, 197 insertions(+), 109 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1313e68..9e11ffb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,12 @@ use crate::ip::{BlockIpData, IpData, IpEvent}; use crate::utils::{gethostname, sleep_s}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, + path::Path, +}; + use chrono::prelude::*; use chrono::Duration; use clap::{Arg, ArgAction, ArgMatches, Command}; @@ -10,11 +16,9 @@ use nix::sys::inotify::{AddWatchFlags, Inotify, WatchDescriptor}; use regex::Regex; use reqwest::{Client, Error as ReqError, Response}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::hash::{Hash, Hasher}; -use std::path::Path; pub const GIT_VERSION: &str = git_version!(args = ["--always", "--dirty="]); + const MASTERSERVER: &str = "ipbl.paulbsd.com"; const WSSUBSCRIPTION: &str = "ipbl"; const CONFIG_RETRY_INTERVAL: u64 = 2; @@ -28,7 +32,7 @@ pub struct Context { pub flags: Flags, pub sas: HashMap, pub hashwd: HashMap, - pub reloadinterval: isize, + pub reloadinterval: u64, } #[derive(Debug, Clone)] @@ -134,6 +138,25 @@ impl Context { } }; } + + let mut last_in_err = false; + loop { + let res = self.discovery().await; + match res { + Ok(o) => { + self.discovery = o; + if last_in_err { + println!("loaded discovery"); + } + break; + } + Err(e) => { + println!("error loading disvoery: {e}, retrying in {CONFIG_RETRY_INTERVAL}s"); + last_in_err = true; + sleep_s(CONFIG_RETRY_INTERVAL).await; + } + }; + } if last_in_err { println!("creating sas"); } @@ -145,7 +168,7 @@ impl Context { } #[cfg(test)] - pub async fn get_blocklist_pending(&self) -> Vec { + pub async fn get_blocklist_pending(&self) -> Vec { let mut res: Vec = vec![]; for (_, v) in self.blocklist.iter() { res.push(v.ipdata.clone()); @@ -153,13 +176,13 @@ impl Context { res } - pub async fn get_blocklist_toblock(&self) -> Vec { - let mut res: Vec = vec![]; + pub async fn get_blocklist_toblock(&self, all: bool) -> Vec { + let mut res: Vec = vec![]; for (_, ipblock) in self.blocklist.iter() { match self.cfg.sets.get(&ipblock.ipdata.src) { Some(set) => { - if ipblock.tryfail >= set.tryfail && !ipblock.blocked { - res.push(ipblock.ipdata.clone()); + if ipblock.tryfail >= set.tryfail && (!ipblock.blocked || all) { + res.push(ipblock.clone()); } } None => {} @@ -177,6 +200,7 @@ impl Context { .with_timezone(&chrono::Local); let blocktime = set.blocktime; let blocked = false; + let handle = u64::MIN; if ipevent.mode == "file".to_string() && gethostname(true) == ipevent.hostname { let block = self.blocklist @@ -187,6 +211,7 @@ impl Context { starttime, blocktime, blocked, + handle, }); block.tryfail += 1; block.blocktime = blocktime; @@ -202,6 +227,7 @@ impl Context { starttime, blocktime, blocked, + handle, }); } } @@ -212,8 +238,8 @@ impl Context { None } - pub async fn gc_blocklist(&mut self) -> Vec { - let mut removed: Vec = vec![]; + pub async fn gc_blocklist(&mut self) -> Vec { + let mut removed: Vec = vec![]; let now: DateTime = Local::now().trunc_subsecs(0); // nightly, future use // let drained: HashMap = ctx.blocklist.drain_filter(|k,v| v.parse_date() < mindate) @@ -228,7 +254,7 @@ impl Context { let mindate = now - Duration::minutes(blocked.blocktime); if blocked.starttime < mindate { self.blocklist.remove(&ip.clone()).unwrap(); - removed.push(blocked.ipdata.clone()); + removed.push(blocked.clone()); } } removed @@ -640,7 +666,7 @@ mod test { pub async fn test_blocklist_toblock() { let mut ctx = prepare_test_data().await; ctx.gc_blocklist().await; - let toblock = ctx.get_blocklist_toblock().await; + let toblock = ctx.get_blocklist_toblock(false).await; assert_eq!(toblock.len(), 3); } diff --git a/src/fw.rs b/src/fw.rs index 92c8bdf..2ca6b2e 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,11 +1,11 @@ -use crate::{ip::IpData, ipblc::PKG_NAME}; +use crate::{config::Context, ip::BlockIpData, ipblc::PKG_NAME}; use std::{ io::Error, - net::{Ipv4Addr, Ipv6Addr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; -use rustables::*; +use rustables::{expr::*, *}; pub enum FwTableType { IPv4, @@ -30,7 +30,7 @@ macro_rules! initrules { macro_rules! makerules { ($ipdata:ident, $chain:ident, $batch:ident, $t:ty, $ip_t:ident,$action:ty) => { - let ip = $ipdata.ip.parse::<$t>().unwrap(); + let ip = $ipdata.ipdata.ip.parse::<$t>().unwrap(); Rule::new(&$chain) .unwrap() .saddr(ip.into()) @@ -67,91 +67,132 @@ pub fn fwglobalinit(t: FwTableType, reset: bool) -> (Batch, Chain) { (batch, chain) } -pub fn fwblock<'a>(ip_add: &IpData) -> std::result::Result<(), error::QueryError> { +pub fn fwblock<'a>(ip_add: &BlockIpData) -> std::result::Result<&String, error::QueryError> { let (mut batch4, chain4) = fwglobalinit(FwTableType::IPv4, false); let (mut batch6, chain6) = fwglobalinit(FwTableType::IPv6, false); - match ip_add.t { + match ip_add.ipdata.t { 4 => { makerules!(ip_add, chain4, batch4, Ipv4Addr, ipv4, FwAction::Add); + match batch4.send() { + Ok(_) => {} + Err(e) => { + println!("block not ok {e} {ip_add:?}") + } + } } 6 => { makerules!(ip_add, chain6, batch6, Ipv6Addr, ipv6, FwAction::Add); + match batch6.send() { + Ok(_) => {} + Err(e) => { + println!("block not ok {e} {ip_add:?}") + } + } } _ => {} } - // validate and send batch - for b in [batch4, batch6] { - match b.send() { - Ok(_) => {} - Err(e) => { - println!("error sending batch: {e}"); - return Err(e); - } - }; - } - - Ok(()) + Ok(&ip_add.ipdata.ip) } -pub fn fwunblock<'a>(ips_del: &Vec) -> std::result::Result<(), Error> { +pub fn fwunblock<'a>(ip_del: &BlockIpData) -> std::result::Result<&String, error::QueryError> { let (mut batch4, chain4) = fwglobalinit(FwTableType::IPv4, false); let (mut batch6, chain6) = fwglobalinit(FwTableType::IPv6, false); - // to implement - /*for ip_del in ips_del { - match ip_del.t { - 4 => { - makerules!(ip_del, chain4, batch4, Ipv4Addr, ipv4, FwAction::Del); + match ip_del.ipdata.t { + 4 => { + let r = Rule::new(&chain4).unwrap().with_handle(ip_del.handle); + batch4.add(&r, MsgType::Del); + match batch4.send() { + Ok(_) => {} + Err(e) => { + println!("delete not ok {e} {ip_del:?}") + } } - 6 => { - makerules!(ip_del, chain6, batch6, Ipv6Addr, ipv6, FwAction::Del); - } - _ => {} } - }*/ - Ok(()) + 6 => { + let r = Rule::new(&chain6).unwrap().with_handle(ip_del.handle); + batch6.add(&r, MsgType::Del); + match batch6.send() { + Ok(_) => {} + Err(e) => { + println!("delete not ok {e} {ip_del:?}") + } + } + } + _ => {} + } + Ok(&ip_del.ipdata.ip) } -pub fn get_current_rules(table_name: &str, chain_name: &str) -> Result<(), Error> { - let get_table = || -> Result, Error> { - let tables = list_tables().unwrap(); - for table in tables { - if let Some(name) = table.get_name() { - println!("Found table {}", name); +pub fn get_current_rules( + ctx: &mut Context, + ret: &mut Vec, + fwlen: &mut usize, +) -> Result<(), Error> { + let mut ips_all_count = 0; + let tables = vec![format!("{PKG_NAME}4"), format!("{PKG_NAME}6")]; + for table_name in tables { + let get_table = || -> Result, Error> { + let tables = list_tables().unwrap(); + for table in tables { + if let Some(name) = table.get_name() { + if *name == table_name { + return Ok(Some(table)); + } + } + } - if *name == table_name { - return Ok(Some(table)); + Ok(None) + }; + + let get_chain = |table: &Table| -> Result, Error> { + let chains = list_chains_for_table(table).unwrap(); + for chain in chains { + if let Some(name) = chain.get_name() { + if *name == "ipblc" { + return Ok(Some(chain)); + } + } + } + + Ok(None) + }; + + let table = get_table()?.expect("no table?"); + let chain = get_chain(&table)?.expect("no chain?"); + + for (ip, c) in ctx.blocklist.iter_mut() { + let ip_parsed: IpAddr = ip.parse().unwrap(); + + let cmprule = Rule::new(&chain).unwrap().saddr(ip_parsed).drop(); + let mut gexpr = RawExpression::default(); + for e in cmprule.get_expressions().unwrap().iter() { + if let Some(ExpressionVariant::Cmp(_ip)) = e.get_data() { + gexpr = e.clone(); + } + } + + let rules = list_rules_for_chain(&chain).unwrap(); + for rule in rules { + for expr in rule.get_expressions().unwrap().iter() { + if let Some(expr::ExpressionVariant::Cmp(_)) = expr.get_data() { + if gexpr == expr.clone() { + ips_all_count += 1; + c.handle = *rule.get_handle().unwrap(); + } + } } } } - - Ok(None) - }; - - let get_chain = |table: &Table| -> Result, Error> { - let chains = list_chains_for_table(table).unwrap(); - for chain in chains { - if let Some(name) = chain.get_name() { - println!("Found chain {}", name); - - if *name == chain_name { - return Ok(Some(chain)); - } - } - } - - Ok(None) - }; - - let table = get_table()?.expect("no table?"); - let chain = get_chain(&table)?.expect("no chain?"); - - let rules = list_rules_for_chain(&chain).unwrap(); - for mut rule in rules { - println!("{:?}", rule); } + + if *fwlen != ips_all_count { + ret.push(format!("{length} ip in firewall", length = ips_all_count)); + } + *fwlen = ips_all_count; + Ok(()) } diff --git a/src/ip.rs b/src/ip.rs index 93a705b..63afa07 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -1,15 +1,18 @@ use crate::utils::gethostname; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, + io::{BufRead, BufReader, Read}, + net::IpAddr, +}; + use chrono::offset::LocalResult; use chrono::prelude::*; use ipnet::IpNet; use lazy_static::lazy_static; use regex::Regex; use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; -use std::fmt::{Display, Formatter}; -use std::io::{BufRead, BufReader, Read}; -use std::net::IpAddr; lazy_static! { static ref R_IPV4: Regex = Regex::new(include_str!("regexps/ipv4.txt")).unwrap(); @@ -52,6 +55,7 @@ pub struct BlockIpData { pub blocktime: i64, pub starttime: DateTime, pub blocked: bool, + pub handle: u64, } #[derive(Clone, Debug, Serialize, Deserialize, Eq)] diff --git a/src/ipblc.rs b/src/ipblc.rs index 9f5d004..883cd74 100644 --- a/src/ipblc.rs +++ b/src/ipblc.rs @@ -7,20 +7,19 @@ use crate::utils::{gethostname, read_lines, sleep_s}; use crate::webservice::send_to_ipbl_api; use crate::websocket::{send_to_ipbl_websocket, websocketpubsub, websocketreqrep}; +use std::{collections::HashMap, sync::Arc}; + use chrono::prelude::*; use chrono::prelude::{DateTime, Local}; use chrono::Duration; use nix::sys::inotify::{InitFlags, Inotify, InotifyEvent}; use sd_notify::*; -use std::collections::HashMap; -use std::sync::Arc; use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::RwLock; pub const PKG_NAME: &str = env!("CARGO_PKG_NAME"); const BL_CHAN_SIZE: usize = 32; const WS_CHAN_SIZE: usize = 64; -const LOOP_MAX_WAIT: u64 = 5; macro_rules! log_with_systemd { ($msg:expr) => { @@ -75,21 +74,26 @@ pub async fn run() { loop { let mut ret: Vec = Vec::new(); - let ctxclone = Arc::clone(&ctxarc); + let reloadinterval; + { + let ctx = ctxclone.read().await; + reloadinterval = ctx.reloadinterval; + } + tokio::select! { ipevent = ipeventrx.recv() => { let received_ip = ipevent.unwrap(); let (toblock,server) = { let ctx = ctxclone.read().await; - (ctx.get_blocklist_toblock().await,ctx.flags.server.clone()) + (ctx.get_blocklist_toblock(true).await,ctx.flags.server.clone()) }; if received_ip.msgtype == "bootstrap".to_string() { for ip_to_send in toblock { - let ipe = ipevent!("init","ws",gethostname(true),Some(ip_to_send)); + let ipe = ipevent!("init","ws",gethostname(true),Some(ip_to_send.ipdata)); if !send_to_ipbl_websocket(&mut wssocketrr, &ipe).await { wssocketrr.close(None).unwrap(); wssocketrr = websocketreqrep(&ctxwsrr).await; @@ -118,7 +122,7 @@ pub async fn run() { } } } - _val = sleep_s(LOOP_MAX_WAIT) => { + _val = sleep_s(reloadinterval) => { let ipe = ipevent!("ping", "ws", gethostname(true)); if !send_to_ipbl_websocket(&mut wssocketrr, &ipe).await { wssocketrr.close(None).unwrap(); @@ -134,15 +138,20 @@ pub async fn run() { }; let toblock = { let ctx = ctxclone.read().await; - ctx.get_blocklist_toblock().await + ctx.get_blocklist_toblock(false).await }; - // apply firewall blocking + { + let mut ctx = ctxclone.write().await; + get_current_rules(&mut ctx, &mut ret, &mut fwlen).unwrap(); + get_current_rules(&mut ctx, &mut ret, &mut fwlen).unwrap(); + } + for b in toblock { match fwblock(&b) { - Ok(_) => { + Ok(ip) => { let mut ctx = ctxclone.write().await; - if let Some(x) = ctx.blocklist.get_mut(&b.ip) { + if let Some(x) = ctx.blocklist.get_mut(ip) { x.blocked = true; } } @@ -151,13 +160,18 @@ pub async fn run() { } }; } - get_current_rules("ipblc4", "ipblc").unwrap(); - match fwunblock(&tounblock) { - Ok(_) => {} - Err(e) => { - println!("err: {e}, unable to push firewall rules, use super user") + + for ub in tounblock { + if ub.blocked { + let res = fwunblock(&ub); + match res { + Ok(_) => {} + Err(e) => { + println!("err: {e}, unable to push firewall rules, use super user") + } + }; } - }; + } // log lines if ret.len() > 0 { @@ -167,17 +181,18 @@ pub async fn run() { let ctxclone = Arc::clone(&ctxarc); let inoclone = Arc::clone(&inoarc); - handle_cfg_reload(&ctxclone, &mut last_cfg_reload, inoclone).await; + handle_cfg_reload(&ctxclone, reloadinterval, &mut last_cfg_reload, inoclone).await; } } async fn handle_cfg_reload( ctxclone: &Arc>, + reloadinterval: u64, last_cfg_reload: &mut DateTime, inoarc: Arc>, ) { let now_cfg_reload = Local::now().trunc_subsecs(0); - if (now_cfg_reload - *last_cfg_reload) > Duration::seconds(LOOP_MAX_WAIT as i64) { + if (now_cfg_reload - *last_cfg_reload) > Duration::seconds(reloadinterval as i64) { let inotify; loop { inotify = match inoarc.try_read() { @@ -190,14 +205,14 @@ async fn handle_cfg_reload( }; break; } - let mut ctxtest = match ctxclone.try_write() { + let mut ctx = match ctxclone.try_write() { Ok(o) => o, Err(e) => { println!("{e}"); return; } }; - match ctxtest.load(&inotify).await { + match ctx.load(&inotify).await { Ok(_) => { *last_cfg_reload = Local::now().trunc_subsecs(0); } diff --git a/src/monitoring.rs b/src/monitoring.rs index 4ca1634..ead461f 100644 --- a/src/monitoring.rs +++ b/src/monitoring.rs @@ -1,8 +1,8 @@ use crate::config::Context; +use std::{io, sync::Arc}; + use serde_json; -use std::io; -use std::sync::Arc; use tokio::io::AsyncWriteExt; use tokio::net::TcpListener; use tokio::sync::RwLock; diff --git a/src/utils.rs b/src/utils.rs index 8066012..9fe6bb7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,6 @@ +use std::{boxed::Box, fs::File, io::*}; + use nix::unistd; -use std::boxed::Box; -use std::fs::File; -use std::io::*; use tokio::time::{sleep, Duration}; pub fn read_lines(filename: &String, offset: u64) -> Option> { diff --git a/src/websocket.rs b/src/websocket.rs index 436239d..aac0f79 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -2,10 +2,13 @@ use crate::config::{Context, WebSocketCfg}; use crate::ip::IpEvent; use crate::utils::{gethostname, sleep_s}; +use std::{ + io::{self, Write}, + net::TcpStream, + sync::Arc, +}; + use serde_json::json; -use std::io::{self, Write}; -use std::net::TcpStream; -use std::sync::Arc; use tokio::sync::mpsc::Sender; use tokio::sync::RwLock; use tungstenite::stream::*; -- 2.39.5 From 03d7cc17576166d97c357f1d5353348143954673 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 21 Dec 2024 06:59:08 +0100 Subject: [PATCH 14/20] optimize ip garbage collection --- src/fw.rs | 13 +++++++++---- src/ipblc.rs | 23 ++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/fw.rs b/src/fw.rs index 2ca6b2e..d687c6c 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -3,8 +3,11 @@ use crate::{config::Context, ip::BlockIpData, ipblc::PKG_NAME}; use std::{ io::Error, net::{IpAddr, Ipv4Addr, Ipv6Addr}, + sync::Arc, }; +use tokio::sync::RwLock; + use rustables::{expr::*, *}; pub enum FwTableType { @@ -126,8 +129,8 @@ pub fn fwunblock<'a>(ip_del: &BlockIpData) -> std::result::Result<&String, error Ok(&ip_del.ipdata.ip) } -pub fn get_current_rules( - ctx: &mut Context, +pub async fn get_current_rules( + ctx: &Arc>, ret: &mut Vec, fwlen: &mut usize, ) -> Result<(), Error> { @@ -163,6 +166,9 @@ pub fn get_current_rules( let table = get_table()?.expect("no table?"); let chain = get_chain(&table)?.expect("no chain?"); + let mut ctx = { ctx.write().await }; + let rules = list_rules_for_chain(&chain).unwrap().clone(); + for (ip, c) in ctx.blocklist.iter_mut() { let ip_parsed: IpAddr = ip.parse().unwrap(); @@ -174,8 +180,7 @@ pub fn get_current_rules( } } - let rules = list_rules_for_chain(&chain).unwrap(); - for rule in rules { + for rule in rules.iter() { for expr in rule.get_expressions().unwrap().iter() { if let Some(expr::ExpressionVariant::Cmp(_)) = expr.get_data() { if gexpr == expr.clone() { diff --git a/src/ipblc.rs b/src/ipblc.rs index 883cd74..f026cb6 100644 --- a/src/ipblc.rs +++ b/src/ipblc.rs @@ -132,23 +132,21 @@ pub async fn run() { }; let ctxclone = Arc::clone(&ctxarc); - let tounblock = { + let ipstounblock = { let mut ctx = ctxclone.write().await; ctx.gc_blocklist().await }; - let toblock = { + let ipstoblock = { let ctx = ctxclone.read().await; ctx.get_blocklist_toblock(false).await }; - { - let mut ctx = ctxclone.write().await; - get_current_rules(&mut ctx, &mut ret, &mut fwlen).unwrap(); - get_current_rules(&mut ctx, &mut ret, &mut fwlen).unwrap(); - } + get_current_rules(&ctxarc, &mut ret, &mut fwlen) + .await + .unwrap(); - for b in toblock { - match fwblock(&b) { + for ip in ipstoblock { + match fwblock(&ip) { Ok(ip) => { let mut ctx = ctxclone.write().await; if let Some(x) = ctx.blocklist.get_mut(ip) { @@ -161,10 +159,9 @@ pub async fn run() { }; } - for ub in tounblock { - if ub.blocked { - let res = fwunblock(&ub); - match res { + for ip in ipstounblock { + if ip.blocked { + match fwunblock(&ip) { Ok(_) => {} Err(e) => { println!("err: {e}, unable to push firewall rules, use super user") -- 2.39.5 From 1b2fa0ba3e59e3a94b8880072db4e9ecb2fe25eb Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 21 Dec 2024 07:03:30 +0100 Subject: [PATCH 15/20] move old examples --- examples/{fw2.rs => fw_with_rustables.rs} | 0 {examples => old}/fw.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/{fw2.rs => fw_with_rustables.rs} (100%) rename {examples => old}/fw.rs (100%) diff --git a/examples/fw2.rs b/examples/fw_with_rustables.rs similarity index 100% rename from examples/fw2.rs rename to examples/fw_with_rustables.rs diff --git a/examples/fw.rs b/old/fw.rs similarity index 100% rename from examples/fw.rs rename to old/fw.rs -- 2.39.5 From 03102d63150e8c4fbe8f7371eeb956750b3956e3 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 21 Dec 2024 07:07:18 +0100 Subject: [PATCH 16/20] move old files --- src/old.rs => old/oldfuncs.rs | 0 {tests => old}/testfw.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/old.rs => old/oldfuncs.rs (100%) rename {tests => old}/testfw.rs (100%) diff --git a/src/old.rs b/old/oldfuncs.rs similarity index 100% rename from src/old.rs rename to old/oldfuncs.rs diff --git a/tests/testfw.rs b/old/testfw.rs similarity index 100% rename from tests/testfw.rs rename to old/testfw.rs -- 2.39.5 From 0130e47b3467530d79b2ace998a3db75c4132046 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 21 Dec 2024 07:08:17 +0100 Subject: [PATCH 17/20] move old files --- {tests => misc}/testzmq.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {tests => misc}/testzmq.py (100%) diff --git a/tests/testzmq.py b/misc/testzmq.py similarity index 100% rename from tests/testzmq.py rename to misc/testzmq.py -- 2.39.5 From dc09883c36ec753bc31b18fa3d2fb96725b08763 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 21 Dec 2024 07:14:26 +0100 Subject: [PATCH 18/20] fix tests --- examples/fw_with_rustables.rs | 10 +++++----- src/config.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/fw_with_rustables.rs b/examples/fw_with_rustables.rs index 66c4c3b..0f98fe2 100644 --- a/examples/fw_with_rustables.rs +++ b/examples/fw_with_rustables.rs @@ -1,6 +1,6 @@ use rustables::*; -use rustables::{expr::*, set::*, Batch, Chain, MsgType, ProtocolFamily, Rule, Table}; -use std::{ffi::*, io::*, net::*}; +use rustables::{expr::*, Chain, Rule, Table}; +use std::{io::*, net::*}; const TABLE_NAME: &str = "ipblc4"; const CHAIN_NAME: &str = "ipblc"; @@ -68,18 +68,18 @@ fn main() -> Result<()> { let mut gexpr = RawExpression::default(); for e in cmprule.get_expressions().unwrap().iter() { - if let Some(ExpressionVariant::Cmp(ip)) = e.get_data() { + if let Some(ExpressionVariant::Cmp(_)) = e.get_data() { gexpr = e.clone(); } } let rules = list_rules_for_chain(&chain).unwrap(); - for mut rule in rules { + for rule in rules { let handle = rule.get_handle().unwrap(); println!("handle {}", handle); let exprs = rule.get_expressions().unwrap(); for expr in exprs.iter() { - if let Some(ExpressionVariant::Cmp(ip)) = expr.get_data() { + if let Some(ExpressionVariant::Cmp(_)) = expr.get_data() { if expr.clone() == gexpr { println!("{:?}", expr.get_data()); println!("test"); diff --git a/src/config.rs b/src/config.rs index 9e11ffb..df7d244 100644 --- a/src/config.rs +++ b/src/config.rs @@ -169,9 +169,9 @@ impl Context { #[cfg(test)] pub async fn get_blocklist_pending(&self) -> Vec { - let mut res: Vec = vec![]; - for (_, v) in self.blocklist.iter() { - res.push(v.ipdata.clone()); + let mut res: Vec = vec![]; + for (_, ipblock) in self.blocklist.iter() { + res.push(ipblock.clone()); } res } -- 2.39.5 From 98a75d2dcd63f4c1c9c018ecd780a92898400f59 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 21 Dec 2024 07:18:04 +0100 Subject: [PATCH 19/20] allow some dead code --- src/config.rs | 1 + src/fw.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index df7d244..92893e5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -47,6 +47,7 @@ pub struct SetMap { #[derive(Debug, Clone)] pub struct Flags { + #[allow(dead_code)] pub debug: bool, pub server: String, } diff --git a/src/fw.rs b/src/fw.rs index d687c6c..498c449 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -15,6 +15,7 @@ pub enum FwTableType { IPv6, } +#[allow(dead_code)] pub enum FwAction { Add, Delete, @@ -201,6 +202,7 @@ pub async fn get_current_rules( Ok(()) } -pub fn fw_rules_count() -> i64 { +#[allow(dead_code)] +fn fw_rules_count() -> i64 { 0 } -- 2.39.5 From 84f3aba863e475d513a8e67a3ad51aef80ed6ec2 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 23 Dec 2024 19:12:12 +0100 Subject: [PATCH 20/20] prepare version 1.8.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a23b0af..9796c8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -724,7 +724,7 @@ dependencies = [ [[package]] name = "ipblc" -version = "1.7.1" +version = "1.8.0" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 59c4b98..90509ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ipblc" -version = "1.7.1" +version = "1.8.0" edition = "2021" authors = ["PaulBSD "] description = "ipblc is a tool that search and send attacking ip addresses to ipbl" -- 2.39.5