From 0da09ec605bc38b2ae65eb5e3ff6c71a9a9e371c Mon Sep 17 00:00:00 2001 From: "sijie.sun" Date: Sat, 3 Aug 2024 15:12:54 +0800 Subject: [PATCH] add network whitelist for public relay node --- Cargo.lock | 7 +++++ easytier/Cargo.toml | 2 ++ easytier/src/common/config.rs | 2 ++ easytier/src/connector/udp_hole_punch.rs | 1 - easytier/src/easytier-core.rs | 19 ++++++++++-- easytier/src/peers/foreign_network_manager.rs | 31 +++++++++++++++++++ easytier/src/peers/peer_manager.rs | 16 ++++++++++ 7 files changed, 75 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41a1d84..6c5f2f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1425,6 +1425,7 @@ dependencies = [ "tun-easytier", "url", "uuid", + "wildmatch", "windows-sys 0.52.0", "winreg 0.11.0", "zerocopy", @@ -6801,6 +6802,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "wildmatch" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19" + [[package]] name = "winapi" version = "0.3.9" diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index 59194b0..5cdd95d 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -166,6 +166,8 @@ smoltcp = { version = "0.11.0", optional = true, default-features = false, featu ] } parking_lot = { version = "0.12.0", optional = true } +wildmatch = "2.3.4" + [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = [ "Win32_Networking_WinSock", diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 758ad34..9ff51df 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -169,6 +169,8 @@ pub struct Flags { pub no_tun: bool, #[derivative(Default(value = "false"))] pub use_smoltcp: bool, + #[derivative(Default(value = "\"*\".to_string()"))] + pub foreign_network_whitelist: String, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] diff --git a/easytier/src/connector/udp_hole_punch.rs b/easytier/src/connector/udp_hole_punch.rs index 4a35345..fa3c7a7 100644 --- a/easytier/src/connector/udp_hole_punch.rs +++ b/easytier/src/connector/udp_hole_punch.rs @@ -1007,7 +1007,6 @@ pub mod tests { use tokio::net::UdpSocket; - use crate::connector::udp_hole_punch::UdpHolePunchListener; use crate::rpc::{NatType, StunInfo}; use crate::tunnel::common::tests::wait_for_condition; diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 46808dc..e975b13 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -79,7 +79,10 @@ struct Cli { #[arg( short, long, - help = "automatically determine and set IP address by Easytier, and the IP address starts from 10.0.0.1 by default. Warning, if there is an IP conflict in the network when using DHCP, the IP will be automatically changed." + help = "automatically determine and set IP address by Easytier, and the +IP address starts from 10.0.0.1 by default. Warning, if there is an IP +conflict in the network when using DHCP, the IP will be automatically +changed." )] dhcp: bool, @@ -219,10 +222,19 @@ and the vpn client is in network of 10.14.14.0/24" #[arg( long, help = "assign routes cidr manually, will disable subnet proxy and - wireguard routes propogated from peers. e.g.: 192.168.0.0/16", +wireguard routes propogated from peers. e.g.: 192.168.0.0/16", num_args = 0.. )] manual_routes: Option>, + + #[arg( + long, + help = "only relay traffic of whitelisted networks, input is a wildcard +string, e.g.: '*' (all networks), 'def*' (network prefixed with def), can specify multiple networks +disable relay if arg is empty. default is allowing all networks", + num_args = 0.., + )] + relay_network_whitelist: Option>, } impl Cli { @@ -455,6 +467,9 @@ impl From for TomlConfigLoader { f.enable_exit_node = cli.enable_exit_node; f.no_tun = cli.no_tun || cfg!(not(feature = "tun")); f.use_smoltcp = cli.use_smoltcp; + if let Some(wl) = cli.relay_network_whitelist { + f.foreign_network_whitelist = wl.join(" "); + } cfg.set_flags(f); cfg.set_exit_nodes(cli.exit_nodes.clone()); diff --git a/easytier/src/peers/foreign_network_manager.rs b/easytier/src/peers/foreign_network_manager.rs index beecaa3..099feb2 100644 --- a/easytier/src/peers/foreign_network_manager.rs +++ b/easytier/src/peers/foreign_network_manager.rs @@ -399,6 +399,37 @@ mod tests { assert_eq!(2, rpc_resp.foreign_networks["net1"].peers.len()); } + async fn foreign_network_whitelist_helper(name: String) { + let pm_center = create_mock_peer_manager_with_mock_stun(crate::rpc::NatType::Unknown).await; + tracing::debug!("pm_center: {:?}", pm_center.my_peer_id()); + let mut flag = pm_center.get_global_ctx().get_flags(); + flag.foreign_network_whitelist = vec!["net1".to_string(), "net2*".to_string()].join(" "); + pm_center.get_global_ctx().config.set_flags(flag); + + let pma_net1 = create_mock_peer_manager_for_foreign_network(name.as_str()).await; + + let (a_ring, b_ring) = crate::tunnel::ring::create_ring_tunnel_pair(); + let b_mgr_copy = pm_center.clone(); + let s_ret = tokio::spawn(async move { b_mgr_copy.add_tunnel_as_server(b_ring).await }); + + pma_net1.add_client_tunnel(a_ring).await.unwrap(); + + s_ret.await.unwrap().unwrap(); + } + + #[tokio::test] + async fn foreign_network_whitelist() { + foreign_network_whitelist_helper("net1".to_string()).await; + foreign_network_whitelist_helper("net2".to_string()).await; + foreign_network_whitelist_helper("net2abc".to_string()).await; + } + + #[tokio::test] + #[should_panic] + async fn foreign_network_whitelist_fail() { + foreign_network_whitelist_helper("net3".to_string()).await; + } + #[tokio::test] async fn test_foreign_network_manager() { let pm_center = create_mock_peer_manager_with_mock_stun(crate::rpc::NatType::Unknown).await; diff --git a/easytier/src/peers/peer_manager.rs b/easytier/src/peers/peer_manager.rs index f10fba6..392bcdd 100644 --- a/easytier/src/peers/peer_manager.rs +++ b/easytier/src/peers/peer_manager.rs @@ -309,6 +309,21 @@ impl PeerManager { self.add_client_tunnel(t).await } + fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), Error> { + if self + .global_ctx + .get_flags() + .foreign_network_whitelist + .split(" ") + .map(wildmatch::WildMatch::new) + .any(|wl| wl.matches(network_name)) + { + Ok(()) + } else { + Err(anyhow::anyhow!("network {} not in whitelist", network_name).into()) + } + } + #[tracing::instrument] pub async fn add_tunnel_as_server(&self, tunnel: Box) -> Result<(), Error> { tracing::info!("add tunnel as server start"); @@ -319,6 +334,7 @@ impl PeerManager { { self.add_new_peer_conn(peer).await?; } else { + self.check_network_in_whitelist(&peer.get_network_identity().network_name)?; self.foreign_network_manager.add_peer_conn(peer).await?; } tracing::info!("add tunnel as server done");