From d1cbccd9ba141d567e4c23620818f03447ff7839 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 23 Apr 2020 11:46:12 -0700 Subject: [PATCH] solana-dos can now DoS gossip nodes (#9652) automerge --- core/src/gossip_service.rs | 61 ++++++++++++------- dos/src/main.rs | 4 +- gossip/src/main.rs | 29 +++++---- .../stability-testcases/gossip-dos-test.sh | 4 ++ .../stability-testcases/gossip-dos-test.yml | 2 + 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/core/src/gossip_service.rs b/core/src/gossip_service.rs index 8464fdd9aa..3b8ab46de6 100644 --- a/core/src/gossip_service.rs +++ b/core/src/gossip_service.rs @@ -63,7 +63,7 @@ impl GossipService { } } -/// Discover Nodes and Archivers in a cluster +/// Discover Validators and Archivers in a cluster pub fn discover_cluster( entrypoint: &SocketAddr, num_nodes: usize, @@ -76,16 +76,17 @@ pub fn discover_cluster( None, None, ) + .map(|(_all_peers, validators, archivers)| (validators, archivers)) } pub fn discover( entrypoint: Option<&SocketAddr>, - num_nodes: Option, + num_nodes: Option, // num_nodes only counts validators and archivers, excludes spy nodes timeout: Option, find_node_by_pubkey: Option, find_node_by_gossip_addr: Option<&SocketAddr>, my_gossip_addr: Option<&SocketAddr>, -) -> std::io::Result<(Vec, Vec)> { +) -> std::io::Result<(Vec, Vec, Vec)> { let exit = Arc::new(AtomicBool::new(false)); let (gossip_service, ip_echo, spy_ref) = make_gossip_node(entrypoint, &exit, my_gossip_addr); @@ -98,7 +99,7 @@ pub fn discover( let _ip_echo_server = ip_echo.map(solana_net_utils::ip_echo_server); - let (met_criteria, secs, tvu_peers, storage_peers) = spy( + let (met_criteria, secs, all_peers, tvu_peers, storage_peers) = spy( spy_ref.clone(), num_nodes, timeout, @@ -115,7 +116,7 @@ pub fn discover( secs, spy_ref.contact_info_trace() ); - return Ok((tvu_peers, storage_peers)); + return Ok((all_peers, tvu_peers, storage_peers)); } if !tvu_peers.is_empty() { @@ -123,7 +124,7 @@ pub fn discover( "discover failed to match criteria by timeout...\n{}", spy_ref.contact_info_trace() ); - return Ok((tvu_peers, storage_peers)); + return Ok((all_peers, tvu_peers, storage_peers)); } info!("discover failed...\n{}", spy_ref.contact_info_trace()); @@ -178,9 +179,16 @@ fn spy( timeout: Option, find_node_by_pubkey: Option, find_node_by_gossip_addr: Option<&SocketAddr>, -) -> (bool, u64, Vec, Vec) { +) -> ( + bool, + u64, + Vec, + Vec, + Vec, +) { let now = Instant::now(); let mut met_criteria = false; + let mut all_peers: Vec = Vec::new(); let mut tvu_peers: Vec = Vec::new(); let mut storage_peers: Vec = Vec::new(); let mut i = 1; @@ -191,26 +199,32 @@ fn spy( } } + all_peers = spy_ref + .all_peers() + .into_iter() + .map(|x| x.0) + .collect::>(); tvu_peers = spy_ref.all_tvu_peers().into_iter().collect::>(); storage_peers = spy_ref.all_storage_peers(); - let mut nodes: Vec<_> = tvu_peers.iter().chain(storage_peers.iter()).collect(); - nodes.sort(); - nodes.dedup(); - let found_node_by_pubkey = if let Some(pubkey) = find_node_by_pubkey { - nodes.iter().any(|x| x.id == pubkey) + all_peers.iter().any(|x| x.id == pubkey) } else { false }; let found_node_by_gossip_addr = if let Some(gossip_addr) = find_node_by_gossip_addr { - nodes.iter().any(|x| x.gossip == *gossip_addr) + all_peers.iter().any(|x| x.gossip == *gossip_addr) } else { false }; if let Some(num) = num_nodes { + // Only consider validators and archives for `num_nodes` + let mut nodes: Vec<_> = tvu_peers.iter().chain(storage_peers.iter()).collect(); + nodes.sort(); + nodes.dedup(); + if nodes.len() >= num { if found_node_by_pubkey || found_node_by_gossip_addr { met_criteria = true; @@ -234,6 +248,7 @@ fn spy( ( met_criteria, now.elapsed().as_secs(), + all_peers, tvu_peers, storage_peers, ) @@ -295,21 +310,21 @@ mod tests { let spy_ref = Arc::new(cluster_info); - let (met_criteria, secs, tvu_peers, _) = spy(spy_ref.clone(), None, Some(1), None, None); + let (met_criteria, secs, _, tvu_peers, _) = spy(spy_ref.clone(), None, Some(1), None, None); assert_eq!(met_criteria, false); assert_eq!(secs, 1); assert_eq!(tvu_peers, spy_ref.tvu_peers()); // Find num_nodes - let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(1), None, None, None); + let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(1), None, None, None); assert_eq!(met_criteria, true); - let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(2), None, None, None); + let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(2), None, None, None); assert_eq!(met_criteria, true); // Find specific node by pubkey - let (met_criteria, _, _, _) = spy(spy_ref.clone(), None, None, Some(peer0), None); + let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), None, None, Some(peer0), None); assert_eq!(met_criteria, true); - let (met_criteria, _, _, _) = spy( + let (met_criteria, _, _, _, _) = spy( spy_ref.clone(), None, Some(0), @@ -319,11 +334,11 @@ mod tests { assert_eq!(met_criteria, false); // Find num_nodes *and* specific node by pubkey - let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(1), None, Some(peer0), None); + let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(1), None, Some(peer0), None); assert_eq!(met_criteria, true); - let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(3), Some(0), Some(peer0), None); + let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(3), Some(0), Some(peer0), None); assert_eq!(met_criteria, false); - let (met_criteria, _, _, _) = spy( + let (met_criteria, _, _, _, _) = spy( spy_ref.clone(), Some(1), Some(0), @@ -333,11 +348,11 @@ mod tests { assert_eq!(met_criteria, false); // Find specific node by gossip address - let (met_criteria, _, _, _) = + let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), None, None, None, Some(&peer0_info.gossip)); assert_eq!(met_criteria, true); - let (met_criteria, _, _, _) = spy( + let (met_criteria, _, _, _, _) = spy( spy_ref.clone(), None, Some(0), diff --git a/dos/src/main.rs b/dos/src/main.rs index dec1c31a3e..9576c06f5c 100644 --- a/dos/src/main.rs +++ b/dos/src/main.rs @@ -91,7 +91,7 @@ fn run_dos( } fn main() { - solana_logger::setup(); + solana_logger::setup_with_default("solana=info"); let matches = App::new(crate_name!()) .about(crate_description!()) .version(solana_clap_utils::version!()) @@ -148,7 +148,7 @@ fn main() { let data_type = value_t_or_exit!(matches, "data_type", String); info!("Finding cluster entry: {:?}", entrypoint_addr); - let (nodes, _archivers) = discover( + let (nodes, _validators, _archivers) = discover( Some(&entrypoint_addr), None, Some(60), diff --git a/gossip/src/main.rs b/gossip/src/main.rs index e390eb7b04..ebd2f9778f 100644 --- a/gossip/src/main.rs +++ b/gossip/src/main.rs @@ -205,7 +205,7 @@ fn main() -> Result<(), Box> { }), ); - let (nodes, _archivers) = discover( + let (_all_peers, validators, _archivers) = discover( entrypoint_addr.as_ref(), num_nodes, timeout, @@ -216,28 +216,28 @@ fn main() -> Result<(), Box> { if timeout.is_some() { if let Some(num) = num_nodes { - if nodes.len() < num { + if validators.len() < num { let add = if num_nodes_exactly.is_some() { "" } else { " or more" }; eprintln!( - "Error: Insufficient nodes discovered. Expecting {}{}", + "Error: Insufficient validators discovered. Expecting {}{}", num, add, ); exit(1); } } if let Some(node) = pubkey { - if nodes.iter().find(|x| x.id == node).is_none() { + if validators.iter().find(|x| x.id == node).is_none() { eprintln!("Error: Could not find node {:?}", node); exit(1); } } } if let Some(num_nodes_exactly) = num_nodes_exactly { - if nodes.len() > num_nodes_exactly { + if validators.len() > num_nodes_exactly { eprintln!( "Error: Extra nodes discovered. Expecting exactly {}", num_nodes_exactly @@ -251,7 +251,7 @@ fn main() -> Result<(), Box> { let all = matches.is_present("all"); let entrypoint_addr = parse_entrypoint(&matches); let timeout = value_t_or_exit!(matches, "timeout", u64); - let (nodes, _archivers) = discover( + let (_all_peers, validators, _archivers) = discover( entrypoint_addr.as_ref(), Some(1), Some(timeout), @@ -260,7 +260,7 @@ fn main() -> Result<(), Box> { None, )?; - let rpc_addrs: Vec<_> = nodes + let rpc_addrs: Vec<_> = validators .iter() .filter_map(|contact_info| { if (any || all || Some(contact_info.gossip) == entrypoint_addr) @@ -291,7 +291,7 @@ fn main() -> Result<(), Box> { .unwrap() .parse::() .unwrap(); - let (nodes, _archivers) = discover( + let (_all_peers, validators, _archivers) = discover( entrypoint_addr.as_ref(), None, None, @@ -299,15 +299,18 @@ fn main() -> Result<(), Box> { None, None, )?; - let node = nodes.iter().find(|x| x.id == pubkey).unwrap(); + let validator = validators.iter().find(|x| x.id == pubkey).unwrap(); - if !ContactInfo::is_valid_address(&node.rpc) { - eprintln!("Error: RPC service is not enabled on node {:?}", pubkey); + if !ContactInfo::is_valid_address(&validator.rpc) { + eprintln!( + "Error: RPC service is not enabled on validator {:?}", + pubkey + ); exit(1); } - println!("\nSending stop request to node {:?}", pubkey); + println!("\nSending stop request to validator {:?}", pubkey); - let result = RpcClient::new_socket(node.rpc).validator_exit()?; + let result = RpcClient::new_socket(validator.rpc).validator_exit()?; if result { println!("Stop signal accepted"); } else { diff --git a/system-test/stability-testcases/gossip-dos-test.sh b/system-test/stability-testcases/gossip-dos-test.sh index e367aa1953..f8afade75d 100755 --- a/system-test/stability-testcases/gossip-dos-test.sh +++ b/system-test/stability-testcases/gossip-dos-test.sh @@ -25,6 +25,10 @@ bootstrapInstall() { } bootstrapInstall "edge" +solana-install-init --version +solana-install-init edge +solana-gossip --version +solana-dos --version killall solana-gossip || true solana-gossip spy --gossip-port 8001 > "$logDir"/gossip.log 2>&1 & diff --git a/system-test/stability-testcases/gossip-dos-test.yml b/system-test/stability-testcases/gossip-dos-test.yml index 95ab5dae1d..46e2711d05 100644 --- a/system-test/stability-testcases/gossip-dos-test.yml +++ b/system-test/stability-testcases/gossip-dos-test.yml @@ -1,3 +1,5 @@ steps: - command: "system-test/stability-testcases/gossip-dos-test.sh" label: "Gossip DoS Test" + artifact_paths: + - "system-test/stability-testcases/logs/*"