From 70fa41c165c2bb6fe28f9c0752260e196f25e845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Fri, 7 Jun 2024 14:02:47 +0300 Subject: [PATCH] Update kernel peers on peer modification (#4622) * Include wireguard in gw config * Support nym node first * Create wg keypair * Activate wg feature in gw dep * Move key paths to separate structure * Use client reg * Generate and use own private key * Rename network to ip for wg gw * Propagate wireguard setup error message * Remove logs * Bump gateway version number * Remove upgrade code * Init wireguard on migration * Upgrade code for nym-node too * Wireguard paths upgrade * Init wg keys on upgrade * Simplify pub key translatations * Fix clippy * Undo comment change * Fix tests * Don't bump version just yet * Remove redundant source attr * Remove unused wg details * Rename wg device * Init for mixnode migration as well * Add upgrade for gw wireguard deleted field * Move interface removal to Drop trait impl * Fix clippy * Wgapi could be included on other platforms * Handle peer control msgs * Send add peer msg * Handle double registration * Fix deadlock * Fix clippy * More clippy * Use same defguard * Fix unit test * Fix wasm build * Move implementations from types to wireguard crate * Fix linux --- Cargo.lock | 3 + common/wireguard-types/src/error.rs | 3 + common/wireguard-types/src/lib.rs | 33 ---- common/wireguard-types/src/registration.rs | 20 ++- common/wireguard/Cargo.toml | 2 + common/wireguard/src/lib.rs | 104 +++++++++-- common/wireguard/src/peer_controller.rs | 58 +++++++ gateway/src/http/mod.rs | 13 -- gateway/src/node/mod.rs | 24 ++- nym-node/Cargo.toml | 1 + nym-node/nym-node-http-api/Cargo.toml | 2 - .../wireguard/client_registry.rs | 164 +++++++++++------- .../client_interfaces/wireguard/mod.rs | 40 +++-- nym-node/src/node/mod.rs | 58 ++++--- 14 files changed, 335 insertions(+), 190 deletions(-) create mode 100644 common/wireguard/src/peer_controller.rs diff --git a/Cargo.lock b/Cargo.lock index 87cc6514c2..b06aeeec7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5031,6 +5031,7 @@ dependencies = [ "nym-sphinx-addressing", "nym-task", "nym-types", + "nym-wireguard", "nym-wireguard-types", "rand 0.8.5", "semver 1.0.23", @@ -5773,9 +5774,11 @@ name = "nym-wireguard" version = "0.1.0" dependencies = [ "base64 0.21.7", + "dashmap", "defguard_wireguard_rs", "ip_network", "log", + "nym-crypto", "nym-network-defaults", "nym-task", "nym-wireguard-types", diff --git a/common/wireguard-types/src/error.rs b/common/wireguard-types/src/error.rs index 0bd4a8d3f6..3584a056a6 100644 --- a/common/wireguard-types/src/error.rs +++ b/common/wireguard-types/src/error.rs @@ -32,4 +32,7 @@ pub enum Error { #[source] source: hmac::digest::MacError, }, + + #[error("peers can't be modified anymore")] + PeerModifyStopped, } diff --git a/common/wireguard-types/src/lib.rs b/common/wireguard-types/src/lib.rs index cf190c55bc..9c42c798bb 100644 --- a/common/wireguard-types/src/lib.rs +++ b/common/wireguard-types/src/lib.rs @@ -1,10 +1,6 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use dashmap::DashMap; -use nym_crypto::asymmetric::encryption::KeyPair; -use std::sync::Arc; - pub mod config; pub mod error; pub mod public_key; @@ -20,32 +16,3 @@ pub use registration::{ #[cfg(feature = "verify")] pub use registration::HmacSha256; - -#[derive(Clone)] -pub struct WireguardGatewayData { - config: Config, - keypair: Arc, - client_registry: Arc, -} - -impl WireguardGatewayData { - pub fn new(config: Config, keypair: Arc) -> Self { - WireguardGatewayData { - config, - keypair, - client_registry: Arc::new(DashMap::default()), - } - } - - pub fn config(&self) -> Config { - self.config - } - - pub fn keypair(&self) -> &Arc { - &self.keypair - } - - pub fn client_registry(&self) -> &Arc { - &self.client_registry - } -} diff --git a/common/wireguard-types/src/registration.rs b/common/wireguard-types/src/registration.rs index e921077ba8..e1c2d67114 100644 --- a/common/wireguard-types/src/registration.rs +++ b/common/wireguard-types/src/registration.rs @@ -17,7 +17,7 @@ use nym_crypto::asymmetric::encryption::PrivateKey; use sha2::Sha256; pub type GatewayClientRegistry = DashMap; -pub type PendingRegistrations = DashMap; +pub type PendingRegistrations = DashMap; pub type PrivateIPs = DashMap; #[cfg(feature = "verify")] @@ -56,14 +56,16 @@ impl InitMessage { #[serde(tag = "type", rename_all = "camelCase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum ClientRegistrationResponse { - PendingRegistration { - nonce: u64, - gateway_data: GatewayClient, - wg_port: u16, - }, - Registered { - success: bool, - }, + PendingRegistration(RegistrationData), + Registered, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +pub struct RegistrationData { + pub nonce: u64, + pub gateway_data: GatewayClient, + pub wg_port: u16, } /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. diff --git a/common/wireguard/Cargo.toml b/common/wireguard/Cargo.toml index 0ef630aa23..50ea7573b6 100644 --- a/common/wireguard/Cargo.toml +++ b/common/wireguard/Cargo.toml @@ -12,6 +12,7 @@ license.workspace = true [dependencies] base64 = "0.21.3" +dashmap = { workspace = true } defguard_wireguard_rs = { workspace = true } # The latest version on crates.io at the time of writing this (6.0.0) has a # version mismatch with x25519-dalek/curve25519-dalek that is resolved in the @@ -19,6 +20,7 @@ defguard_wireguard_rs = { workspace = true } x25519-dalek = "2.0.0" ip_network = { workspace = true } log.workspace = true +nym-crypto = { path = "../crypto", features = ["asymmetric"] } nym-network-defaults = { path = "../network-defaults" } nym-task = { path = "../task" } nym-wireguard-types = { path = "../wireguard-types" } diff --git a/common/wireguard/src/lib.rs b/common/wireguard/src/lib.rs index 2eb3d2760c..6a6ee01682 100644 --- a/common/wireguard/src/lib.rs +++ b/common/wireguard/src/lib.rs @@ -3,42 +3,106 @@ // #![warn(clippy::expect_used)] // #![warn(clippy::unwrap_used)] -use defguard_wireguard_rs::WGApi; +use dashmap::DashMap; +use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask, WGApi}; +use nym_crypto::asymmetric::encryption::KeyPair; +use nym_wireguard_types::{Config, Error, GatewayClient, GatewayClientRegistry}; +use peer_controller::PeerControlMessage; +use std::sync::Arc; +use tokio::sync::mpsc::{self, UnboundedReceiver}; const WG_TUN_NAME: &str = "nymwg"; +pub mod peer_controller; + pub struct WgApiWrapper { - wg_api: WGApi, + inner: WGApi, } impl WgApiWrapper { pub fn new(wg_api: WGApi) -> Self { - WgApiWrapper { wg_api } + WgApiWrapper { inner: wg_api } } } impl Drop for WgApiWrapper { fn drop(&mut self) { - if let Err(e) = defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&self.wg_api) + if let Err(e) = defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&self.inner) { log::error!("Could not remove the wireguard interface: {:?}", e); } } } +#[derive(Clone)] +pub struct WireguardGatewayData { + config: Config, + keypair: Arc, + client_registry: Arc, + peer_tx: mpsc::UnboundedSender, +} + +impl WireguardGatewayData { + pub fn new( + config: Config, + keypair: Arc, + ) -> (Self, mpsc::UnboundedReceiver) { + let (peer_tx, peer_rx) = mpsc::unbounded_channel(); + ( + WireguardGatewayData { + config, + keypair, + client_registry: Arc::new(DashMap::default()), + peer_tx, + }, + peer_rx, + ) + } + + pub fn config(&self) -> Config { + self.config + } + + pub fn keypair(&self) -> &Arc { + &self.keypair + } + + pub fn client_registry(&self) -> &Arc { + &self.client_registry + } + + pub fn add_peer(&self, client: &GatewayClient) -> Result<(), Error> { + let mut peer = Peer::new(Key::new(client.pub_key.to_bytes())); + peer.allowed_ips + .push(IpAddrMask::new(client.private_ip, 32)); + let msg = PeerControlMessage::AddPeer(peer); + self.peer_tx.send(msg).map_err(|_| Error::PeerModifyStopped) + } + + pub fn remove_peer(&self, client: &GatewayClient) -> Result<(), Error> { + let key = Key::new(client.pub_key().to_bytes()); + let msg = PeerControlMessage::RemovePeer(key); + self.peer_tx.send(msg).map_err(|_| Error::PeerModifyStopped) + } +} + +pub struct WireguardData { + pub inner: WireguardGatewayData, + pub peer_rx: UnboundedReceiver, +} + /// Start wireguard device #[cfg(target_os = "linux")] pub async fn start_wireguard( - mut task_client: nym_task::TaskClient, - wireguard_data: std::sync::Arc, -) -> Result> { + task_client: nym_task::TaskClient, + wireguard_data: WireguardData, +) -> Result, Box> { use base64::{prelude::BASE64_STANDARD, Engine}; - use defguard_wireguard_rs::{ - host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WireguardInterfaceApi, - }; + use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi}; + use peer_controller::PeerController; let mut peers = vec![]; - for peer_client in wireguard_data.client_registry().iter() { + for peer_client in wireguard_data.inner.client_registry().iter() { let mut peer = Peer::new(Key::new(peer_client.pub_key.to_bytes())); let peer_ip_mask = IpAddrMask::new(peer_client.private_ip, 32); peer.set_allowed_ips(vec![peer_ip_mask]); @@ -46,21 +110,23 @@ pub async fn start_wireguard( } let ifname = String::from(WG_TUN_NAME); - let wgapi = WGApi::new(ifname.clone(), false)?; - wgapi.create_interface()?; + let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?; + wg_api.create_interface()?; let interface_config = InterfaceConfiguration { name: ifname.clone(), - prvkey: BASE64_STANDARD.encode(wireguard_data.keypair().private_key().to_bytes()), - address: wireguard_data.config().private_ip.to_string(), - port: wireguard_data.config().announced_port as u32, + prvkey: BASE64_STANDARD.encode(wireguard_data.inner.keypair().private_key().to_bytes()), + address: wireguard_data.inner.config().private_ip.to_string(), + port: wireguard_data.inner.config().announced_port as u32, peers, }; - wgapi.configure_interface(&interface_config)?; + wg_api.configure_interface(&interface_config)?; // wgapi.configure_peer_routing(&peers)?; - tokio::spawn(async move { task_client.recv().await }); + let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api)); + let mut controller = PeerController::new(wg_api.clone(), wireguard_data.peer_rx); + tokio::spawn(async move { controller.run(task_client).await }); - Ok(WgApiWrapper::new(wgapi)) + Ok(wg_api) } #[cfg(not(target_os = "linux"))] diff --git a/common/wireguard/src/peer_controller.rs b/common/wireguard/src/peer_controller.rs new file mode 100644 index 0000000000..a25e5a7f61 --- /dev/null +++ b/common/wireguard/src/peer_controller.rs @@ -0,0 +1,58 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::Arc; + +use defguard_wireguard_rs::{host::Peer, key::Key, WireguardInterfaceApi}; +use tokio::sync::mpsc; + +use crate::WgApiWrapper; + +pub enum PeerControlMessage { + AddPeer(Peer), + RemovePeer(Key), +} + +pub struct PeerController { + peer_rx: mpsc::UnboundedReceiver, + wg_api: Arc, +} + +impl PeerController { + pub fn new( + wg_api: Arc, + peer_rx: mpsc::UnboundedReceiver, + ) -> Self { + PeerController { wg_api, peer_rx } + } + + pub async fn run(&mut self, mut task_client: nym_task::TaskClient) { + loop { + tokio::select! { + _ = task_client.recv() => { + log::trace!("PeerController handler: Received shutdown"); + break; + } + msg = self.peer_rx.recv() => { + match msg { + Some(PeerControlMessage::AddPeer(peer)) => { + if self.wg_api.inner.configure_peer(&peer).is_err() { + log::error!("Could not configure peer {:?}", peer); + } + } + Some(PeerControlMessage::RemovePeer(peer_pubkey)) => { + if self.wg_api.inner.remove_peer(&peer_pubkey).is_err() { + log::error!("Could not remove peer with key {:?}", peer_pubkey); + } + } + None => { + log::trace!("PeerController [main loop]: stopping since channel closed"); + break; + + } + } + } + } + } + } +} diff --git a/gateway/src/http/mod.rs b/gateway/src/http/mod.rs index 40972a523e..b6d09fc9e5 100644 --- a/gateway/src/http/mod.rs +++ b/gateway/src/http/mod.rs @@ -14,7 +14,6 @@ use nym_node_http_api::api::api_requests::SignedHostInformation; use nym_node_http_api::NymNodeHttpError; use nym_sphinx::addressing::clients::Recipient; use nym_task::TaskClient; -use nym_wireguard_types::WireguardGatewayData; use std::sync::Arc; fn load_gateway_details( @@ -141,8 +140,6 @@ pub(crate) struct HttpApiBuilder<'a> { identity_keypair: &'a identity::KeyPair, // TODO: this should be a wg specific key and not re-used sphinx sphinx_keypair: Arc, - - wireguard_data: Option>, } impl<'a> HttpApiBuilder<'a> { @@ -158,7 +155,6 @@ impl<'a> HttpApiBuilder<'a> { exit_policy: None, identity_keypair, sphinx_keypair, - wireguard_data: None, } } @@ -222,15 +218,6 @@ impl<'a> HttpApiBuilder<'a> { self } - #[must_use] - pub(crate) fn with_wireguard_data( - mut self, - wireguard_data: Option>, - ) -> Self { - self.wireguard_data = wireguard_data; - self - } - pub(crate) fn start(self, task_client: TaskClient) -> Result<(), GatewayError> { debug!("starting http API"); diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index 0e29fb629f..7fa387030d 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -27,7 +27,6 @@ use nym_task::{TaskClient, TaskHandle, TaskManager}; use nym_types::gateway::GatewayNodeDetailsResponse; use nym_validator_client::nyxd::{Coin, CosmWasmClient}; use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; -use nym_wireguard_types::WireguardGatewayData; use rand::seq::SliceRandom; use rand::thread_rng; use std::net::SocketAddr; @@ -128,7 +127,8 @@ pub struct Gateway { storage: St, - wireguard_data: Option>, + #[cfg(all(feature = "wireguard", target_os = "linux"))] + wireguard_data: Option, run_http_server: bool, task_client: Option, @@ -149,6 +149,7 @@ impl Gateway { config, network_requester_opts, ip_packet_router_opts, + #[cfg(all(feature = "wireguard", target_os = "linux"))] wireguard_data: None, run_http_server: true, task_client: None, @@ -170,6 +171,7 @@ impl Gateway { identity_keypair, sphinx_keypair, storage, + #[cfg(all(feature = "wireguard", target_os = "linux"))] wireguard_data: None, run_http_server: true, task_client: None, @@ -184,13 +186,8 @@ impl Gateway { self.task_client = Some(task_client) } - pub fn set_wireguard_data(&mut self, wireguard_data: Arc) { - // sanity check: - if let Some(wg_data) = self.wireguard_data.as_ref() { - if Arc::strong_count(wg_data) != 1 { - panic!("the client registry is already being used elsewhere") - } - } + #[cfg(all(feature = "wireguard", target_os = "linux"))] + pub fn set_wireguard_data(&mut self, wireguard_data: nym_wireguard::WireguardData) { self.wireguard_data = Some(wireguard_data) } @@ -229,11 +226,11 @@ impl Gateway { #[cfg(all(feature = "wireguard", target_os = "linux"))] async fn start_wireguard( - &self, + &mut self, shutdown: TaskClient, - ) -> Result> { - if let Some(wireguard_data) = self.wireguard_data.as_ref() { - nym_wireguard::start_wireguard(shutdown, Arc::clone(wireguard_data)).await + ) -> Result, Box> { + if let Some(wireguard_data) = self.wireguard_data.take() { + nym_wireguard::start_wireguard(shutdown, wireguard_data).await } else { Err(Box::new(GatewayError::WireguardNotSet)) } @@ -561,7 +558,6 @@ impl Gateway { self.identity_keypair.as_ref(), self.sphinx_keypair.clone(), ) - .with_wireguard_data(self.wireguard_data.clone()) .with_maybe_network_requester(self.network_requester_opts.as_ref().map(|o| &o.config)) .with_maybe_network_request_filter(nr_request_filter) .with_maybe_ip_packet_router(self.ip_packet_router_opts.as_ref().map(|o| &o.config)) diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index c80b926385..5fe29d354a 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -49,6 +49,7 @@ nym-sphinx-acknowledgements = { path = "../common/nymsphinx/acknowledgements" } nym-sphinx-addressing = { path = "../common/nymsphinx/addressing" } nym-task = { path = "../common/task" } nym-types = { path = "../common/types" } +nym-wireguard = { path = "../common/wireguard" } nym-wireguard-types = { path = "../common/wireguard-types", default-features = false } # nodes: diff --git a/nym-node/nym-node-http-api/Cargo.toml b/nym-node/nym-node-http-api/Cargo.toml index 65900fe68a..f59e85fe83 100644 --- a/nym-node/nym-node-http-api/Cargo.toml +++ b/nym-node/nym-node-http-api/Cargo.toml @@ -46,5 +46,3 @@ serde_json.workspace = true hmac = { workspace = true } tower = { workspace = true } x25519-dalek = { version = "2.0.0" } - -nym-crypto = { path = "../../common/crypto", features = ["rand"] } diff --git a/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs b/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs index f95e28942f..cbd8b08e12 100644 --- a/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs +++ b/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs @@ -11,33 +11,59 @@ use axum::extract::{Path, Query, State}; use axum::http::StatusCode; use axum::Json; use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{ - ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey, + ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, PeerPublicKey, }; +use nym_wireguard_types::registration::RegistrationData; use rand::{prelude::IteratorRandom, thread_rng}; +fn remove_from_registry( + state: &WireguardAppStateInner, + remote_public: &PeerPublicKey, + gateway_client: &GatewayClient, +) -> Result<(), RequestError> { + state + .wireguard_gateway_data + .remove_peer(gateway_client) + .map_err(|err| RequestError::from_err(err, StatusCode::INTERNAL_SERVER_ERROR))?; + state + .wireguard_gateway_data + .client_registry() + .remove(remote_public); + Ok(()) +} + async fn process_final_message( client: GatewayClient, state: &WireguardAppStateInner, -) -> Result { - let preshared_nonce = { - if let Some(nonce) = state.registration_in_progress.get(&client.pub_key()) { - *nonce - } else { - return Err(RequestError::from_err( - WireguardError::RegistrationNotInProgress, - StatusCode::BAD_REQUEST, - )); - } - }; +) -> Result { + let registration_data = state + .registration_in_progress + .get(&client.pub_key()) + .ok_or(RequestError::from_err( + WireguardError::RegistrationNotInProgress, + StatusCode::BAD_REQUEST, + ))? + .value() + .clone(); if client - .verify(state.keypair.private_key(), preshared_nonce) + .verify( + state.wireguard_gateway_data.keypair().private_key(), + registration_data.nonce, + ) .is_ok() { + state + .wireguard_gateway_data + .add_peer(&client) + .map_err(|err| RequestError::from_err(err, StatusCode::INTERNAL_SERVER_ERROR))?; state.registration_in_progress.remove(&client.pub_key()); - state.client_registry.insert(client.pub_key(), client); + state + .wireguard_gateway_data + .client_registry() + .insert(client.pub_key(), client); - Ok(StatusCode::OK) + Ok(ClientRegistrationResponse::Registered) } else { Err(RequestError::from_err( WireguardError::MacVerificationFailure, @@ -46,12 +72,65 @@ async fn process_final_message( } } -async fn process_init_message(init_message: InitMessage, state: &WireguardAppStateInner) -> Nonce { +async fn process_init_message( + init_message: InitMessage, + state: &WireguardAppStateInner, +) -> Result { + let remote_public = init_message.pub_key(); let nonce: u64 = fastrand::u64(..); + if let Some(registration_data) = state.registration_in_progress.get(&remote_public) { + return Ok(ClientRegistrationResponse::PendingRegistration( + registration_data.value().clone(), + )); + } + let gateway_client_opt = if let Some(gateway_client) = state + .wireguard_gateway_data + .client_registry() + .get(&remote_public) + { + let mut private_ip_ref = state + .free_private_network_ips + .get_mut(&gateway_client.private_ip) + .ok_or(RequestError::new( + "Internal data corruption", + StatusCode::INTERNAL_SERVER_ERROR, + ))?; + *private_ip_ref = true; + Some(gateway_client.clone()) + } else { + None + }; + if let Some(gateway_client) = gateway_client_opt { + remove_from_registry(state, &remote_public, &gateway_client)?; + } + let mut private_ip_ref = state + .free_private_network_ips + .iter_mut() + .filter(|r| **r) + .choose(&mut thread_rng()) + .ok_or(RequestError::new( + "No more space in the network", + StatusCode::SERVICE_UNAVAILABLE, + ))?; + // mark it as used, even though it's not final + *private_ip_ref = false; + let gateway_data = GatewayClient::new( + state.wireguard_gateway_data.keypair().private_key(), + remote_public.inner(), + *private_ip_ref.key(), + nonce, + ); + let registration_data = RegistrationData { + nonce, + gateway_data, + wg_port: state.binding_port, + }; state .registration_in_progress - .insert(init_message.pub_key(), nonce); - nonce + .insert(remote_public, registration_data.clone()); + Ok(ClientRegistrationResponse::PendingRegistration( + registration_data, + )) } /// Perform wireguard client registration. @@ -86,44 +165,11 @@ pub(crate) async fn register_client( return Err(RequestError::new_status(StatusCode::NOT_IMPLEMENTED)); }; - match payload { - ClientMessage::Initial(init) => { - let remote_public = init.pub_key().inner(); - let nonce = process_init_message(init, state).await; - let mut private_ip_ref = state - .free_private_network_ips - .iter_mut() - .filter(|r| **r) - .choose(&mut thread_rng()) - .ok_or(RequestError::new( - "No more space in the network", - StatusCode::SERVICE_UNAVAILABLE, - ))?; - // mark it as used, even though it's not final - *private_ip_ref = false; - let gateway_data = GatewayClient::new( - state.keypair.private_key(), - remote_public, - *private_ip_ref.key(), - nonce, - ); - let response = ClientRegistrationResponse::PendingRegistration { - nonce, - gateway_data, - wg_port: state.binding_port, - }; - Ok(output.to_response(response)) - } - ClientMessage::Final(finalize) => { - let result = process_final_message(finalize, state).await?; - if result.is_success() { - let response = ClientRegistrationResponse::Registered { success: true }; - Ok(output.to_response(response)) - } else { - Err(RequestError::new_status(result)) - } - } - } + let response = match payload { + ClientMessage::Initial(init) => process_init_message(init, state).await?, + ClientMessage::Final(finalize) => process_final_message(finalize, state).await?, + }; + Ok(output.to_response(response)) } pub type RegisterClientResponse = FormattedResponse; @@ -154,7 +200,8 @@ pub(crate) async fn get_all_clients( }; let clients = state - .client_registry + .wireguard_gateway_data + .client_registry() .iter() .map(|c| c.pub_key()) .collect::>(); @@ -196,7 +243,8 @@ pub(crate) async fn get_client( }; let clients = state - .client_registry + .wireguard_gateway_data + .client_registry() .iter() .filter_map(|c| { if c.pub_key() == pub_key { diff --git a/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/mod.rs b/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/mod.rs index dee432bd37..f0a4828187 100644 --- a/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/mod.rs +++ b/nym-node/nym-node-http-api/src/router/api/v1/gateway/client_interfaces/wireguard/mod.rs @@ -8,11 +8,10 @@ use crate::error::NymNodeHttpError; use axum::routing::{get, post}; use axum::Router; use ipnetwork::IpNetwork; -use nym_crypto::asymmetric::x25519::KeyPair; use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard; +use nym_wireguard::WireguardGatewayData; +use nym_wireguard_types::registration::PendingRegistrations; use nym_wireguard_types::registration::PrivateIPs; -use nym_wireguard_types::registration::{GatewayClientRegistry, PendingRegistrations}; -use nym_wireguard_types::WireguardGatewayData; use std::sync::Arc; pub(crate) mod client_registry; @@ -27,15 +26,14 @@ pub struct WireguardAppState { impl WireguardAppState { pub fn new( - wireguard_gateway_data: &WireguardGatewayData, + wireguard_gateway_data: WireguardGatewayData, registration_in_progress: Arc, binding_port: u16, private_ip_network: IpNetwork, ) -> Result { Ok(WireguardAppState { inner: Some(WireguardAppStateInner { - keypair: wireguard_gateway_data.keypair().clone(), - client_registry: wireguard_gateway_data.client_registry().clone(), + wireguard_gateway_data, registration_in_progress, binding_port, free_private_network_ips: Arc::new( @@ -81,8 +79,7 @@ macro_rules! get_state { #[derive(Clone)] pub(crate) struct WireguardAppStateInner { - keypair: Arc, - client_registry: Arc, + wireguard_gateway_data: WireguardGatewayData, registration_in_progress: Arc, binding_port: u16, free_private_network_ips: Arc, @@ -116,7 +113,8 @@ mod test { PeerPublicKey, }; use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard; - use nym_wireguard_types::registration::HmacSha256; + use nym_wireguard::{peer_controller::PeerControlMessage, WireguardGatewayData}; + use nym_wireguard_types::registration::{HmacSha256, RegistrationData}; use std::net::IpAddr; use std::str::FromStr; use std::sync::Arc; @@ -169,7 +167,6 @@ mod test { let client_dh = client_static_private.diffie_hellman(&gateway_static_public); let registration_in_progress = Arc::new(DashMap::new()); - let client_registry = Arc::new(DashMap::new()); let free_private_network_ips = Arc::new( IpNetwork::from_str("10.1.0.0/24") .unwrap() @@ -178,11 +175,19 @@ mod test { .collect(), ); let client_private_ip = IpAddr::from_str("10.1.0.42").unwrap(); + let (wireguard_gateway_data, mut peer_rx) = WireguardGatewayData::new( + nym_wireguard_types::Config { + bind_address: "0.0.0.0:51822".parse().unwrap(), + private_ip: "10.1.0.1".parse().unwrap(), + announced_port: 51822, + private_network_prefix: 16, + }, + Arc::new(gateway_key_pair), + ); let state = WireguardAppState { inner: Some(WireguardAppStateInner { - client_registry: Arc::clone(&client_registry), - keypair: Arc::new(gateway_key_pair), + wireguard_gateway_data: wireguard_gateway_data.clone(), registration_in_progress: Arc::clone(®istration_in_progress), binding_port: 8080, free_private_network_ips, @@ -214,11 +219,11 @@ mod test { assert_eq!(response.status(), StatusCode::OK); assert!(!registration_in_progress.is_empty()); - let ClientRegistrationResponse::PendingRegistration { + let ClientRegistrationResponse::PendingRegistration(RegistrationData { nonce, gateway_data, wg_port: 8080, - } = serde_json::from_slice(&to_bytes(response.into_body(), usize::MAX).await.unwrap()) + }) = serde_json::from_slice(&to_bytes(response.into_body(), usize::MAX).await.unwrap()) .unwrap() else { panic!("invalid response") @@ -252,9 +257,11 @@ mod test { .call(final_request) .await .unwrap(); + let msg = peer_rx.recv().await.unwrap(); + assert!(matches!(msg, PeerControlMessage::AddPeer(_))); assert_eq!(response.status(), StatusCode::OK); - assert!(!client_registry.is_empty()); + assert!(!wireguard_gateway_data.client_registry().is_empty()); let clients_request = Request::builder() .method("GET") @@ -278,7 +285,8 @@ mod test { assert!(!clients.is_empty()); assert_eq!( - client_registry + wireguard_gateway_data + .client_registry() .iter() .map(|c| c.value().pub_key()) .collect::>(), diff --git a/nym-node/src/node/mod.rs b/nym-node/src/node/mod.rs index 654d57ff99..20b62c3e44 100644 --- a/nym-node/src/node/mod.rs +++ b/nym-node/src/node/mod.rs @@ -33,11 +33,12 @@ use nym_node_http_api::{NymNodeHTTPServer, NymNodeRouter}; use nym_sphinx_acknowledgements::AckKey; use nym_sphinx_addressing::Recipient; use nym_task::{TaskClient, TaskManager}; -use nym_wireguard_types::WireguardGatewayData; +use nym_wireguard::{peer_controller::PeerControlMessage, WireguardGatewayData}; use rand::rngs::OsRng; use rand::{CryptoRng, RngCore}; use std::path::Path; use std::sync::Arc; +use tokio::sync::mpsc::UnboundedReceiver; use tracing::{debug, error, info, trace}; use zeroize::Zeroizing; @@ -252,16 +253,19 @@ impl ExitGatewayData { } pub struct WireguardData { - x25519_wireguard_keys: Arc, + inner: WireguardGatewayData, + peer_rx: UnboundedReceiver, } impl WireguardData { pub(crate) fn new(config: &Wireguard) -> Result { - Ok(WireguardData { - x25519_wireguard_keys: Arc::new(load_x25519_wireguard_keypair( + let (inner, peer_rx) = WireguardGatewayData::new( + config.clone().into(), + Arc::new(load_x25519_wireguard_keypair( config.storage_paths.x25519_wireguard_storage_paths(), )?), - }) + ); + Ok(WireguardData { inner, peer_rx }) } pub(crate) fn initialise(config: &Wireguard) -> Result<(), ExitGatewayError> { @@ -278,6 +282,15 @@ impl WireguardData { } } +impl From for nym_wireguard::WireguardData { + fn from(value: WireguardData) -> Self { + nym_wireguard::WireguardData { + inner: value.inner, + peer_rx: value.peer_rx, + } + } +} + pub(crate) struct NymNode { config: Config, description: NodeDescription, @@ -358,10 +371,6 @@ impl NymNode { pub(crate) async fn new(config: Config) -> Result { let wireguard_data = WireguardData::new(&config.wireguard)?; - let wireguard_gateway_data = WireguardGatewayData::new( - config.wireguard.clone().into(), - wireguard_data.x25519_wireguard_keys.clone(), - ); Ok(NymNode { ed25519_identity_keys: Arc::new(load_ed25519_identity_keypair( config.storage_paths.keys.ed25519_identity_storage_paths(), @@ -375,8 +384,11 @@ impl NymNode { description: load_node_description(&config.storage_paths.description)?, verloc_stats: Default::default(), mixnode: MixnodeData::new(&config.mixnode)?, - entry_gateway: EntryGatewayData::new(&config.entry_gateway, wireguard_gateway_data) - .await?, + entry_gateway: EntryGatewayData::new( + &config.entry_gateway, + wireguard_data.inner.clone(), + ) + .await?, exit_gateway: ExitGatewayData::new(&config.exit_gateway)?, wireguard: wireguard_data, config, @@ -400,7 +412,7 @@ impl NymNode { } fn x25519_wireguard_key(&self) -> &x25519::PublicKey { - self.wireguard.x25519_wireguard_keys.public_key() + self.wireguard.inner.keypair().public_key() } pub(crate) fn display_details(&self) -> DisplayDetails { @@ -432,7 +444,7 @@ impl NymNode { self.x25519_noise_keys.public_key() } - fn start_mixnode(&self, task_client: TaskClient) -> Result<(), NymNodeError> { + fn start_mixnode(self, task_client: TaskClient) -> Result<(), NymNodeError> { info!("going to start the nym-node in MIXNODE mode"); let config = ephemeral_mixnode_config(self.config.clone())?; @@ -455,15 +467,11 @@ impl NymNode { Ok(()) } - fn start_entry_gateway(&self, task_client: TaskClient) -> Result<(), NymNodeError> { + fn start_entry_gateway(self, task_client: TaskClient) -> Result<(), NymNodeError> { info!("going to start the nym-node in ENTRY GATEWAY mode"); let config = ephemeral_entry_gateway_config(self.config.clone(), &self.entry_gateway.mnemonic)?; - let wireguard_data = Arc::new(WireguardGatewayData::new( - self.config.wireguard.clone().into(), - self.wireguard.x25519_wireguard_keys.clone(), - )); let mut entry_gateway = Gateway::new_loaded( config, None, @@ -474,7 +482,8 @@ impl NymNode { ); entry_gateway.disable_http_server(); entry_gateway.set_task_client(task_client); - entry_gateway.set_wireguard_data(wireguard_data); + #[cfg(all(feature = "wireguard", target_os = "linux"))] + entry_gateway.set_wireguard_data(self.wireguard.into()); tokio::spawn(async move { if let Err(err) = entry_gateway.run().await { @@ -484,15 +493,11 @@ impl NymNode { Ok(()) } - fn start_exit_gateway(&self, task_client: TaskClient) -> Result<(), NymNodeError> { + fn start_exit_gateway(self, task_client: TaskClient) -> Result<(), NymNodeError> { info!("going to start the nym-node in EXIT GATEWAY mode"); let config = ephemeral_exit_gateway_config(self.config.clone(), &self.entry_gateway.mnemonic)?; - let wireguard_data = Arc::new(WireguardGatewayData::new( - self.config.wireguard.clone().into(), - self.wireguard.x25519_wireguard_keys.clone(), - )); let mut exit_gateway = Gateway::new_loaded( config.gateway, @@ -504,7 +509,8 @@ impl NymNode { ); exit_gateway.disable_http_server(); exit_gateway.set_task_client(task_client); - exit_gateway.set_wireguard_data(wireguard_data); + #[cfg(all(feature = "wireguard", target_os = "linux"))] + exit_gateway.set_wireguard_data(self.wireguard.into()); tokio::spawn(async move { if let Err(err) = exit_gateway.run().await { @@ -585,7 +591,7 @@ impl NymNode { )?; let wg_state = WireguardAppState::new( - &self.entry_gateway.wireguard_data, + self.entry_gateway.wireguard_data.clone(), Default::default(), self.config.wireguard.bind_address.port(), wireguard_private_network,