mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-15 18:35:47 +00:00
feat(web): implement secure core-web tunnel with Noise protocol (#1976)
Implement end-to-end encryption for core-web connections using the Noise protocol framework with the following changes: Client-side (easytier/src/web_client/): - Add security.rs module with Noise handshake implementation - Add upgrade_client_tunnel() for client-side handshake - Add Noise frame encryption/decryption via TunnelFilter - Integrate GetFeature RPC for capability negotiation - Support secure_mode option to enforce encrypted connections - Handle graceful fallback for backward compatibility Server-side (easytier-web/): - Accept Noise handshake in client_manager - Expose encryption support via GetFeature RPC The implementation uses Noise_NN_25519_ChaChaPoly_SHA256 pattern for encryption without authentication. Provides backward compatibility with automatic fallback to plaintext connections.
This commit is contained in:
@@ -36,6 +36,7 @@ pub struct DefaultHooks;
|
||||
impl WebClientHooks for DefaultHooks {}
|
||||
|
||||
pub mod controller;
|
||||
pub mod security;
|
||||
pub mod session;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -52,6 +53,7 @@ impl WebClient {
|
||||
connector: T,
|
||||
token: S,
|
||||
hostname: H,
|
||||
secure_mode: bool,
|
||||
manager: Arc<NetworkInstanceManager>,
|
||||
hooks: Option<Arc<dyn WebClientHooks>>,
|
||||
) -> Self {
|
||||
@@ -68,7 +70,13 @@ impl WebClient {
|
||||
let controller_clone = controller.clone();
|
||||
let connected_clone = connected.clone();
|
||||
let tasks = ScopedTask::from(tokio::spawn(async move {
|
||||
Self::routine(controller_clone, connected_clone, Box::new(connector)).await;
|
||||
Self::routine(
|
||||
controller_clone,
|
||||
connected_clone,
|
||||
secure_mode,
|
||||
Box::new(connector),
|
||||
)
|
||||
.await;
|
||||
}));
|
||||
|
||||
WebClient {
|
||||
@@ -82,6 +90,7 @@ impl WebClient {
|
||||
async fn routine(
|
||||
controller: Arc<controller::Controller>,
|
||||
connected: Arc<AtomicBool>,
|
||||
secure_mode: bool,
|
||||
mut connector: Box<dyn TunnelConnector>,
|
||||
) {
|
||||
loop {
|
||||
@@ -99,6 +108,65 @@ impl WebClient {
|
||||
log::info!("Successfully connected to {:?}", conn.info());
|
||||
|
||||
let mut session = session::Session::new(conn, controller.clone());
|
||||
let support_encryption = match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(3),
|
||||
session.get_feature(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(feature)) => feature.support_encryption,
|
||||
Ok(Err(error)) => {
|
||||
log::warn!(%error, "GetFeature rpc failed, fallback to legacy tunnel");
|
||||
false
|
||||
}
|
||||
Err(_) => {
|
||||
log::warn!("GetFeature rpc timeout, fallback to legacy tunnel");
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if support_encryption {
|
||||
log::info!("Server supports encryption, reconnecting with secure tunnel");
|
||||
drop(session);
|
||||
|
||||
let conn = match connector.connect().await {
|
||||
Ok(conn) => conn,
|
||||
Err(error) => {
|
||||
connected.store(false, Ordering::Release);
|
||||
let wait = 1;
|
||||
log::warn!(%error, "Failed to reconnect secure tunnel, retrying in {} seconds...", wait);
|
||||
tokio::time::sleep(std::time::Duration::from_secs(wait)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let conn = match security::upgrade_client_tunnel(conn).await {
|
||||
Ok(conn) => conn,
|
||||
Err(error) => {
|
||||
connected.store(false, Ordering::Release);
|
||||
let wait = 1;
|
||||
log::warn!(%error, "Noise handshake failed, retrying in {} seconds...", wait);
|
||||
tokio::time::sleep(std::time::Duration::from_secs(wait)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let mut session = session::Session::new(conn, controller.clone());
|
||||
session.start_heartbeat().await;
|
||||
session.wait().await;
|
||||
connected.store(false, Ordering::Release);
|
||||
continue;
|
||||
}
|
||||
|
||||
if secure_mode {
|
||||
connected.store(false, Ordering::Release);
|
||||
let wait = 1;
|
||||
log::warn!("secure-mode enabled but server does not support encryption, retrying in {} seconds...", wait);
|
||||
tokio::time::sleep(std::time::Duration::from_secs(wait)).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
session.start_heartbeat().await;
|
||||
session.wait().await;
|
||||
connected.store(false, Ordering::Release);
|
||||
}
|
||||
@@ -113,6 +181,7 @@ pub async fn run_web_client(
|
||||
config_server_url_s: &str,
|
||||
machine_id: Option<String>,
|
||||
hostname: Option<String>,
|
||||
secure_mode: bool,
|
||||
manager: Arc<NetworkInstanceManager>,
|
||||
hooks: Option<Arc<dyn WebClientHooks>>,
|
||||
) -> Result<WebClient> {
|
||||
@@ -160,6 +229,7 @@ pub async fn run_web_client(
|
||||
create_connector_by_url(c_url.as_str(), &global_ctx, IpVersion::Both).await?,
|
||||
token.to_string(),
|
||||
hostname,
|
||||
secure_mode,
|
||||
manager.clone(),
|
||||
hooks,
|
||||
))
|
||||
@@ -178,6 +248,7 @@ mod tests {
|
||||
format!("ring://{}/test", uuid::Uuid::new_v4()).as_str(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
manager.clone(),
|
||||
None,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user