diff --git a/bin/silius/src/cli/args.rs b/bin/silius/src/cli/args.rs index 821f9174..bce3b0b2 100644 --- a/bin/silius/src/cli/args.rs +++ b/bin/silius/src/cli/args.rs @@ -309,8 +309,13 @@ pub struct P2PArgs { /// List of whitelisted ENRs (for permissioned mempools). /// If empty, all ENRs are allowed. - #[clap(long = "p2p.whitelist", value_delimiter = ',', value_parser=parse_enr)] + #[clap(long = "p2p.whitelist-enrs", value_delimiter = ',', value_parser=parse_enr)] pub peers_whitelist: Vec, + + /// List of whitelisted IPs (for permissioned mempools). + /// If empty, all IPs are allowed. + #[clap(long = "p2p.whitelist-ips", value_delimiter = ',')] + pub ips_whitelist: Vec, } impl P2PArgs { @@ -340,6 +345,7 @@ impl P2PArgs { .chain_spec(ChainSpec::from_chain_id(chain.id())) .bootnodes(self.bootnodes.clone()) .peers_whitelist(self.peers_whitelist.clone()) + .ips_whitelist(self.ips_whitelist.clone()) .gs_config(gossipsub_config()) .discv5_config(discv5::ConfigBuilder::new(listen_addr.to_listen_config()).build()); @@ -784,7 +790,7 @@ mod tests { "~/.silius/p2p/node-key", "--nodeenr", "~/.silius/p2p/node-enr", - "--p2p.whitelist", + "--p2p.whitelist-enrs", &binding, ]; assert_eq!( @@ -798,6 +804,7 @@ mod tests { node_key: Some(PathBuf::from("~/.silius/p2p/node-key")), node_enr: Some(PathBuf::from("~/.silius/p2p/node-enr")), peers_whitelist: vec![enr], + ips_whitelist: vec![], }, P2PArgs::try_parse_from(args).unwrap() ) diff --git a/crates/p2p/src/config.rs b/crates/p2p/src/config.rs index efde1811..5fe1929b 100644 --- a/crates/p2p/src/config.rs +++ b/crates/p2p/src/config.rs @@ -10,7 +10,7 @@ use silius_primitives::{ }, }; use std::{ - net::{Ipv4Addr, Ipv6Addr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, path::PathBuf, }; @@ -60,6 +60,9 @@ pub struct Config { /// List of whitelisted peer ENRs pub peers_whitelist: Vec, + + /// List of whitelisted IP addresses + pub ips_whitelist: Vec, } impl Default for Config { @@ -89,6 +92,7 @@ impl Default for Config { target_peers: TARGET_PEERS, bootnodes: vec![], peers_whitelist: vec![], + ips_whitelist: vec![], } } } @@ -204,6 +208,12 @@ impl ConfigBuilder { self.config.peers_whitelist = peers_whitelist; self } + + /// Set the IPs whitelist. + pub fn ips_whitelist(mut self, ips_whitelist: Vec) -> Self { + self.config.ips_whitelist = ips_whitelist; + self + } } /// Create a `GossipsubConfig`. diff --git a/crates/p2p/src/peer_manager/mod.rs b/crates/p2p/src/peer_manager/mod.rs index 31c61b4e..b3112ab0 100644 --- a/crates/p2p/src/peer_manager/mod.rs +++ b/crates/p2p/src/peer_manager/mod.rs @@ -14,7 +14,7 @@ use libp2p::{Multiaddr, PeerId}; use silius_primitives::constants::p2p::{ HEARTBEAT_INTERVAL, PING_INTERVAL_INBOUND, PING_INTERVAL_OUTBOUND, TARGET_PEERS, }; -use std::{collections::VecDeque, sync::Arc, time::Duration}; +use std::{collections::VecDeque, net::IpAddr, sync::Arc, time::Duration}; use tracing::debug; /// The events that the `PeerManager` outputs (requests). @@ -57,12 +57,18 @@ pub struct PeerManager { peers_to_dial: Vec, /// The list of whitelisted ENRs. peers_whitelist: Vec, + /// The list of whitelisted IPs. + ips_whitelist: Vec, /// The heartbeat interval for peer management. heartbeat: tokio::time::Interval, } impl PeerManager { - pub fn new(network_globals: Arc, peers_whitelist: Vec) -> Self { + pub fn new( + network_globals: Arc, + peers_whitelist: Vec, + ips_whitelist: Vec, + ) -> Self { Self { network_globals, events: Default::default(), @@ -71,6 +77,7 @@ impl PeerManager { target_peers: TARGET_PEERS, peers_to_dial: Vec::new(), peers_whitelist, + ips_whitelist, heartbeat: tokio::time::interval(Duration::from_secs(HEARTBEAT_INTERVAL)), } } diff --git a/crates/p2p/src/peer_manager/network_behaviour.rs b/crates/p2p/src/peer_manager/network_behaviour.rs index 0010bd57..818f9e37 100644 --- a/crates/p2p/src/peer_manager/network_behaviour.rs +++ b/crates/p2p/src/peer_manager/network_behaviour.rs @@ -7,11 +7,11 @@ use libp2p::{ behaviour::ConnectionEstablished, dial_opts::{DialOpts, PeerCondition}, dummy::ConnectionHandler, - ConnectionClosed, DialFailure, FromSwarm, NetworkBehaviour, ToSwarm, + ConnectionClosed, ConnectionDenied, DialFailure, FromSwarm, NetworkBehaviour, ToSwarm, }, PeerId, }; -use std::task::Poll; +use std::{net::IpAddr, task::Poll}; use tracing::error; impl NetworkBehaviour for PeerManager { @@ -85,8 +85,28 @@ impl NetworkBehaviour for PeerManager { &mut self, _connection_id: libp2p::swarm::ConnectionId, _local_addr: &libp2p::Multiaddr, - _remote_addr: &libp2p::Multiaddr, + remote_addr: &libp2p::Multiaddr, ) -> Result<(), libp2p::swarm::ConnectionDenied> { + // get the IP address to verify it's whitelisted + let ip = match remote_addr.iter().next() { + Some(libp2p::multiaddr::Protocol::Ip6(ip)) => IpAddr::V6(ip), + Some(libp2p::multiaddr::Protocol::Ip4(ip)) => IpAddr::V4(ip), + _ => { + return Err(ConnectionDenied::new(format!( + "Connection to peer rejected: invalid multiaddr: {remote_addr}" + ))) + } + }; + + // check if whitelist exists and if the IP is in the whitelist + if !self.ips_whitelist.is_empty() && + self.ips_whitelist.iter().filter(|&&whitelist_ip| whitelist_ip == ip).count() == 0 + { + return Err(ConnectionDenied::new(format!( + "Connection to peer rejected: IP {ip} not in the whitelist" + ))); + } + Ok(()) } diff --git a/crates/p2p/src/service/mod.rs b/crates/p2p/src/service/mod.rs index 5a5584c4..1f2f49f1 100644 --- a/crates/p2p/src/service/mod.rs +++ b/crates/p2p/src/service/mod.rs @@ -221,8 +221,11 @@ impl Network { let rpc = RPC::new(); - let peer_manager = - PeerManager::new(network_globals.clone(), config.clone().peers_whitelist); + let peer_manager = PeerManager::new( + network_globals.clone(), + config.clone().peers_whitelist, + config.clone().ips_whitelist, + ); let mut discovery = Discovery::new(combined_key, config.clone(), network_globals.clone()).await?; diff --git a/crates/p2p/tests/common.rs b/crates/p2p/tests/common.rs index eabd2774..41c98852 100644 --- a/crates/p2p/tests/common.rs +++ b/crates/p2p/tests/common.rs @@ -60,6 +60,7 @@ async fn build_p2p_instance(bootnode: Option) -> eyre::Result { target_peers: TARGET_PEERS, bootnodes: if let Some(bootnode) = bootnode { vec![bootnode] } else { vec![] }, peers_whitelist: vec![], + ips_whitelist: vec![], }; let (_, receiver) = unbounded();