feat(acl): add group-based ACL rules and related structures (#1265)

* feat(acl): add group-based ACL rules and related structures

* refactor(acl): optimize group handling with Arc and improve cache management

* refactor(acl): clippy

* feat(tests): add performance tests for generate_with_proof and verify methods

* feat: update group_trust_map to use HashMap for more secure group proofs

* refactor: refactor the logic of the trusted group getting and setting

* feat(acl): support kcp/quic use group acl

* feat(proxy): optimize group retrieval by IP in Kcp and Quic proxy handlers

* feat(tests): add group-based ACL tree node test

* always allow quic proxy traffic

---------

Co-authored-by: Sijie.Sun <sunsijie@buaa.edu.cn>
Co-authored-by: sijie.sun <sijie.sun@smartx.com>
This commit is contained in:
Mg Pig
2025-08-22 22:25:00 +08:00
committed by GitHub
parent 34560af141
commit 08a92a53c3
18 changed files with 1042 additions and 29 deletions
+111 -1
View File
@@ -1,5 +1,7 @@
use std::{
collections::{BTreeMap, BTreeSet},
collections::{
HashMap, {BTreeMap, BTreeSet},
},
fmt::Debug,
net::{Ipv4Addr, Ipv6Addr},
sync::{
@@ -33,6 +35,7 @@ use crate::{
},
peers::route_trait::{Route, RouteInterfaceBox},
proto::{
acl::GroupIdentity,
common::{Ipv4Inet, NatType, StunInfo},
peer_rpc::{
route_foreign_network_infos, route_foreign_network_summary,
@@ -127,6 +130,7 @@ impl RoutePeerInfo {
network_length: 24,
quic_port: None,
ipv6_addr: None,
groups: Vec::new(),
}
}
@@ -168,6 +172,8 @@ impl RoutePeerInfo {
quic_port: global_ctx.get_quic_proxy_port().map(|x| x as u32),
ipv6_addr: global_ctx.get_ipv6().map(|x| x.into()),
groups: global_ctx.get_acl_groups(my_peer_id),
};
let need_update_periodically = if let Ok(Ok(d)) =
@@ -296,6 +302,8 @@ struct SyncedRouteInfo {
raw_peer_infos: DashMap<PeerId, DynamicMessage>,
conn_map: DashMap<PeerId, (BTreeSet<PeerId>, AtomicVersion)>,
foreign_network: DashMap<ForeignNetworkRouteInfoKey, ForeignNetworkRouteInfoEntry>,
group_trust_map: DashMap<PeerId, HashMap<String, Vec<u8>>>,
group_trust_map_cache: DashMap<PeerId, Arc<Vec<String>>>, // cache for group trust map, should sync with group_trust_map
version: AtomicVersion,
}
@@ -306,6 +314,7 @@ impl Debug for SyncedRouteInfo {
.field("peer_infos", &self.peer_infos)
.field("conn_map", &self.conn_map)
.field("foreign_network", &self.foreign_network)
.field("group_trust_map", &self.group_trust_map)
.field("version", &self.version.get())
.finish()
}
@@ -324,6 +333,8 @@ impl SyncedRouteInfo {
self.raw_peer_infos.remove(&peer_id);
self.conn_map.remove(&peer_id);
self.foreign_network.retain(|k, _| k.peer_id != peer_id);
self.group_trust_map.remove(&peer_id);
self.group_trust_map_cache.remove(&peer_id);
self.version.inc();
}
@@ -613,6 +624,85 @@ impl SyncedRouteInfo {
self.is_peer_bidirectly_connected(src_peer_id, dst_peer_id)
|| self.is_peer_bidirectly_connected(dst_peer_id, src_peer_id)
}
fn verify_and_update_group_trusts(
&self,
peer_infos: &[RoutePeerInfo],
local_group_declarations: &[GroupIdentity],
) {
let local_group_declarations = local_group_declarations
.iter()
.map(|g| (g.group_name.as_str(), g.group_secret.as_str()))
.collect::<std::collections::HashMap<&str, &str>>();
let verify_groups = |old_trusted_groups: Option<&HashMap<String, Vec<u8>>>,
info: &RoutePeerInfo|
-> HashMap<String, Vec<u8>> {
let mut trusted_groups_for_peer: HashMap<String, Vec<u8>> = HashMap::new();
for group_proof in &info.groups {
let name = &group_proof.group_name;
let proof_bytes = group_proof.group_proof.clone();
// If we already trusted this group and the proof hasn't changed, reuse it.
if old_trusted_groups
.and_then(|g| g.get(name))
.map(|old| old == &proof_bytes)
.unwrap_or(false)
{
trusted_groups_for_peer.insert(name.clone(), proof_bytes);
continue;
}
if let Some(&local_secret) =
local_group_declarations.get(group_proof.group_name.as_str())
{
if group_proof.verify(local_secret, info.peer_id) {
trusted_groups_for_peer.insert(name.clone(), proof_bytes);
} else {
tracing::warn!(
peer_id = info.peer_id,
group = %group_proof.group_name,
"Group proof verification failed"
);
}
}
}
trusted_groups_for_peer
};
for info in peer_infos {
match self.group_trust_map.entry(info.peer_id) {
dashmap::mapref::entry::Entry::Occupied(mut entry) => {
let old_trusted_groups = entry.get().clone();
let trusted_groups_for_peer = verify_groups(Some(&old_trusted_groups), info);
if trusted_groups_for_peer.is_empty() {
entry.remove();
self.group_trust_map_cache.remove(&info.peer_id);
} else {
self.group_trust_map_cache.insert(
info.peer_id,
Arc::new(trusted_groups_for_peer.keys().cloned().collect()),
);
*entry.get_mut() = trusted_groups_for_peer;
}
}
dashmap::mapref::entry::Entry::Vacant(entry) => {
let trusted_groups_for_peer = verify_groups(None, info);
if !trusted_groups_for_peer.is_empty() {
self.group_trust_map_cache.insert(
info.peer_id,
Arc::new(trusted_groups_for_peer.keys().cloned().collect()),
);
entry.insert(trusted_groups_for_peer);
}
}
}
}
}
}
type PeerGraph = Graph<PeerId, usize, Directed>;
@@ -1154,6 +1244,8 @@ impl PeerRouteServiceImpl {
raw_peer_infos: DashMap::new(),
conn_map: DashMap::new(),
foreign_network: DashMap::new(),
group_trust_map: DashMap::new(),
group_trust_map_cache: DashMap::new(),
version: AtomicVersion::new(),
},
cached_local_conn_map: std::sync::Mutex::new(RouteConnBitmap::new()),
@@ -1679,6 +1771,14 @@ impl PeerRouteServiceImpl {
fn get_peer_info_last_update(&self) -> std::time::Instant {
self.peer_info_last_update.load()
}
fn get_peer_groups(&self, peer_id: PeerId) -> Arc<Vec<String>> {
self.synced_route_info
.group_trust_map_cache
.get(&peer_id)
.map(|groups| groups.value().clone())
.unwrap_or_default()
}
}
impl Drop for PeerRouteServiceImpl {
@@ -2016,6 +2116,12 @@ impl RouteSessionManager {
peer_infos,
raw_peer_infos.as_ref().unwrap(),
)?;
service_impl
.synced_route_info
.verify_and_update_group_trusts(
peer_infos,
&service_impl.global_ctx.get_acl_group_declarations(),
);
session.update_dst_saved_peer_info_version(peer_infos);
need_update_route_table = true;
}
@@ -2364,6 +2470,10 @@ impl Route for PeerRoute {
async fn get_peer_info_last_update_time(&self) -> Instant {
self.service_impl.get_peer_info_last_update()
}
fn get_peer_groups(&self, peer_id: PeerId) -> Arc<Vec<String>> {
self.service_impl.get_peer_groups(peer_id)
}
}
impl PeerPacketFilter for Arc<PeerRoute> {}