From 1a9199011d6f879ec07051db2164713e17e81b2f Mon Sep 17 00:00:00 2001 From: darkgallium Date: Fri, 22 May 2020 01:12:53 +0200 Subject: first basic support for adding nft rules --- Cargo.lock | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 ++ src/fw.rs | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 src/fw.rs diff --git a/Cargo.lock b/Cargo.lock index b51fc45..e86e28a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,7 +127,10 @@ dependencies = [ "anyhow", "futures", "igd", + "libc", "log", + "mnl", + "nftnl", "pretty_env_logger", "reqwest", "serde", @@ -164,6 +167,20 @@ dependencies = [ "termcolor", ] +[[package]] +name = "err-derive" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22deed3a8124cff5fa835713fa105621e43bbdc46690c3a6b68328a012d350d4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "rustversion", + "syn", + "synstructure", +] + [[package]] name = "fnv" version = "1.0.6" @@ -548,9 +565,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.66" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "log" @@ -620,6 +637,27 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "mnl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6efb50a48dbacd112e7e847b9847fa530ec4a473ba6322a2f82c42ef333e226" +dependencies = [ + "libc", + "log", + "mnl-sys", +] + +[[package]] +name = "mnl-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5302035599c722b3a5b92a6502ff73c501dc6d100c53b89f0fae0cb932a37122" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "native-tls" version = "0.2.3" @@ -649,6 +687,30 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "nftnl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3727d1e8c1c9af88857f46539c3030693158a2a7586056b8cab6ded523bf7aa" +dependencies = [ + "bitflags", + "err-derive", + "libc", + "log", + "nftnl-sys", +] + +[[package]] +name = "nftnl-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba134c9b125b7d7c13d813388aaeb2aeeba60fb1eb702799163fb845086ca33" +dependencies = [ + "cfg-if", + "libc", + "pkg-config", +] + [[package]] name = "nom" version = "4.2.3" @@ -764,6 +826,32 @@ dependencies = [ "log", ] +[[package]] +name = "proc-macro-error" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check 0.9.1", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syn-mid", + "version_check 0.9.1", +] + [[package]] name = "proc-macro-hack" version = "0.5.11" @@ -952,6 +1040,17 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustversion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ryu" version = "1.0.2" @@ -1071,6 +1170,29 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "tempfile" version = "3.1.0" diff --git a/Cargo.toml b/Cargo.toml index ce0fbc3..2376c52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ serde = { version = "1.0.107", features = ["derive"] } serde_json = "1.0.53" serde-lexpr = "0.1.1" anyhow = "1.0.28" +nftnl = "0.3.0" +mnl = "0.2.0" +libc = "0.2.70" diff --git a/src/fw.rs b/src/fw.rs new file mode 100644 index 0000000..aabc420 --- /dev/null +++ b/src/fw.rs @@ -0,0 +1,101 @@ +use nftnl::{nft_expr, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table}; +use std::{ + ffi::{self, CString}, + io, +}; +use mnl; +use libc; + +const TABLE_NAME: &str = "diplonat"; +const OUT_CHAIN_NAME: &str = "out"; +const IN_CHAIN_NAME: &str = "in"; + +#[derive(Debug)] +struct Error(String); + +impl From for Error { + fn from(error: io::Error) -> Self { + Error(error.to_string()) + } +} + +impl From for Error { + fn from(error: ffi::NulError) -> Self { + Error(error.to_string()) + } +} + +fn send_and_process(batch: &FinalizedBatch) -> Result<(), Error> { + let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; + socket.send_all(batch)?; + + let portid = socket.portid(); + 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, 2, portid)? { + mnl::CbResult::Stop => { + break; + } + mnl::CbResult::Ok => (), + } + } + Ok(()) +} + +fn socket_recv<'a>(socket: &mnl::Socket, buf: &'a mut [u8]) -> Result, Error> { + let ret = socket.recv(buf)?; + if ret > 0 { + Ok(Some(&buf[..ret])) + } else { + Ok(None) + } +} + +fn add_port_allowed(port: u16) -> Result<(), Error> { + let mut batch = Batch::new(); + + // TODO: at the moment, I haven't found a way to properly separate setup part (create table and + // chains) and the add rule part because the add rule part needs a reference on the chains. + // apparently creating a table and chains that already exist seems to do nothing so it works + // doing the following. To be done properly though + + let table = Table::new(&CString::new(TABLE_NAME).unwrap(), ProtoFamily::Inet); + + batch.add(&table, nftnl::MsgType::Add); + + let mut out_chain = Chain::new(&CString::new(OUT_CHAIN_NAME).unwrap(), &table); + let mut in_chain = Chain::new(&CString::new(IN_CHAIN_NAME).unwrap(), &table); + + out_chain.set_hook(nftnl::Hook::Out, 0); + in_chain.set_hook(nftnl::Hook::In, 0); + + out_chain.set_policy(nftnl::Policy::Accept); + in_chain.set_policy(nftnl::Policy::Drop); + + batch.add(&out_chain, nftnl::MsgType::Add); + batch.add(&in_chain, nftnl::MsgType::Add); + + let mut _rule = Rule::new(&in_chain); + + _rule.add_expr(&nft_expr!(meta nfproto)); + _rule.add_expr(&nft_expr!(cmp == libc::NFPROTO_IPV4 as u8)); + + _rule.add_expr(&nft_expr!(meta l4proto)); + _rule.add_expr(&nft_expr!(cmp == libc::IPPROTO_TCP as u8)); + + _rule.add_expr(&nftnl::expr::Payload::Transport( + nftnl::expr::TransportHeaderField::Tcp(nftnl::expr::TcpHeaderField::Dport), + )); + _rule.add_expr(&nft_expr!(cmp == port.to_be())); + + _rule.add_expr(&nft_expr!(verdict accept)); + + batch.add(&_rule, nftnl::MsgType::Add); + + let finalized_batch = batch.finalize(); + send_and_process(&finalized_batch)?; + + Ok(()) + +} -- cgit v1.2.3 From 2dbf9da005f2b86da9c6861b8f30725033b9b5f2 Mon Sep 17 00:00:00 2001 From: darkgallium Date: Fri, 22 May 2020 16:02:31 +0200 Subject: changing backend to use the cleaner ipt bindings updating dependencies minor fixes --- Cargo.lock | 238 +++++++++++++++++++++++++++++------------------------------- Cargo.toml | 5 +- src/fw.rs | 131 ++++++++++++--------------------- src/main.rs | 7 +- 4 files changed, 169 insertions(+), 212 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e86e28a..83c7c19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +dependencies = [ + "memchr", +] + [[package]] name = "aho-corasick" version = "0.7.10" @@ -49,6 +58,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +[[package]] +name = "bitflags" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" + [[package]] name = "bitflags" version = "1.2.1" @@ -127,11 +142,10 @@ dependencies = [ "anyhow", "futures", "igd", - "libc", + "iptables", "log", - "mnl", - "nftnl", "pretty_env_logger", + "regex 1.3.7", "reqwest", "serde", "serde-lexpr", @@ -163,24 +177,10 @@ dependencies = [ "atty", "humantime", "log", - "regex", + "regex 1.3.7", "termcolor", ] -[[package]] -name = "err-derive" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22deed3a8124cff5fa835713fa105621e43bbdc46690c3a6b68328a012d350d4" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "rustversion", - "syn", - "synstructure", -] - [[package]] name = "fnv" version = "1.0.6" @@ -214,7 +214,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags", + "bitflags 1.2.1", "fuchsia-zircon-sys", ] @@ -509,6 +509,17 @@ dependencies = [ "libc", ] +[[package]] +name = "iptables" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7910549cb022e3112eea631a4c3e62523281b6c61024b2c3a8d61da620010de" +dependencies = [ + "lazy_static 0.2.11", + "nix", + "regex 0.2.11", +] + [[package]] name = "itoa" version = "0.4.5" @@ -534,6 +545,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" + [[package]] name = "lazy_static" version = "1.4.0" @@ -637,34 +654,13 @@ dependencies = [ "ws2_32-sys", ] -[[package]] -name = "mnl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6efb50a48dbacd112e7e847b9847fa530ec4a473ba6322a2f82c42ef333e226" -dependencies = [ - "libc", - "log", - "mnl-sys", -] - -[[package]] -name = "mnl-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5302035599c722b3a5b92a6502ff73c501dc6d100c53b89f0fae0cb932a37122" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "native-tls" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", "libc", "log", "openssl", @@ -688,27 +684,17 @@ dependencies = [ ] [[package]] -name = "nftnl" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3727d1e8c1c9af88857f46539c3030693158a2a7586056b8cab6ded523bf7aa" -dependencies = [ - "bitflags", - "err-derive", - "libc", - "log", - "nftnl-sys", -] - -[[package]] -name = "nftnl-sys" -version = "0.3.0" +name = "nix" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba134c9b125b7d7c13d813388aaeb2aeeba60fb1eb702799163fb845086ca33" +checksum = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b" dependencies = [ + "bitflags 0.4.0", "cfg-if", "libc", - "pkg-config", + "rustc_version", + "semver", + "void", ] [[package]] @@ -733,10 +719,10 @@ version = "0.10.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" dependencies = [ - "bitflags", + "bitflags 1.2.1", "cfg-if", "foreign-types", - "lazy_static", + "lazy_static 1.4.0", "libc", "openssl-sys", ] @@ -826,32 +812,6 @@ dependencies = [ "log", ] -[[package]] -name = "proc-macro-error" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check 0.9.1", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "syn-mid", - "version_check 0.9.1", -] - [[package]] name = "proc-macro-hack" version = "0.5.11" @@ -977,16 +937,38 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "regex" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +dependencies = [ + "aho-corasick 0.6.10", + "memchr", + "regex-syntax 0.5.6", + "thread_local 0.3.6", + "utf8-ranges", +] + [[package]] name = "regex" version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.10", "memchr", - "regex-syntax", - "thread_local", + "regex-syntax 0.6.17", + "thread_local 1.0.1", +] + +[[package]] +name = "regex-syntax" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +dependencies = [ + "ucd-util", ] [[package]] @@ -1020,7 +1002,7 @@ dependencies = [ "hyper", "hyper-tls", "js-sys", - "lazy_static", + "lazy_static 1.4.0", "log", "mime", "mime_guess", @@ -1041,14 +1023,12 @@ dependencies = [ ] [[package]] -name = "rustversion" -version = "1.0.2" +name = "rustc_version" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" dependencies = [ - "proc-macro2", - "quote", - "syn", + "semver", ] [[package]] @@ -1063,7 +1043,7 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", "winapi 0.3.8", ] @@ -1088,6 +1068,12 @@ dependencies = [ "core-foundation-sys", ] +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" + [[package]] name = "serde" version = "1.0.107" @@ -1170,29 +1156,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "synstructure" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "tempfile" version = "3.1.0" @@ -1216,13 +1179,22 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +dependencies = [ + "lazy_static 1.4.0", +] + [[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", ] [[package]] @@ -1245,7 +1217,7 @@ dependencies = [ "bytes 0.5.4", "fnv", "iovec", - "lazy_static", + "lazy_static 1.4.0", "memchr", "mio", "pin-project-lite", @@ -1300,6 +1272,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +[[package]] +name = "ucd-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85f514e095d348c279b1e5cd76795082cf15bd59b93207832abe0b1d8fed236" + [[package]] name = "unicase" version = "2.6.0" @@ -1361,6 +1339,12 @@ dependencies = [ "percent-encoding 2.1.0", ] +[[package]] +name = "utf8-ranges" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" + [[package]] name = "vcpkg" version = "0.2.8" @@ -1379,6 +1363,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "want" version = "0.3.0" @@ -1414,7 +1404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" dependencies = [ "bumpalo", - "lazy_static", + "lazy_static 1.4.0", "log", "proc-macro2", "quote", @@ -1569,7 +1559,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" dependencies = [ - "bitflags", + "bitflags 1.2.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2376c52..6aff0de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,5 @@ serde = { version = "1.0.107", features = ["derive"] } serde_json = "1.0.53" serde-lexpr = "0.1.1" anyhow = "1.0.28" -nftnl = "0.3.0" -mnl = "0.2.0" -libc = "0.2.70" +iptables = "0.2.2" +regex = "1" diff --git a/src/fw.rs b/src/fw.rs index aabc420..07282af 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,101 +1,64 @@ -use nftnl::{nft_expr, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table}; -use std::{ - ffi::{self, CString}, - io, -}; -use mnl; -use libc; +use iptables; +use regex::Regex; +use std::collections::HashSet; + +#[derive(PartialEq,Eq,Debug,Hash)] +pub struct Port { + proto: String, + number: u16, +} -const TABLE_NAME: &str = "diplonat"; -const OUT_CHAIN_NAME: &str = "out"; -const IN_CHAIN_NAME: &str = "in"; +pub fn setup(ipt: &iptables::IPTables) { + ipt.new_chain("filter", "DIPLONAT").unwrap(); + ipt.insert("filter", "INPUT", "-j DIPLONAT", 1).unwrap(); +} -#[derive(Debug)] -struct Error(String); +pub fn open_ports(ipt: &iptables::IPTables, ports: Vec) { -impl From for Error { - fn from(error: io::Error) -> Self { - Error(error.to_string()) + for p in ports { + ipt.append("filter", "DIPLONAT", &format!("-p {} --dport {} -j ACCEPT", p.proto, p.number)).unwrap(); } } -impl From for Error { - fn from(error: ffi::NulError) -> Self { - Error(error.to_string()) - } -} +pub fn get_opened_ports(ipt: &iptables::IPTables) -> HashSet { + let mut opened_ports: HashSet = HashSet::new(); -fn send_and_process(batch: &FinalizedBatch) -> Result<(), Error> { - let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; - socket.send_all(batch)?; + let list = ipt.list("filter", "DIPLONAT").unwrap(); + let re = Regex::new(r"\-A.*? \-p (\w+).*\-\-dport (\d+).*?\-j ACCEPT").unwrap(); + for i in list { + let caps = re.captures(&i); + match caps { + Some(c) => { + let raw_proto = c.get(1).unwrap(); + let raw_port = c.get(2).unwrap(); - let portid = socket.portid(); - let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize]; + let proto = String::from(raw_proto.as_str()); + let number = String::from(raw_port.as_str()).parse::().unwrap(); - while let Some(message) = socket_recv(&socket, &mut buffer[..])? { - match mnl::cb_run(message, 2, portid)? { - mnl::CbResult::Stop => { - break; - } - mnl::CbResult::Ok => (), + opened_ports.insert( Port { proto, number } ); + }, + _ => {} } } - Ok(()) -} -fn socket_recv<'a>(socket: &mnl::Socket, buf: &'a mut [u8]) -> Result, Error> { - let ret = socket.recv(buf)?; - if ret > 0 { - Ok(Some(&buf[..ret])) - } else { - Ok(None) - } + opened_ports } -fn add_port_allowed(port: u16) -> Result<(), Error> { - let mut batch = Batch::new(); - - // TODO: at the moment, I haven't found a way to properly separate setup part (create table and - // chains) and the add rule part because the add rule part needs a reference on the chains. - // apparently creating a table and chains that already exist seems to do nothing so it works - // doing the following. To be done properly though - - let table = Table::new(&CString::new(TABLE_NAME).unwrap(), ProtoFamily::Inet); - - batch.add(&table, nftnl::MsgType::Add); - - let mut out_chain = Chain::new(&CString::new(OUT_CHAIN_NAME).unwrap(), &table); - let mut in_chain = Chain::new(&CString::new(IN_CHAIN_NAME).unwrap(), &table); - - out_chain.set_hook(nftnl::Hook::Out, 0); - in_chain.set_hook(nftnl::Hook::In, 0); - - out_chain.set_policy(nftnl::Policy::Accept); - in_chain.set_policy(nftnl::Policy::Drop); - - batch.add(&out_chain, nftnl::MsgType::Add); - batch.add(&in_chain, nftnl::MsgType::Add); - - let mut _rule = Rule::new(&in_chain); - - _rule.add_expr(&nft_expr!(meta nfproto)); - _rule.add_expr(&nft_expr!(cmp == libc::NFPROTO_IPV4 as u8)); - - _rule.add_expr(&nft_expr!(meta l4proto)); - _rule.add_expr(&nft_expr!(cmp == libc::IPPROTO_TCP as u8)); - - _rule.add_expr(&nftnl::expr::Payload::Transport( - nftnl::expr::TransportHeaderField::Tcp(nftnl::expr::TcpHeaderField::Dport), - )); - _rule.add_expr(&nft_expr!(cmp == port.to_be())); - - _rule.add_expr(&nft_expr!(verdict accept)); +pub fn cleanup(ipt: &iptables::IPTables) { + ipt.flush_chain("filter", "DIPLONAT").unwrap(); + ipt.delete("filter", "INPUT", "-j DIPLONAT").unwrap(); + ipt.delete_chain("filter", "DIPLONAT").unwrap(); +} - batch.add(&_rule, nftnl::MsgType::Add); +/* +fn main() { + let ipt = iptables::new(false).unwrap(); + setup(&ipt); - let finalized_batch = batch.finalize(); - send_and_process(&finalized_batch)?; - - Ok(()) - + let mut test: HashSet = HashSet::new(); + test.insert(Port { proto: String::from("tcp"), number: 443 }); + let a = get_opened_ports(&ipt); + let l = test.difference(&a).collect::>(); + println!("{:?}", l); } +*/ diff --git a/src/main.rs b/src/main.rs index a35916a..bf8248d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,9 @@ mod consul; mod consul_actor; mod igd_actor; mod diplonat; +mod fw; +use iptables; use log::*; use diplonat::Diplonat; @@ -12,7 +14,10 @@ use diplonat::Diplonat; async fn main() { pretty_env_logger::init(); info!("Starting Diplonat"); - + + let ipt = iptables::new(false).unwrap(); + fw::setup(&ipt).expect("iptables setup failed"); + let mut diplo = Diplonat::new().await.expect("Setup failed"); diplo.listen().await.expect("A runtime error occured"); } -- cgit v1.2.3 From a2d25820985b04f15f3c0f38cabfd7130124d943 Mon Sep 17 00:00:00 2001 From: darkgallium Date: Fri, 22 May 2020 18:51:46 +0200 Subject: add better error handling --- src/fw.rs | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/fw.rs b/src/fw.rs index 07282af..7650b3a 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,6 +1,8 @@ use iptables; use regex::Regex; use std::collections::HashSet; +use std::io; + #[derive(PartialEq,Eq,Debug,Hash)] pub struct Port { @@ -8,22 +10,35 @@ pub struct Port { number: u16, } -pub fn setup(ipt: &iptables::IPTables) { - ipt.new_chain("filter", "DIPLONAT").unwrap(); - ipt.insert("filter", "INPUT", "-j DIPLONAT", 1).unwrap(); +#[derive(Debug)] +pub struct FirewallError(String); + +impl From for FirewallError { + fn from(error: iptables::error::IPTError) -> Self { + FirewallError(error.to_string()) + } + } -pub fn open_ports(ipt: &iptables::IPTables, ports: Vec) { +pub fn setup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { + ipt.new_chain("filter", "DIPLONAT")?; + ipt.insert("filter", "INPUT", "-j DIPLONAT", 1)?; + Ok(()) +} + +pub fn open_ports(ipt: &iptables::IPTables, ports: Vec) -> Result<(), FirewallError> { for p in ports { - ipt.append("filter", "DIPLONAT", &format!("-p {} --dport {} -j ACCEPT", p.proto, p.number)).unwrap(); + ipt.append("filter", "DIPLONAT", &format!("-p {} --dport {} -j ACCEPT", p.proto, p.number))?; } + + Ok(()) } -pub fn get_opened_ports(ipt: &iptables::IPTables) -> HashSet { +pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result, FirewallError> { let mut opened_ports: HashSet = HashSet::new(); - let list = ipt.list("filter", "DIPLONAT").unwrap(); + let list = ipt.list("filter", "DIPLONAT")?; let re = Regex::new(r"\-A.*? \-p (\w+).*\-\-dport (\d+).*?\-j ACCEPT").unwrap(); for i in list { let caps = re.captures(&i); @@ -41,13 +56,14 @@ pub fn get_opened_ports(ipt: &iptables::IPTables) -> HashSet { } } - opened_ports + Ok(opened_ports) } -pub fn cleanup(ipt: &iptables::IPTables) { - ipt.flush_chain("filter", "DIPLONAT").unwrap(); - ipt.delete("filter", "INPUT", "-j DIPLONAT").unwrap(); - ipt.delete_chain("filter", "DIPLONAT").unwrap(); +pub fn cleanup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { + ipt.flush_chain("filter", "DIPLONAT")?; + ipt.delete("filter", "INPUT", "-j DIPLONAT")?; + ipt.delete_chain("filter", "DIPLONAT")?; + Ok(()) } /* -- cgit v1.2.3 From d2ae084fc1be2671c2a301e689c8632576922785 Mon Sep 17 00:00:00 2001 From: darkgallium Date: Sun, 24 May 2020 20:40:49 +0200 Subject: add actor for firewall & massive refactor --- src/consul_actor.rs | 13 +++++---- src/diplonat.rs | 15 ++++++++-- src/fw.rs | 37 +++++++++++++++---------- src/fw_actor.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +-- src/messages.rs | 12 ++++---- 6 files changed, 129 insertions(+), 32 deletions(-) create mode 100644 src/fw_actor.rs diff --git a/src/consul_actor.rs b/src/consul_actor.rs index 1cbb1b8..ba5d704 100644 --- a/src/consul_actor.rs +++ b/src/consul_actor.rs @@ -8,11 +8,12 @@ use serde::{Serialize, Deserialize}; use serde_lexpr::{from_str,error}; use crate::messages; use crate::consul; +use std::collections::HashSet; #[derive(Serialize, Deserialize, Debug)] pub enum DiplonatParameter { - tcp_port(Vec), - udp_port(Vec) + tcp_port(HashSet), + udp_port(HashSet) } #[derive(Serialize, Deserialize, Debug)] @@ -53,8 +54,8 @@ fn to_parameters(catalog: &consul::CatalogNode) -> Vec { fn to_open_ports(params: &Vec) -> messages::PublicExposedPorts { let mut op = messages::PublicExposedPorts { - tcp_ports: Vec::new(), - udp_ports: Vec::new() + tcp_ports: HashSet::new(), + udp_ports: HashSet::new() }; for conf in params { @@ -73,8 +74,8 @@ fn to_open_ports(params: &Vec) -> messages::PublicExposedPorts { impl ConsulActor { pub fn new(url: &str, node: &str) -> Self { let (tx, rx) = watch::channel(messages::PublicExposedPorts{ - tcp_ports: Vec::new(), - udp_ports: Vec::new() + tcp_ports: HashSet::new(), + udp_ports: HashSet::new() }); return Self { diff --git a/src/diplonat.rs b/src/diplonat.rs index 1be00f7..7b7bbb8 100644 --- a/src/diplonat.rs +++ b/src/diplonat.rs @@ -4,10 +4,12 @@ use tokio::try_join; use crate::consul_actor::ConsulActor; use crate::igd_actor::IgdActor; use crate::environment::Environment; +use crate::fw_actor::FirewallActor; pub struct Diplonat { consul: ConsulActor, - igd: IgdActor + igd: IgdActor, + firewall: FirewallActor } impl Diplonat { @@ -21,9 +23,15 @@ impl Diplonat { &ca.rx_open_ports ).await?; + let fw = FirewallActor::new( + env.refresh_time, + &ca.rx_open_ports + ).await?; + let ctx = Self { consul: ca, - igd: ia + igd: ia, + firewall: fw }; return Ok(ctx); @@ -32,7 +40,8 @@ impl Diplonat { pub async fn listen(&mut self) -> Result<()> { try_join!( self.consul.listen(), - self.igd.listen() + self.igd.listen(), + self.firewall.listen() )?; return Ok(()); diff --git a/src/fw.rs b/src/fw.rs index 7650b3a..955425a 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -2,13 +2,7 @@ use iptables; use regex::Regex; use std::collections::HashSet; use std::io; - - -#[derive(PartialEq,Eq,Debug,Hash)] -pub struct Port { - proto: String, - number: u16, -} +use crate::messages; #[derive(Debug)] pub struct FirewallError(String); @@ -17,26 +11,34 @@ impl From for FirewallError { fn from(error: iptables::error::IPTError) -> Self { FirewallError(error.to_string()) } - } pub fn setup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { + ipt.new_chain("filter", "DIPLONAT")?; ipt.insert("filter", "INPUT", "-j DIPLONAT", 1)?; + Ok(()) } -pub fn open_ports(ipt: &iptables::IPTables, ports: Vec) -> Result<(), FirewallError> { +pub fn open_ports(ipt: &iptables::IPTables, ports: messages::PublicExposedPorts) -> Result<(), FirewallError> { + + for p in ports.tcp_ports { + ipt.append("filter", "DIPLONAT", &format!("-p tcp --dport {} -j ACCEPT", p))?; + } - for p in ports { - ipt.append("filter", "DIPLONAT", &format!("-p {} --dport {} -j ACCEPT", p.proto, p.number))?; + for p in ports.udp_ports { + ipt.append("filter", "DIPLONAT", &format!("-p udp --dport {} -j ACCEPT", p))?; } Ok(()) } -pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result, FirewallError> { - let mut opened_ports: HashSet = HashSet::new(); +pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result { + let mut ports = messages::PublicExposedPorts { + tcp_ports: HashSet::new(), + udp_ports: HashSet::new() + }; let list = ipt.list("filter", "DIPLONAT")?; let re = Regex::new(r"\-A.*? \-p (\w+).*\-\-dport (\d+).*?\-j ACCEPT").unwrap(); @@ -50,13 +52,18 @@ pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result, Firew let proto = String::from(raw_proto.as_str()); let number = String::from(raw_port.as_str()).parse::().unwrap(); - opened_ports.insert( Port { proto, number } ); + if proto == "tcp" { + ports.tcp_ports.insert(number); + } else { + ports.udp_ports.insert(number); + } + }, _ => {} } } - Ok(opened_ports) + Ok(ports) } pub fn cleanup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { diff --git a/src/fw_actor.rs b/src/fw_actor.rs new file mode 100644 index 0000000..9bc6610 --- /dev/null +++ b/src/fw_actor.rs @@ -0,0 +1,80 @@ +use igd::aio::*; +use igd::PortMappingProtocol; +use std::net::SocketAddrV4; +use log::*; +use anyhow::{Result, Context}; +use tokio::{ + select, + sync::watch, + time::{ + self, + Duration +}}; + +use iptables; +use crate::messages; +use crate::fw; +use std::collections::HashSet; + +pub struct FirewallActor { + ipt: iptables::IPTables, + rx_ports: watch::Receiver, + last_ports: messages::PublicExposedPorts, + refresh: Duration +} + +impl FirewallActor { + pub async fn new(_refresh: Duration, rxp: &watch::Receiver) -> Result { + + + let ctx = Self { + ipt: iptables::new(false).unwrap(), + rx_ports: rxp.clone(), + last_ports: messages::PublicExposedPorts::new(), + refresh: _refresh, + }; + + fw::setup(&ctx.ipt).expect("iptables setup failed"); + + return Ok(ctx); + } + + pub async fn listen(&mut self) -> Result<()> { + let mut interval = time::interval(self.refresh); + loop { + // 1. Wait for an event + let new_ports = select! { + Some(ports) = self.rx_ports.recv() => Some(ports), + _ = interval.tick() => None, + else => return Ok(()) // Sender dropped, terminate loop. + }; + + // 2. Update last ports if needed + if let Some(p) = new_ports { self.last_ports = p; } + + // 3. Update firewall rules + match self.do_fw_update().await { + Ok(()) => debug!("Successfully updated firewall rules"), + Err(e) => error!("An error occured while updating firewall rules. {}", e), + } + } + } + + pub async fn do_fw_update(&self) -> Result<()> { + + let curr_opened_ports = fw::get_opened_ports(&self.ipt).unwrap(); + + let diff_tcp = self.last_ports.tcp_ports.difference(&curr_opened_ports.tcp_ports).copied().collect::>(); + let diff_udp = self.last_ports.udp_ports.difference(&curr_opened_ports.udp_ports).copied().collect::>(); + + let ports_to_open = messages::PublicExposedPorts { + tcp_ports: diff_tcp, + udp_ports: diff_udp + }; + + fw::open_ports(&self.ipt, ports_to_open).unwrap(); + + return Ok(()); + } + +} diff --git a/src/main.rs b/src/main.rs index bf8248d..e845017 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod consul_actor; mod igd_actor; mod diplonat; mod fw; +mod fw_actor; use iptables; use log::*; @@ -15,9 +16,6 @@ async fn main() { pretty_env_logger::init(); info!("Starting Diplonat"); - let ipt = iptables::new(false).unwrap(); - fw::setup(&ipt).expect("iptables setup failed"); - let mut diplo = Diplonat::new().await.expect("Setup failed"); diplo.listen().await.expect("A runtime error occured"); } diff --git a/src/messages.rs b/src/messages.rs index 31ed48f..09a7c14 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -1,14 +1,16 @@ -#[derive(Debug, Clone)] +use std::collections::HashSet; + +#[derive(Debug, Clone, PartialEq, Eq)] pub struct PublicExposedPorts { - pub tcp_ports: Vec, - pub udp_ports: Vec + pub tcp_ports: HashSet, + pub udp_ports: HashSet } impl PublicExposedPorts { pub fn new() -> Self { return Self { - tcp_ports: Vec::new(), - udp_ports: Vec::new() + tcp_ports: HashSet::new(), + udp_ports: HashSet::new() } } } -- cgit v1.2.3 From 6fe86469ee9b74a8cf628ff21513a8f298a6b4b6 Mon Sep 17 00:00:00 2001 From: darkgallium Date: Sun, 28 Jun 2020 17:01:12 +0200 Subject: update README & Dockerfile --- Dockerfile | 2 +- README.md | 17 +++++++++++++++-- src/fw.rs | 12 ------------ src/fw_actor.rs | 1 + 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 41c7da9..f34dd2c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,6 @@ COPY ./src ./src RUN cargo build --release FROM debian:bullseye-slim -RUN apt-get update && apt-get install -y libssl1.1 +RUN apt-get update && apt-get install -y libssl1.1 iptables COPY --from=builder /srv/target/release/diplonat /usr/local/sbin/diplonat CMD ["/usr/local/sbin/diplonat"] diff --git a/README.md b/README.md index 518061b..3fb0f52 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Diplonat ## Feature set * [X] (Re)Configure NAT via UPNP/IGD (prio: high) - * [ ] (Re)Configure nftable (prio: low) + * [X] (Re)Configure iptables (prio: low) * [ ] (Re)Configure DNS via ??? (prio: low) ## Understand scope @@ -17,11 +17,24 @@ Diplonat ## Operate +You need to add the following to your nomad config file : + +``` +client { + [...] + + options { + docker.privileged.enabled = "true" + } +} +``` + + ```bash cargo build consul agent -dev # in a separate terminal -# adapt following values to your configuratio +# adapt following values to your configuration export DIPLONAT_PRIVATE_IP="192.168.0.18" export DIPLONAT_REFRESH_TIME="60" export DIPLONAT_EXPIRATION_TIME="300" diff --git a/src/fw.rs b/src/fw.rs index 955425a..42ce73a 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -73,15 +73,3 @@ pub fn cleanup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { Ok(()) } -/* -fn main() { - let ipt = iptables::new(false).unwrap(); - setup(&ipt); - - let mut test: HashSet = HashSet::new(); - test.insert(Port { proto: String::from("tcp"), number: 443 }); - let a = get_opened_ports(&ipt); - let l = test.difference(&a).collect::>(); - println!("{:?}", l); -} -*/ diff --git a/src/fw_actor.rs b/src/fw_actor.rs index 9bc6610..0ef08eb 100644 --- a/src/fw_actor.rs +++ b/src/fw_actor.rs @@ -78,3 +78,4 @@ impl FirewallActor { } } + -- cgit v1.2.3 From a59ed3812151410c125f62f60b00aad673fd4c66 Mon Sep 17 00:00:00 2001 From: darkgallium Date: Sun, 28 Jun 2020 18:22:23 +0200 Subject: ensure chain jump is added only once --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/diplonat.rs | 1 - src/fw.rs | 11 ++++++----- src/fw_actor.rs | 12 +++--------- src/main.rs | 1 - 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83c7c19..7a050b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1210,9 +1210,9 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.11" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdd17989496f49cdc57978c96f0c9fe5e4a58a8bddc6813c449a4624f6a030b" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" dependencies = [ "bytes 0.5.4", "fnv", diff --git a/Cargo.toml b/Cargo.toml index 6aff0de..a2a9667 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ reqwest = { version = "0.10", features = ["json"] } igd = { version = "0.10.0", features = ["aio"] } log = "0.4" pretty_env_logger = "0.4" -tokio = "0.2.11" +tokio = "0.2" futures = "0.3.5" serde = { version = "1.0.107", features = ["derive"] } serde_json = "1.0.53" diff --git a/src/diplonat.rs b/src/diplonat.rs index 7b7bbb8..798b779 100644 --- a/src/diplonat.rs +++ b/src/diplonat.rs @@ -1,5 +1,4 @@ use anyhow::Result; -use log::*; use tokio::try_join; use crate::consul_actor::ConsulActor; use crate::igd_actor::IgdActor; diff --git a/src/fw.rs b/src/fw.rs index 42ce73a..8ee3e6b 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,7 +1,6 @@ use iptables; use regex::Regex; use std::collections::HashSet; -use std::io; use crate::messages; #[derive(Debug)] @@ -14,15 +13,17 @@ impl From for FirewallError { } pub fn setup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { - - ipt.new_chain("filter", "DIPLONAT")?; - ipt.insert("filter", "INPUT", "-j DIPLONAT", 1)?; + + if !ipt.chain_exists("filter", "DIPLONAT")? { + ipt.new_chain("filter", "DIPLONAT")?; + } + + ipt.insert_unique("filter", "INPUT", "-j DIPLONAT", 1)?; Ok(()) } pub fn open_ports(ipt: &iptables::IPTables, ports: messages::PublicExposedPorts) -> Result<(), FirewallError> { - for p in ports.tcp_ports { ipt.append("filter", "DIPLONAT", &format!("-p tcp --dport {} -j ACCEPT", p))?; } diff --git a/src/fw_actor.rs b/src/fw_actor.rs index 0ef08eb..523bdaa 100644 --- a/src/fw_actor.rs +++ b/src/fw_actor.rs @@ -1,8 +1,4 @@ -use igd::aio::*; -use igd::PortMappingProtocol; -use std::net::SocketAddrV4; -use log::*; -use anyhow::{Result, Context}; +use anyhow::Result; use tokio::{ select, sync::watch, @@ -10,6 +6,7 @@ use tokio::{ self, Duration }}; +use log::*; use iptables; use crate::messages; @@ -17,7 +14,7 @@ use crate::fw; use std::collections::HashSet; pub struct FirewallActor { - ipt: iptables::IPTables, + pub ipt: iptables::IPTables, rx_ports: watch::Receiver, last_ports: messages::PublicExposedPorts, refresh: Duration @@ -25,8 +22,6 @@ pub struct FirewallActor { impl FirewallActor { pub async fn new(_refresh: Duration, rxp: &watch::Receiver) -> Result { - - let ctx = Self { ipt: iptables::new(false).unwrap(), rx_ports: rxp.clone(), @@ -61,7 +56,6 @@ impl FirewallActor { } pub async fn do_fw_update(&self) -> Result<()> { - let curr_opened_ports = fw::get_opened_ports(&self.ipt).unwrap(); let diff_tcp = self.last_ports.tcp_ports.difference(&curr_opened_ports.tcp_ports).copied().collect::>(); diff --git a/src/main.rs b/src/main.rs index e845017..ca36c26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ mod diplonat; mod fw; mod fw_actor; -use iptables; use log::*; use diplonat::Diplonat; -- cgit v1.2.3 From 4f4b6b048d53f3c4c9cc2437ba6bc6a9e70cb8c7 Mon Sep 17 00:00:00 2001 From: darkgallium Date: Sat, 4 Jul 2020 16:59:41 +0200 Subject: corrections after review --- src/fw.rs | 67 +++++++++++++++++++++++++++++++-------------------------- src/fw_actor.rs | 8 +++---- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/fw.rs b/src/fw.rs index 8ee3e6b..bc4d119 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -2,61 +2,58 @@ use iptables; use regex::Regex; use std::collections::HashSet; use crate::messages; +use anyhow::{Result,Context}; +use log::*; -#[derive(Debug)] -pub struct FirewallError(String); +pub fn setup(ipt: &iptables::IPTables) -> Result<()> { -impl From for FirewallError { - fn from(error: iptables::error::IPTError) -> Self { - FirewallError(error.to_string()) - } -} - -pub fn setup(ipt: &iptables::IPTables) -> Result<(), FirewallError> { - - if !ipt.chain_exists("filter", "DIPLONAT")? { - ipt.new_chain("filter", "DIPLONAT")?; - } - - ipt.insert_unique("filter", "INPUT", "-j DIPLONAT", 1)?; + // ensure we start from a clean state without any rule already set + cleanup(ipt)?; + + ipt.new_chain("filter", "DIPLONAT").context("Failed to create new chain")?; + ipt.insert_unique("filter", "INPUT", "-j DIPLONAT", 1).context("Failed to insert jump rule")?; Ok(()) } -pub fn open_ports(ipt: &iptables::IPTables, ports: messages::PublicExposedPorts) -> Result<(), FirewallError> { +pub fn open_ports(ipt: &iptables::IPTables, ports: messages::PublicExposedPorts) -> Result<()> { for p in ports.tcp_ports { - ipt.append("filter", "DIPLONAT", &format!("-p tcp --dport {} -j ACCEPT", p))?; + ipt.append("filter", "DIPLONAT", &format!("-p tcp --dport {} -j ACCEPT", p)).context("Failed to insert port rule")?; } for p in ports.udp_ports { - ipt.append("filter", "DIPLONAT", &format!("-p udp --dport {} -j ACCEPT", p))?; + ipt.append("filter", "DIPLONAT", &format!("-p udp --dport {} -j ACCEPT", p)).context("Failed to insert port rule")?; } Ok(()) } -pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result { +pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result { let mut ports = messages::PublicExposedPorts { tcp_ports: HashSet::new(), udp_ports: HashSet::new() }; let list = ipt.list("filter", "DIPLONAT")?; - let re = Regex::new(r"\-A.*? \-p (\w+).*\-\-dport (\d+).*?\-j ACCEPT").unwrap(); + let re = Regex::new(r"\-A.*? \-p (\w+).*\-\-dport (\d+).*?\-j ACCEPT").context("Regex matching open ports encountered an unexpected rule")?; for i in list { let caps = re.captures(&i); match caps { Some(c) => { - let raw_proto = c.get(1).unwrap(); - let raw_port = c.get(2).unwrap(); + + if let (Some(raw_proto), Some(raw_port)) = (c.get(1), c.get(2)) { + + let proto = String::from(raw_proto.as_str()); + let number = String::from(raw_port.as_str()).parse::()?; - let proto = String::from(raw_proto.as_str()); - let number = String::from(raw_port.as_str()).parse::().unwrap(); + if proto == "tcp" { + ports.tcp_ports.insert(number); + } else { + ports.udp_ports.insert(number); + } - if proto == "tcp" { - ports.tcp_ports.insert(number); } else { - ports.udp_ports.insert(number); + error!("Unexpected rule found in DIPLONAT chain") } }, @@ -67,10 +64,18 @@ pub fn get_opened_ports(ipt: &iptables::IPTables) -> Result Result<(), FirewallError> { - ipt.flush_chain("filter", "DIPLONAT")?; - ipt.delete("filter", "INPUT", "-j DIPLONAT")?; - ipt.delete_chain("filter", "DIPLONAT")?; +pub fn cleanup(ipt: &iptables::IPTables) -> Result<()> { + + if ipt.chain_exists("filter", "DIPLONAT")? { + ipt.flush_chain("filter", "DIPLONAT").context("Failed to flush the DIPLONAT chain")?; + + if ipt.exists("filter", "INPUT", "-j DIPLONAT")? { + ipt.delete("filter", "INPUT", "-j DIPLONAT").context("Failed to delete jump rule")?; + } + + ipt.delete_chain("filter", "DIPLONAT").context("Failed to delete chain")?; + } + Ok(()) } diff --git a/src/fw_actor.rs b/src/fw_actor.rs index 523bdaa..b5e4c7e 100644 --- a/src/fw_actor.rs +++ b/src/fw_actor.rs @@ -23,13 +23,13 @@ pub struct FirewallActor { impl FirewallActor { pub async fn new(_refresh: Duration, rxp: &watch::Receiver) -> Result { let ctx = Self { - ipt: iptables::new(false).unwrap(), + ipt: iptables::new(false)?, rx_ports: rxp.clone(), last_ports: messages::PublicExposedPorts::new(), refresh: _refresh, }; - fw::setup(&ctx.ipt).expect("iptables setup failed"); + fw::setup(&ctx.ipt)?; return Ok(ctx); } @@ -56,7 +56,7 @@ impl FirewallActor { } pub async fn do_fw_update(&self) -> Result<()> { - let curr_opened_ports = fw::get_opened_ports(&self.ipt).unwrap(); + let curr_opened_ports = fw::get_opened_ports(&self.ipt)?; let diff_tcp = self.last_ports.tcp_ports.difference(&curr_opened_ports.tcp_ports).copied().collect::>(); let diff_udp = self.last_ports.udp_ports.difference(&curr_opened_ports.udp_ports).copied().collect::>(); @@ -66,7 +66,7 @@ impl FirewallActor { udp_ports: diff_udp }; - fw::open_ports(&self.ipt, ports_to_open).unwrap(); + fw::open_ports(&self.ipt, ports_to_open)?; return Ok(()); } -- cgit v1.2.3