feat(credential): enforce signed credential distribution across mixed admin/shared topology (#1972)

This commit is contained in:
KKRainbow
2026-03-10 08:37:33 +08:00
committed by GitHub
parent ef44027f57
commit 694b8d349d
15 changed files with 894 additions and 186 deletions
+32 -49
View File
@@ -1,12 +1,12 @@
use std::{sync::Arc, time::Instant};
use dashmap::DashMap;
use hmac::Mac;
use prost::Message;
use snow::params::NoiseParams;
use tokio::sync::{oneshot, Mutex, OwnedMutexGuard};
use tokio::time::{timeout, Duration};
use crate::peers::foreign_network_client::ForeignNetworkClient;
use crate::{
common::error::Error,
common::{global_ctx::ArcGlobalCtx, PeerId},
@@ -43,6 +43,7 @@ impl Default for RelayPeerState {
pub struct RelayPeerMap {
peer_map: Arc<PeerMap>,
foreign_network_client: Option<Arc<ForeignNetworkClient>>,
global_ctx: ArcGlobalCtx,
my_peer_id: PeerId,
peer_session_store: Arc<PeerSessionStore>,
@@ -50,17 +51,26 @@ pub struct RelayPeerMap {
pending_handshakes: DashMap<PeerId, oneshot::Sender<ZCPacket>>,
handshake_locks: DashMap<PeerId, Arc<Mutex<()>>>,
pub(crate) pending_packets: DashMap<PeerId, Vec<(ZCPacket, NextHopPolicy)>>,
is_secure_mode_enabled: bool,
}
impl RelayPeerMap {
pub fn new(
peer_map: Arc<PeerMap>,
foreign_network_client: Option<Arc<ForeignNetworkClient>>,
global_ctx: ArcGlobalCtx,
my_peer_id: PeerId,
peer_session_store: Arc<PeerSessionStore>,
) -> Arc<Self> {
let is_secure_mode_enabled = global_ctx
.config
.get_secure_mode()
.map(|cfg| cfg.enabled)
.unwrap_or(false);
Arc::new(Self {
peer_map,
foreign_network_client,
global_ctx,
my_peer_id,
peer_session_store,
@@ -68,15 +78,12 @@ impl RelayPeerMap {
pending_handshakes: DashMap::new(),
handshake_locks: DashMap::new(),
pending_packets: DashMap::new(),
is_secure_mode_enabled,
})
}
fn is_secure_mode_enabled(&self) -> bool {
self.global_ctx
.config
.get_secure_mode()
.map(|cfg| cfg.enabled)
.unwrap_or(false)
pub fn is_secure_mode_enabled(&self) -> bool {
self.is_secure_mode_enabled
}
fn get_local_keypair(&self) -> Result<(Vec<u8>, Vec<u8>), Error> {
@@ -134,12 +141,19 @@ impl RelayPeerMap {
policy: NextHopPolicy,
) -> Result<(), Error> {
let Some(next_hop) = self.peer_map.get_gateway_peer_id(dst_peer_id, policy).await else {
return Err(Error::RouteError(None));
return Err(Error::RouteError(Some(format!(
"next hop not found in route for peer {dst_peer_id:?}"
))));
};
if !self.peer_map.has_peer(next_hop) {
return Err(Error::RouteError(None));
if self.peer_map.has_peer(next_hop) {
self.peer_map.send_msg_directly(msg, next_hop).await
} else if let Some(foreign_network_client) = &self.foreign_network_client {
foreign_network_client.send_msg(msg, next_hop).await
} else {
Err(Error::RouteError(Some(format!(
"next hop not found in direct peer map: {next_hop:?}"
))))
}
self.peer_map.send_msg_directly(msg, next_hop).await
}
pub async fn send_msg(
@@ -321,7 +335,6 @@ impl RelayPeerMap {
let a_conn_id = uuid::Uuid::new_v4();
let msg1_pb = RelayNoiseMsg1Pb {
version: RELAY_NOISE_VERSION,
a_network_name: network.network_name.clone(),
a_session_generation,
a_conn_id: Some(a_conn_id.into()),
client_encryption_algorithm: self.global_ctx.get_flags().encryption_algorithm.clone(),
@@ -331,7 +344,6 @@ impl RelayPeerMap {
let out_len = hs
.write_message(&payload, &mut out)
.map_err(|e| Error::RouteError(Some(format!("noise write msg1 failed: {e:?}"))))?;
let server_handshake_hash = hs.get_handshake_hash().to_vec();
let (tx, rx) = oneshot::channel();
self.pending_handshakes.insert(dst_peer_id, tx);
@@ -374,27 +386,6 @@ impl RelayPeerMap {
"relay msg2 conn_id_echo mismatch".to_string(),
)));
}
if msg2_pb.b_network_name == network.network_name {
if msg2_pb.role_hint != 1 {
return Err(Error::RouteError(Some(
"role_hint must be 1 when network_name is same".to_string(),
)));
}
let Some(secret_proof_32) = msg2_pb.secret_proof_32 else {
return Err(Error::RouteError(Some(
"secret_proof_32 must be present when role_hint is 1".to_string(),
)));
};
let verify_result = self
.global_ctx
.get_secret_proof(&server_handshake_hash)
.map(|mac| mac.verify_slice(&secret_proof_32).is_ok());
if verify_result != Some(true) {
return Err(Error::RouteError(Some(
"secret_proof_32 verify failed".to_string(),
)));
}
}
let action = PeerConnSessionActionPb::try_from(msg2_pb.action)
.map_err(|_| Error::RouteError(Some("invalid session action".to_string())))?;
@@ -539,7 +530,6 @@ impl RelayPeerMap {
&mut hs,
msg1,
)?;
let remote_network_name = msg1_pb.a_network_name.clone();
let remote_static = hs
.get_remote_static()
.map(|x: &[u8]| x.to_vec())
@@ -564,16 +554,6 @@ impl RelayPeerMap {
}
let server_network_name = self.global_ctx.get_network_name();
let (role_hint, secret_proof_32) = if remote_network_name == server_network_name {
let proof = self
.global_ctx
.get_secret_proof(hs.get_handshake_hash())
.map(|mac| mac.finalize().into_bytes().to_vec());
(1, proof)
} else {
(2, None)
};
let algo = self.global_ctx.get_flags().encryption_algorithm.clone();
let key = SessionKey::new(server_network_name.clone(), remote_peer_id);
let upsert = self
@@ -587,8 +567,6 @@ impl RelayPeerMap {
)
.map_err(|e| Error::RouteError(Some(format!("{e:?}"))))?;
let msg2_pb = RelayNoiseMsg2Pb {
b_network_name: server_network_name,
role_hint,
action: match upsert.action {
PeerSessionAction::Join => PeerConnSessionActionPb::Join as i32,
PeerSessionAction::Sync => PeerConnSessionActionPb::Sync as i32,
@@ -599,7 +577,6 @@ impl RelayPeerMap {
initial_epoch: upsert.initial_epoch,
b_conn_id: Some(uuid::Uuid::new_v4().into()),
a_conn_id_echo: msg1_pb.a_conn_id,
secret_proof_32,
server_encryption_algorithm: algo,
};
let payload = msg2_pb.encode_to_vec();
@@ -625,7 +602,7 @@ impl RelayPeerMap {
Ok(())
}
pub fn decrypt_if_needed(&self, packet: &mut ZCPacket) -> Result<bool, Error> {
pub async fn decrypt_if_needed(self: &Arc<Self>, packet: &mut ZCPacket) -> Result<bool, Error> {
if !self.is_secure_mode_enabled() {
return Ok(false);
}
@@ -636,6 +613,12 @@ impl RelayPeerMap {
let network = self.global_ctx.get_network_identity();
let key = SessionKey::new(network.network_name.clone(), from_peer_id);
let Some(session) = self.peer_session_store.get(&key) else {
tracing::debug!(
"relay session not found for peer {}, try handshake",
from_peer_id
);
self.ensure_session(from_peer_id, NextHopPolicy::LeastHop)
.await?;
return Ok(false);
};
let now = Instant::now();