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
This commit is contained in:
committed by
GitHub
parent
102a8a0d8b
commit
70fa41c165
Generated
+3
@@ -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",
|
||||
|
||||
@@ -32,4 +32,7 @@ pub enum Error {
|
||||
#[source]
|
||||
source: hmac::digest::MacError,
|
||||
},
|
||||
|
||||
#[error("peers can't be modified anymore")]
|
||||
PeerModifyStopped,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<KeyPair>,
|
||||
client_registry: Arc<GatewayClientRegistry>,
|
||||
}
|
||||
|
||||
impl WireguardGatewayData {
|
||||
pub fn new(config: Config, keypair: Arc<KeyPair>) -> Self {
|
||||
WireguardGatewayData {
|
||||
config,
|
||||
keypair,
|
||||
client_registry: Arc::new(DashMap::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(&self) -> Config {
|
||||
self.config
|
||||
}
|
||||
|
||||
pub fn keypair(&self) -> &Arc<KeyPair> {
|
||||
&self.keypair
|
||||
}
|
||||
|
||||
pub fn client_registry(&self) -> &Arc<GatewayClientRegistry> {
|
||||
&self.client_registry
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type GatewayClientRegistry = DashMap<PeerPublicKey, GatewayClient>;
|
||||
pub type PendingRegistrations = DashMap<PeerPublicKey, Nonce>;
|
||||
pub type PendingRegistrations = DashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = DashMap<IpAddr, Free>;
|
||||
|
||||
#[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.
|
||||
|
||||
@@ -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" }
|
||||
|
||||
+85
-19
@@ -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<KeyPair>,
|
||||
client_registry: Arc<GatewayClientRegistry>,
|
||||
peer_tx: mpsc::UnboundedSender<PeerControlMessage>,
|
||||
}
|
||||
|
||||
impl WireguardGatewayData {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
keypair: Arc<KeyPair>,
|
||||
) -> (Self, mpsc::UnboundedReceiver<PeerControlMessage>) {
|
||||
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<KeyPair> {
|
||||
&self.keypair
|
||||
}
|
||||
|
||||
pub fn client_registry(&self) -> &Arc<GatewayClientRegistry> {
|
||||
&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<PeerControlMessage>,
|
||||
}
|
||||
|
||||
/// Start wireguard device
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn start_wireguard(
|
||||
mut task_client: nym_task::TaskClient,
|
||||
wireguard_data: std::sync::Arc<nym_wireguard_types::WireguardGatewayData>,
|
||||
) -> Result<WgApiWrapper, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
task_client: nym_task::TaskClient,
|
||||
wireguard_data: WireguardData,
|
||||
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
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"))]
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<PeerControlMessage>,
|
||||
wg_api: Arc<WgApiWrapper>,
|
||||
}
|
||||
|
||||
impl PeerController {
|
||||
pub fn new(
|
||||
wg_api: Arc<WgApiWrapper>,
|
||||
peer_rx: mpsc::UnboundedReceiver<PeerControlMessage>,
|
||||
) -> 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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<encryption::KeyPair>,
|
||||
|
||||
wireguard_data: Option<Arc<WireguardGatewayData>>,
|
||||
}
|
||||
|
||||
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<Arc<WireguardGatewayData>>,
|
||||
) -> Self {
|
||||
self.wireguard_data = wireguard_data;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn start(self, task_client: TaskClient) -> Result<(), GatewayError> {
|
||||
debug!("starting http API");
|
||||
|
||||
|
||||
+10
-14
@@ -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<St = PersistentStorage> {
|
||||
|
||||
storage: St,
|
||||
|
||||
wireguard_data: Option<Arc<WireguardGatewayData>>,
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
wireguard_data: Option<nym_wireguard::WireguardData>,
|
||||
|
||||
run_http_server: bool,
|
||||
task_client: Option<TaskClient>,
|
||||
@@ -149,6 +149,7 @@ impl<St> Gateway<St> {
|
||||
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<St> Gateway<St> {
|
||||
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<St> Gateway<St> {
|
||||
self.task_client = Some(task_client)
|
||||
}
|
||||
|
||||
pub fn set_wireguard_data(&mut self, wireguard_data: Arc<WireguardGatewayData>) {
|
||||
// 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<St> Gateway<St> {
|
||||
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
async fn start_wireguard(
|
||||
&self,
|
||||
&mut self,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<nym_wireguard::WgApiWrapper, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if let Some(wireguard_data) = self.wireguard_data.as_ref() {
|
||||
nym_wireguard::start_wireguard(shutdown, Arc::clone(wireguard_data)).await
|
||||
) -> Result<Arc<nym_wireguard::WgApiWrapper>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
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<St> Gateway<St> {
|
||||
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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
+106
-58
@@ -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<StatusCode, RequestError> {
|
||||
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<ClientRegistrationResponse, RequestError> {
|
||||
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<ClientRegistrationResponse, RequestError> {
|
||||
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<ClientRegistrationResponse>;
|
||||
@@ -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::<Vec<PeerPublicKey>>();
|
||||
@@ -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 {
|
||||
|
||||
+24
-16
@@ -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<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
private_ip_network: IpNetwork,
|
||||
) -> Result<Self, NymNodeHttpError> {
|
||||
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<KeyPair>,
|
||||
client_registry: Arc<GatewayClientRegistry>,
|
||||
wireguard_gateway_data: WireguardGatewayData,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
free_private_network_ips: Arc<PrivateIPs>,
|
||||
@@ -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::<Vec<PeerPublicKey>>(),
|
||||
|
||||
+32
-26
@@ -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<x25519::KeyPair>,
|
||||
inner: WireguardGatewayData,
|
||||
peer_rx: UnboundedReceiver<PeerControlMessage>,
|
||||
}
|
||||
|
||||
impl WireguardData {
|
||||
pub(crate) fn new(config: &Wireguard) -> Result<Self, NymNodeError> {
|
||||
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<WireguardData> 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<Self, NymNodeError> {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user