mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 08:14:42 +00:00
Merge branch 'master' into next
Integrates PR #50 (peer ACL enforcement) into the XX handshake architecture. ACL enforcement points adapted for XX's deferred identity learning: InboundHandshake check moves from handle_msg1 to handle_msg3 (responder learns initiator identity), OutboundHandshake check remains in handle_msg2 (initiator learns responder identity). Borrow scopes restructured to release connection borrows before authorize_peer calls.
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
use super::*;
|
||||
use crate::ReceivedPacket;
|
||||
use crate::node::acl::PeerAclReloader;
|
||||
use crate::node::wire::{build_msg1, build_msg2};
|
||||
use crate::utils::index::SessionIndex;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
fn make_acl_node() -> (tempfile::TempDir, Node) {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut node = Node::new(Config::new()).unwrap();
|
||||
node.peer_acl = PeerAclReloader::with_paths(
|
||||
dir.path().join("peers.allow"),
|
||||
dir.path().join("peers.deny"),
|
||||
);
|
||||
(dir, node)
|
||||
}
|
||||
|
||||
fn allow_path(dir: &tempfile::TempDir) -> PathBuf {
|
||||
dir.path().join("peers.allow")
|
||||
}
|
||||
|
||||
fn deny_path(dir: &tempfile::TempDir) -> PathBuf {
|
||||
dir.path().join("peers.deny")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_outbound_connect_denied_by_denylist() {
|
||||
let (dir, mut node) = make_acl_node();
|
||||
let denied = Identity::generate();
|
||||
std::fs::write(deny_path(&dir), format!("{}\n", denied.npub())).unwrap();
|
||||
node.reload_peer_acl();
|
||||
|
||||
let result = node
|
||||
.initiate_connection(
|
||||
TransportId::new(1),
|
||||
TransportAddr::from_string("127.0.0.1:9000"),
|
||||
PeerIdentity::from_pubkey_full(denied.pubkey_full()),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(matches!(result, Err(NodeError::AccessDenied(_))));
|
||||
assert_eq!(node.link_count(), 0);
|
||||
assert_eq!(node.connection_count(), 0);
|
||||
assert_eq!(node.peer_count(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_inbound_msg1_denied_by_acl() {
|
||||
let (dir, mut node_b) = make_acl_node();
|
||||
let node_a = make_node();
|
||||
|
||||
std::fs::write(deny_path(&dir), format!("{}\n", node_a.npub())).unwrap();
|
||||
node_b.reload_peer_acl();
|
||||
|
||||
let peer_b_identity = PeerIdentity::from_pubkey_full(node_b.identity.pubkey_full());
|
||||
let mut conn_a = PeerConnection::outbound(LinkId::new(1), peer_b_identity, 1000);
|
||||
let noise_msg1 = conn_a
|
||||
.start_handshake(node_a.identity.keypair(), node_a.startup_epoch, 1000)
|
||||
.unwrap();
|
||||
let wire_msg1 = build_msg1(SessionIndex::new(7), &noise_msg1);
|
||||
let packet = ReceivedPacket::with_timestamp(
|
||||
TransportId::new(1),
|
||||
TransportAddr::from_string("127.0.0.1:5000"),
|
||||
wire_msg1,
|
||||
1000,
|
||||
);
|
||||
|
||||
node_b.handle_msg1(packet).await;
|
||||
|
||||
assert_eq!(node_b.peer_count(), 0);
|
||||
assert_eq!(node_b.connection_count(), 0);
|
||||
assert_eq!(node_b.link_count(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_outbound_msg2_denied_after_acl_reload() {
|
||||
let (dir, mut node_a) = make_acl_node();
|
||||
let node_b = make_node();
|
||||
let transport_id = TransportId::new(1);
|
||||
let remote_addr = TransportAddr::from_string("127.0.0.1:5001");
|
||||
let peer_b_identity = PeerIdentity::from_pubkey_full(node_b.identity.pubkey_full());
|
||||
|
||||
let link_id_a = node_a.allocate_link_id();
|
||||
let mut conn_a = PeerConnection::outbound(link_id_a, peer_b_identity, 1000);
|
||||
let our_index_a = node_a.index_allocator.allocate().unwrap();
|
||||
let noise_msg1 = conn_a
|
||||
.start_handshake(node_a.identity.keypair(), node_a.startup_epoch, 1000)
|
||||
.unwrap();
|
||||
conn_a.set_our_index(our_index_a);
|
||||
conn_a.set_transport_id(transport_id);
|
||||
conn_a.set_source_addr(remote_addr.clone());
|
||||
|
||||
let link_a = Link::connectionless(
|
||||
link_id_a,
|
||||
transport_id,
|
||||
remote_addr.clone(),
|
||||
LinkDirection::Outbound,
|
||||
Duration::from_millis(100),
|
||||
);
|
||||
node_a.links.insert(link_id_a, link_a);
|
||||
node_a
|
||||
.addr_to_link
|
||||
.insert((transport_id, remote_addr.clone()), link_id_a);
|
||||
node_a.connections.insert(link_id_a, conn_a);
|
||||
node_a
|
||||
.pending_outbound
|
||||
.insert((transport_id, our_index_a.as_u32()), link_id_a);
|
||||
|
||||
let mut conn_b = PeerConnection::inbound(LinkId::new(2), 1000);
|
||||
let responder_epoch = [0x11; 8];
|
||||
let noise_msg2 = conn_b
|
||||
.receive_handshake_init(
|
||||
node_b.identity.keypair(),
|
||||
responder_epoch,
|
||||
&noise_msg1,
|
||||
1000,
|
||||
)
|
||||
.unwrap();
|
||||
let our_index_b = SessionIndex::new(9);
|
||||
let wire_msg2 = build_msg2(our_index_b, our_index_a, &noise_msg2);
|
||||
|
||||
std::fs::write(deny_path(&dir), format!("{}\n", node_b.npub())).unwrap();
|
||||
assert!(node_a.reload_peer_acl());
|
||||
|
||||
let packet = ReceivedPacket::with_timestamp(transport_id, remote_addr, wire_msg2, 1100);
|
||||
node_a.handle_msg2(packet).await;
|
||||
|
||||
assert_eq!(node_a.peer_count(), 0);
|
||||
assert_eq!(node_a.connection_count(), 0);
|
||||
assert_eq!(node_a.link_count(), 0);
|
||||
assert!(node_a.pending_outbound.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_outbound_connect_not_denied_by_allowlist_miss() {
|
||||
let (dir, mut node) = make_acl_node();
|
||||
let denied = Identity::generate();
|
||||
let allowed = Identity::generate();
|
||||
std::fs::write(allow_path(&dir), format!("{}\n", allowed.npub())).unwrap();
|
||||
node.reload_peer_acl();
|
||||
|
||||
let result = node
|
||||
.initiate_connection(
|
||||
TransportId::new(1),
|
||||
TransportAddr::from_string("127.0.0.1:9000"),
|
||||
PeerIdentity::from_pubkey_full(denied.pubkey_full()),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(!matches!(result, Err(NodeError::AccessDenied(_))));
|
||||
}
|
||||
@@ -439,6 +439,8 @@ async fn test_bloom_filter_split_horizon() {
|
||||
/// 100-node random graph: bloom filter exchange at scale.
|
||||
#[tokio::test]
|
||||
async fn test_bloom_filter_convergence_100_nodes() {
|
||||
let _guard = lock_large_network_test().await;
|
||||
|
||||
const NUM_NODES: usize = 100;
|
||||
const TARGET_EDGES: usize = 250;
|
||||
const SEED: u64 = 42;
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::node::RecentRequest;
|
||||
use crate::protocol::{LookupRequest, LookupResponse};
|
||||
use crate::tree::TreeCoordinate;
|
||||
use spanning_tree::{
|
||||
cleanup_nodes, generate_random_edges, process_available_packets, run_tree_test,
|
||||
run_tree_test_with_mtus, verify_tree_convergence,
|
||||
cleanup_nodes, generate_random_edges, lock_large_network_test, process_available_packets,
|
||||
run_tree_test, run_tree_test_with_mtus, verify_tree_convergence,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@@ -513,6 +513,8 @@ async fn test_request_dedup_convergent_paths() {
|
||||
#[tokio::test]
|
||||
#[ignore] // Long-running (~2 min): run explicitly with --ignored
|
||||
async fn test_discovery_100_nodes() {
|
||||
let _guard = lock_large_network_test().await;
|
||||
|
||||
// Set up a 100-node random topology (same seed as other 100-node tests).
|
||||
// Each node initiates lookups to a sample of other nodes in batches,
|
||||
// processing packets between batches to avoid flooding the network.
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::bloom::BloomFilter;
|
||||
use crate::tree::{ParentDeclaration, TreeCoordinate};
|
||||
use spanning_tree::{
|
||||
TestNode, cleanup_nodes, drain_all_packets, generate_random_edges, initiate_handshake,
|
||||
make_test_node, run_tree_test, verify_tree_convergence,
|
||||
lock_large_network_test, make_test_node, run_tree_test, verify_tree_convergence,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -579,6 +579,8 @@ fn simulate_forwarding(
|
||||
/// without loops.
|
||||
#[tokio::test]
|
||||
async fn test_routing_reachability_100_nodes() {
|
||||
let _guard = lock_large_network_test().await;
|
||||
|
||||
const NUM_NODES: usize = 100;
|
||||
const TARGET_EDGES: usize = 250;
|
||||
const SEED: u64 = 42;
|
||||
@@ -897,6 +899,8 @@ async fn test_routing_bloom_only_transit() {
|
||||
/// non-adjacent nodes. Direct peer adjacency handles the last hop.
|
||||
#[tokio::test]
|
||||
async fn test_routing_source_only_coords_100_nodes() {
|
||||
let _guard = lock_large_network_test().await;
|
||||
|
||||
const NUM_NODES: usize = 100;
|
||||
const TARGET_EDGES: usize = 250;
|
||||
const SEED: u64 = 42;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use super::*;
|
||||
use crate::node::session::EndToEndState;
|
||||
use crate::node::tests::spanning_tree::{
|
||||
TestNode, cleanup_nodes, generate_random_edges, process_available_packets, run_tree_test,
|
||||
run_tree_test_with_mtus, verify_tree_convergence,
|
||||
TestNode, cleanup_nodes, generate_random_edges, lock_large_network_test,
|
||||
process_available_packets, run_tree_test, run_tree_test_with_mtus, verify_tree_convergence,
|
||||
};
|
||||
use crate::protocol::{SessionAck, SessionDatagram};
|
||||
|
||||
@@ -509,6 +509,8 @@ async fn drain_to_quiescence(nodes: &mut [TestNode]) {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_session_100_nodes() {
|
||||
let _guard = lock_large_network_test().await;
|
||||
|
||||
use rand::rngs::StdRng;
|
||||
use rand::{RngExt, SeedableRng};
|
||||
use std::sync::mpsc;
|
||||
|
||||
@@ -8,6 +8,13 @@ use super::*;
|
||||
use crate::protocol::TreeAnnounce;
|
||||
use crate::tree::{CoordEntry, ParentDeclaration, TreeCoordinate};
|
||||
|
||||
static LARGE_NETWORK_TEST_LOCK: std::sync::LazyLock<tokio::sync::Mutex<()>> =
|
||||
std::sync::LazyLock::new(|| tokio::sync::Mutex::new(()));
|
||||
|
||||
pub(super) async fn lock_large_network_test() -> tokio::sync::MutexGuard<'static, ()> {
|
||||
LARGE_NETWORK_TEST_LOCK.lock().await
|
||||
}
|
||||
|
||||
/// A test node bundling a Node with its transport and packet channel.
|
||||
pub(super) struct TestNode {
|
||||
pub(super) node: Node,
|
||||
@@ -669,6 +676,8 @@ pub(super) async fn cleanup_nodes(nodes: &mut [TestNode]) {
|
||||
/// consistent spanning tree with the correct root.
|
||||
#[tokio::test]
|
||||
async fn test_spanning_tree_convergence_100_nodes() {
|
||||
let _guard = lock_large_network_test().await;
|
||||
|
||||
const NUM_NODES: usize = 100;
|
||||
const TARGET_EDGES: usize = 250;
|
||||
const SEED: u64 = 42;
|
||||
|
||||
Reference in New Issue
Block a user