mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 16:43:12 +00:00
node: migrate peer ACL to the Reloadable trait and hot-reload the host map
Move PeerAclReloader onto the Reloadable trait: its ACL snapshot is now published through an arc_swap::ArcSwap so the authorization hot path reads it without locking, and the former check_reload becomes the trait's reload(). The node tick calls self.peer_acl.reload().await. Wire the host map into the tick as well. The host map snapshot was previously taken once at construction and never polled; it now hot-reloads on /etc/fips/hosts mtime changes once per tick, alongside the ACL, so hostname display reflects edits without a restart. The path_mtu_lookup cache (event-driven, populated from observed traffic) and the nostr_discovery subsystem (an async spawned task) are deliberately left off the trait: neither reloads from a backing file, so a no-op reload() would be misleading. The rationale is documented on the trait module. The host map and the ACL's embedded alias reloader still stat /etc/fips/hosts independently each tick. A single small-file stat per tick is cheap, so the duplicate is left in place; sharing one mtime observation between the two is a possible future cleanup. Tests: a node-level test exercises the host-map tick reload end to end through peer_display_name; the ACL reloader tests are updated to drive the async reload().
This commit is contained in:
+32
-4
@@ -1,7 +1,9 @@
|
||||
use super::*;
|
||||
use crate::ReceivedPacket;
|
||||
use crate::node::acl::PeerAclReloader;
|
||||
use crate::node::reloadable::HostMapReloadable;
|
||||
use crate::node::wire::{build_msg1, build_msg2};
|
||||
use crate::upper::hosts::HostMap;
|
||||
use crate::utils::index::SessionIndex;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
@@ -29,7 +31,7 @@ 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();
|
||||
node.reload_peer_acl().await;
|
||||
|
||||
let result = node
|
||||
.initiate_connection(
|
||||
@@ -51,7 +53,7 @@ async fn test_inbound_msg1_denied_by_acl() {
|
||||
let node_a = make_node();
|
||||
|
||||
std::fs::write(deny_path(&dir), format!("{}\n", node_a.npub())).unwrap();
|
||||
node_b.reload_peer_acl();
|
||||
node_b.reload_peer_acl().await;
|
||||
|
||||
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);
|
||||
@@ -121,7 +123,7 @@ async fn test_outbound_msg2_denied_after_acl_reload() {
|
||||
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());
|
||||
assert!(node_a.reload_peer_acl().await);
|
||||
|
||||
let packet = ReceivedPacket::with_timestamp(transport_id, remote_addr, wire_msg2, 1100);
|
||||
node_a.handle_msg2(packet).await;
|
||||
@@ -132,13 +134,39 @@ async fn test_outbound_msg2_denied_after_acl_reload() {
|
||||
assert!(node_a.pending_outbound.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_host_map_hot_reloads_from_tick() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let hosts_path = dir.path().join("hosts");
|
||||
|
||||
let mut node = Node::new(Config::new()).unwrap();
|
||||
node.host_map = HostMapReloadable::new(HostMap::new(), hosts_path.clone());
|
||||
|
||||
let peer = Identity::generate();
|
||||
let peer_addr = *PeerIdentity::from_pubkey_full(peer.pubkey_full()).node_addr();
|
||||
|
||||
// No hosts file yet: the display name is not the alias.
|
||||
assert_ne!(node.peer_display_name(&peer_addr), "gateway");
|
||||
assert!(!node.reload_host_map().await);
|
||||
|
||||
// Write a hosts entry and let the tick-driven reload pick it up.
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
std::fs::write(&hosts_path, format!("gateway {}\n", peer.npub())).unwrap();
|
||||
|
||||
assert!(node.reload_host_map().await);
|
||||
assert_eq!(node.peer_display_name(&peer_addr), "gateway");
|
||||
|
||||
// No further change: reload reports nothing replaced.
|
||||
assert!(!node.reload_host_map().await);
|
||||
}
|
||||
|
||||
#[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();
|
||||
node.reload_peer_acl().await;
|
||||
|
||||
let result = node
|
||||
.initiate_connection(
|
||||
|
||||
Reference in New Issue
Block a user