Remove unused wg http endpoint
This commit is contained in:
@@ -257,7 +257,7 @@ impl<'a> HttpApiBuilder<'a> {
|
||||
}
|
||||
|
||||
let bind_address = self.gateway_config.http.bind_address;
|
||||
let router = nym_node_http_api::NymNodeRouter::new(config, None, None);
|
||||
let router = nym_node_http_api::NymNodeRouter::new(config, None);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let server = match router.build_server(&bind_address).await {
|
||||
|
||||
@@ -103,7 +103,7 @@ impl<'a> HttpApiBuilder<'a> {
|
||||
.with_mixnode(load_mixnode_details(self.mixnode_config)?)
|
||||
.with_landing_page_assets(self.mixnode_config.http.landing_page_assets_path.as_ref());
|
||||
|
||||
let router = nym_node_http_api::NymNodeRouter::new(config, None, None);
|
||||
let router = nym_node_http_api::NymNodeRouter::new(config, None);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let server = match router.build_server(&bind_address).await {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::WireguardAppState;
|
||||
use crate::state::AppState;
|
||||
use axum::Router;
|
||||
use nym_node_requests::routes;
|
||||
@@ -17,9 +16,6 @@ pub struct Config {
|
||||
pub v1_config: v1::Config,
|
||||
}
|
||||
|
||||
pub(super) fn routes(config: Config, initial_wg_state: WireguardAppState) -> Router<AppState> {
|
||||
Router::new().nest(
|
||||
routes::api::V1,
|
||||
v1::routes(config.v1_config, initial_wg_state),
|
||||
)
|
||||
pub(super) fn routes(config: Config) -> Router<AppState> {
|
||||
Router::new().nest(routes::api::V1, v1::routes(config.v1_config))
|
||||
}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::WireguardAppState;
|
||||
use crate::api::{FormattedResponse, OutputParams};
|
||||
use axum::extract::Query;
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use nym_node_requests::api::v1::gateway::models::{ClientInterfaces, WebSockets, Wireguard};
|
||||
use nym_node_requests::api::v1::gateway::models::{ClientInterfaces, WebSockets};
|
||||
use nym_node_requests::routes::api::v1::gateway::client_interfaces;
|
||||
|
||||
pub(crate) mod wireguard;
|
||||
|
||||
pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
|
||||
interfaces: Option<ClientInterfaces>,
|
||||
initial_wg_state: WireguardAppState,
|
||||
) -> Router<S> {
|
||||
Router::new()
|
||||
.route(
|
||||
@@ -31,17 +27,6 @@ pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
|
||||
move |query| mixnet_websockets(websockets, query)
|
||||
}),
|
||||
)
|
||||
.nest(
|
||||
client_interfaces::WIREGUARD,
|
||||
wireguard::routes(initial_wg_state),
|
||||
)
|
||||
.route(
|
||||
client_interfaces::WIREGUARD,
|
||||
get({
|
||||
let wireguard_cfg_info = interfaces.and_then(|i| i.wireguard);
|
||||
move |query| wireguard_info(wireguard_cfg_info, query)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns client interfaces supported by this gateway.
|
||||
@@ -70,32 +55,6 @@ pub(crate) async fn client_interfaces(
|
||||
|
||||
pub type ClientInterfacesResponse = FormattedResponse<ClientInterfaces>;
|
||||
|
||||
/// Returns client interfaces supported by this gateway.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/wireguard",
|
||||
context_path = "/api/v1/gateway/client-interfaces",
|
||||
tag = "Gateway",
|
||||
responses(
|
||||
(status = 501, description = "the endpoint hasn't been implemented yet"),
|
||||
(status = 200, content(
|
||||
("application/json" = Wireguard),
|
||||
("application/yaml" = Wireguard)
|
||||
))
|
||||
),
|
||||
params(OutputParams)
|
||||
)]
|
||||
pub(crate) async fn wireguard_info(
|
||||
wireguard: Option<Wireguard>,
|
||||
Query(output): Query<OutputParams>,
|
||||
) -> Result<WireguardResponse, StatusCode> {
|
||||
let wireguard = wireguard.ok_or(StatusCode::NOT_IMPLEMENTED)?;
|
||||
let output = output.output.unwrap_or_default();
|
||||
Ok(output.to_response(wireguard))
|
||||
}
|
||||
|
||||
pub type WireguardResponse = FormattedResponse<Wireguard>;
|
||||
|
||||
/// Returns client interfaces supported by this gateway.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
|
||||
-265
@@ -1,265 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use super::error::WireguardError;
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::{
|
||||
WireguardAppState, WireguardAppStateInner,
|
||||
};
|
||||
use crate::api::{FormattedResponse, OutputParams};
|
||||
use crate::router::types::RequestError;
|
||||
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, 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<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.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
|
||||
.wireguard_gateway_data
|
||||
.client_registry()
|
||||
.insert(client.pub_key(), client);
|
||||
|
||||
Ok(ClientRegistrationResponse::Registered)
|
||||
} else {
|
||||
Err(RequestError::from_err(
|
||||
WireguardError::MacVerificationFailure,
|
||||
StatusCode::BAD_REQUEST,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
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(remote_public, registration_data.clone());
|
||||
Ok(ClientRegistrationResponse::PendingRegistration(
|
||||
registration_data,
|
||||
))
|
||||
}
|
||||
|
||||
/// Perform wireguard client registration.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/client",
|
||||
context_path = "/api/v1/gateway/client-interfaces/wireguard",
|
||||
tag = "Wireguard (EXPERIMENTAL AND UNSTABLE)",
|
||||
request_body(
|
||||
content = ClientMessage,
|
||||
description = "Data used for proceeding with client wireguard registration",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 501, body = ErrorResponse, description = "the endpoint hasn't been implemented yet"),
|
||||
(status = 400, body = ErrorResponse),
|
||||
(status = 200, content(
|
||||
("application/json" = ClientRegistrationResponse),
|
||||
("application/yaml" = ClientRegistrationResponse)
|
||||
))
|
||||
),
|
||||
params(OutputParams)
|
||||
)]
|
||||
pub(crate) async fn register_client(
|
||||
State(state): State<WireguardAppState>,
|
||||
Query(output): Query<OutputParams>,
|
||||
Json(payload): Json<ClientMessage>,
|
||||
) -> Result<RegisterClientResponse, RequestError> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let Some(state) = state.inner() else {
|
||||
return Err(RequestError::new_status(StatusCode::NOT_IMPLEMENTED));
|
||||
};
|
||||
|
||||
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>;
|
||||
|
||||
/// Get public keys of all registered wireguard clients.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/clients",
|
||||
context_path = "/api/v1/gateway/client-interfaces/wireguard",
|
||||
tag = "Wireguard (EXPERIMENTAL AND UNSTABLE)",
|
||||
responses(
|
||||
(status = 501, body = ErrorResponse, description = "the endpoint hasn't been implemented yet"),
|
||||
(status = 200, content(
|
||||
("application/json" = Vec<String>),
|
||||
("application/yaml" = Vec<String>)
|
||||
))
|
||||
),
|
||||
params(OutputParams)
|
||||
)]
|
||||
pub(crate) async fn get_all_clients(
|
||||
Query(output): Query<OutputParams>,
|
||||
State(state): State<WireguardAppState>,
|
||||
) -> Result<AllClientsResponse, RequestError> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let Some(state) = state.inner() else {
|
||||
return Err(RequestError::new_status(StatusCode::NOT_IMPLEMENTED));
|
||||
};
|
||||
|
||||
let clients = state
|
||||
.wireguard_gateway_data
|
||||
.client_registry()
|
||||
.iter()
|
||||
.map(|c| c.pub_key())
|
||||
.collect::<Vec<PeerPublicKey>>();
|
||||
|
||||
Ok(output.to_response(clients))
|
||||
}
|
||||
|
||||
pub type AllClientsResponse = FormattedResponse<Vec<PeerPublicKey>>;
|
||||
|
||||
/// Get client details of the registered wireguard client by its public key.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/client/{pub_key}",
|
||||
context_path = "/api/v1/gateway/client-interfaces/wireguard",
|
||||
tag = "Wireguard (EXPERIMENTAL AND UNSTABLE)",
|
||||
params(
|
||||
("pub_key", Path, description = "The public key of the client"),
|
||||
OutputParams
|
||||
),
|
||||
responses(
|
||||
(status = 501, body = ErrorResponse, description = "the endpoint hasn't been implemented yet"),
|
||||
(status = 404, body = ErrorResponse, description = "there are no clients with the provided public key"),
|
||||
(status = 400, body = ErrorResponse),
|
||||
(status = 200, content(
|
||||
("application/json" = Vec<GatewayClient>),
|
||||
("application/yaml" = Vec<GatewayClient>)
|
||||
))
|
||||
),
|
||||
)]
|
||||
pub(crate) async fn get_client(
|
||||
Path(pub_key): Path<PeerPublicKey>,
|
||||
Query(output): Query<OutputParams>,
|
||||
State(state): State<WireguardAppState>,
|
||||
) -> Result<ClientResponse, RequestError> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let Some(state) = state.inner() else {
|
||||
return Err(RequestError::new_status(StatusCode::NOT_IMPLEMENTED));
|
||||
};
|
||||
|
||||
let clients = state
|
||||
.wireguard_gateway_data
|
||||
.client_registry()
|
||||
.iter()
|
||||
.filter_map(|c| {
|
||||
if c.pub_key() == pub_key {
|
||||
Some(c.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<GatewayClient>>();
|
||||
|
||||
if clients.is_empty() {
|
||||
return Err(RequestError::new_status(StatusCode::NOT_FOUND));
|
||||
}
|
||||
|
||||
Ok(output.to_response(clients))
|
||||
}
|
||||
|
||||
pub type ClientResponse = FormattedResponse<Vec<GatewayClient>>;
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WireguardError {
|
||||
#[error("the client is currently not in the process of being registered")]
|
||||
RegistrationNotInProgress,
|
||||
|
||||
#[error("the client mac failed to get verified correctly")]
|
||||
MacVerificationFailure,
|
||||
}
|
||||
-296
@@ -1,296 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::client_registry::{
|
||||
get_all_clients, get_client, register_client,
|
||||
};
|
||||
use crate::error::NymNodeHttpError;
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
use ipnetwork::IpNetwork;
|
||||
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 std::sync::Arc;
|
||||
|
||||
pub(crate) mod client_registry;
|
||||
mod error;
|
||||
|
||||
// I don't see any reason why this state should be accessible to any routes outside /wireguard
|
||||
// if anyone finds compelling reason, it could be moved to the `AppState` struct instead
|
||||
#[derive(Clone, Default)]
|
||||
pub struct WireguardAppState {
|
||||
inner: Option<WireguardAppStateInner>,
|
||||
}
|
||||
|
||||
impl WireguardAppState {
|
||||
pub fn new(
|
||||
wireguard_gateway_data: WireguardGatewayData,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
private_ip_network: IpNetwork,
|
||||
) -> Result<Self, NymNodeHttpError> {
|
||||
Ok(WireguardAppState {
|
||||
inner: Some(WireguardAppStateInner {
|
||||
wireguard_gateway_data,
|
||||
registration_in_progress,
|
||||
binding_port,
|
||||
free_private_network_ips: Arc::new(
|
||||
private_ip_network.iter().map(|ip| (ip, true)).collect(),
|
||||
),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
// #[allow(dead_code)]
|
||||
// pub(crate) fn dh_keypair(&self) -> Option<&encryption::KeyPair> {
|
||||
// self.inner.as_ref().map(|s| s.dh_keypair.as_ref())
|
||||
// }
|
||||
//
|
||||
// #[allow(dead_code)]
|
||||
// pub(crate) fn client_registry(&self) -> Option<&RwLock<ClientRegistry>> {
|
||||
// self.inner.as_ref().map(|s| s.client_registry.as_ref())
|
||||
// }
|
||||
//
|
||||
// #[allow(dead_code)]
|
||||
// pub(crate) fn registration_in_progress(&self) -> Option<&RwLock<PendingRegistrations>> {
|
||||
// self.inner
|
||||
// .as_ref()
|
||||
// .map(|s| s.registration_in_progress.as_ref())
|
||||
// }
|
||||
|
||||
// not sure what to feel about exposing this method
|
||||
pub(crate) fn inner(&self) -> Option<&WireguardAppStateInner> {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// helper macro to deal with missing wg state (if not being exposed by the node)
|
||||
#[macro_export]
|
||||
macro_rules! get_state {
|
||||
( $state: ident, $field: ident ) => {{
|
||||
let Some(ref inner) = $state.inner else {
|
||||
return ::axum::http::StatusCode::NOT_IMPLEMENTED;
|
||||
};
|
||||
inner.$field.as_ref()
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct WireguardAppStateInner {
|
||||
wireguard_gateway_data: WireguardGatewayData,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
free_private_network_ips: Arc<PrivateIPs>,
|
||||
}
|
||||
|
||||
pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
|
||||
Router::new()
|
||||
// .route("/", get())
|
||||
.route(wireguard::CLIENTS, get(get_all_clients))
|
||||
.route(wireguard::CLIENT, post(register_client))
|
||||
.route(&format!("{}/:pub_key", wireguard::CLIENT), get(get_client))
|
||||
.with_state(initial_state)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::{
|
||||
routes, WireguardAppState, WireguardAppStateInner,
|
||||
};
|
||||
use axum::body::to_bytes;
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::http::StatusCode;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use dashmap::DashMap;
|
||||
use hmac::Mac;
|
||||
use ipnetwork::IpNetwork;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
|
||||
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage,
|
||||
PeerPublicKey,
|
||||
};
|
||||
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
|
||||
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;
|
||||
use tower::Service;
|
||||
use tower::ServiceExt;
|
||||
use x25519_dalek::{PublicKey, StaticSecret};
|
||||
|
||||
const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
|
||||
|
||||
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
|
||||
general_purpose::STANDARD
|
||||
.decode(base64_key)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn server_static_private_key() -> x25519_dalek::StaticSecret {
|
||||
// TODO: this is a temporary solution for development
|
||||
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
|
||||
x25519_dalek::StaticSecret::from(static_private_bytes)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn registration() {
|
||||
// 1. Provision random keys for gateway and client
|
||||
// 2. Generate DH shared secret
|
||||
// 3. Client submits its public key to the gateway to start the handshake process, gateway responds with nonce
|
||||
// 4. Client generates mac digest using DH shared secret, its own public key, socket address and port, and nonce
|
||||
// 5. Client sends its public key, socket address and port, nonce and mac digest to the gateway
|
||||
// 6. Gateway verifies mac digest and nonce, and stores client's public key and socket address and port
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let gateway_private_key =
|
||||
encryption::PrivateKey::from_bytes(server_static_private_key().as_bytes()).unwrap();
|
||||
let gateway_public_key = encryption::PublicKey::from(&gateway_private_key);
|
||||
|
||||
let gateway_key_pair = encryption::KeyPair::from_bytes(
|
||||
&gateway_private_key.to_bytes(),
|
||||
&gateway_public_key.to_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
let client_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let gateway_static_public = PublicKey::from(gateway_key_pair.public_key().to_bytes());
|
||||
|
||||
let client_static_private = StaticSecret::from(client_key_pair.private_key().to_bytes());
|
||||
let client_static_public = PublicKey::from(client_key_pair.public_key().to_bytes());
|
||||
|
||||
let client_dh = client_static_private.diffie_hellman(&gateway_static_public);
|
||||
|
||||
let registration_in_progress = Arc::new(DashMap::new());
|
||||
let free_private_network_ips = Arc::new(
|
||||
IpNetwork::from_str("10.1.0.0/24")
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|ip| (ip, true))
|
||||
.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 {
|
||||
wireguard_gateway_data: wireguard_gateway_data.clone(),
|
||||
registration_in_progress: Arc::clone(®istration_in_progress),
|
||||
binding_port: 8080,
|
||||
free_private_network_ips,
|
||||
}),
|
||||
};
|
||||
|
||||
// `Router` implements `tower::Service<Request<Body>>` so we can
|
||||
// call it like any tower service, no need to run an HTTP server.
|
||||
let mut app = routes(state);
|
||||
|
||||
let init_message = ClientMessage::Initial(InitMessage {
|
||||
pub_key: PeerPublicKey::new(client_static_public),
|
||||
});
|
||||
|
||||
let init_request = Request::builder()
|
||||
.method("POST")
|
||||
.uri(wireguard::CLIENT)
|
||||
.header("Content-type", "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&init_message).unwrap()))
|
||||
.unwrap();
|
||||
|
||||
let response = ServiceExt::<Request<Body>>::ready(&mut app)
|
||||
.await
|
||||
.unwrap()
|
||||
.call(init_request)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(!registration_in_progress.is_empty());
|
||||
|
||||
let ClientRegistrationResponse::PendingRegistration(RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port: 8080,
|
||||
}) = serde_json::from_slice(&to_bytes(response.into_body(), usize::MAX).await.unwrap())
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("invalid response")
|
||||
};
|
||||
assert!(gateway_data
|
||||
.verify(client_key_pair.private_key(), nonce)
|
||||
.is_ok());
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap();
|
||||
mac.update(client_static_public.as_bytes());
|
||||
mac.update(client_private_ip.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
let mac = mac.finalize().into_bytes();
|
||||
|
||||
let finalized_message = ClientMessage::Final(GatewayClient {
|
||||
pub_key: PeerPublicKey::new(client_static_public),
|
||||
private_ip: client_private_ip,
|
||||
mac: ClientMac::new(mac.as_slice().to_vec()),
|
||||
});
|
||||
|
||||
let final_request = Request::builder()
|
||||
.method("POST")
|
||||
.uri(wireguard::CLIENT)
|
||||
.header("Content-type", "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&finalized_message).unwrap()))
|
||||
.unwrap();
|
||||
|
||||
let response = ServiceExt::<Request<Body>>::ready(&mut app)
|
||||
.await
|
||||
.unwrap()
|
||||
.call(final_request)
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = peer_rx.recv().await.unwrap();
|
||||
|
||||
assert!(matches!(msg, PeerControlMessage::AddPeer(_)));
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(!wireguard_gateway_data.client_registry().is_empty());
|
||||
|
||||
let clients_request = Request::builder()
|
||||
.method("GET")
|
||||
.uri(wireguard::CLIENTS)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = ServiceExt::<Request<Body>>::ready(&mut app)
|
||||
.await
|
||||
.unwrap()
|
||||
.call(clients_request)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let clients: Vec<PeerPublicKey> =
|
||||
serde_json::from_slice(&to_bytes(response.into_body(), usize::MAX).await.unwrap())
|
||||
.unwrap();
|
||||
|
||||
assert!(!clients.is_empty());
|
||||
|
||||
assert_eq!(
|
||||
wireguard_gateway_data
|
||||
.client_registry()
|
||||
.iter()
|
||||
.map(|c| c.value().pub_key())
|
||||
.collect::<Vec<PeerPublicKey>>(),
|
||||
clients
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::WireguardAppState;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use nym_node_requests::api::v1::gateway::models;
|
||||
@@ -15,10 +14,7 @@ pub struct Config {
|
||||
pub details: Option<models::Gateway>,
|
||||
}
|
||||
|
||||
pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
|
||||
config: Config,
|
||||
initial_wg_state: WireguardAppState,
|
||||
) -> Router<S> {
|
||||
pub(crate) fn routes<S: Send + Sync + 'static + Clone>(config: Config) -> Router<S> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/",
|
||||
@@ -29,9 +25,6 @@ pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
|
||||
)
|
||||
.nest(
|
||||
gateway::CLIENT_INTERFACES,
|
||||
client_interfaces::routes(
|
||||
config.details.map(|g| g.client_interfaces),
|
||||
initial_wg_state,
|
||||
),
|
||||
client_interfaces::routes(config.details.map(|g| g.client_interfaces)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::v1::gateway::client_interfaces::wireguard::WireguardAppState;
|
||||
use crate::state::AppState;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
@@ -26,14 +25,11 @@ pub struct Config {
|
||||
pub ip_packet_router: ip_packet_router::Config,
|
||||
}
|
||||
|
||||
pub(super) fn routes(config: Config, initial_wg_state: WireguardAppState) -> Router<AppState> {
|
||||
pub(super) fn routes(config: Config) -> Router<AppState> {
|
||||
Router::new()
|
||||
.route(v1::HEALTH, get(health::root_health))
|
||||
.nest(v1::METRICS, metrics::routes(config.metrics))
|
||||
.nest(
|
||||
v1::GATEWAY,
|
||||
gateway::routes(config.gateway, initial_wg_state),
|
||||
)
|
||||
.nest(v1::GATEWAY, gateway::routes(config.gateway))
|
||||
.nest(v1::MIXNODE, mixnode::routes(config.mixnode))
|
||||
.nest(
|
||||
v1::NETWORK_REQUESTER,
|
||||
|
||||
@@ -26,11 +26,7 @@ use utoipa_swagger_ui::SwaggerUi;
|
||||
api::v1::health::root_health,
|
||||
api::v1::gateway::root::root_gateway,
|
||||
api::v1::gateway::client_interfaces::client_interfaces,
|
||||
api::v1::gateway::client_interfaces::wireguard_info,
|
||||
api::v1::gateway::client_interfaces::mixnet_websockets,
|
||||
api::v1::gateway::client_interfaces::wireguard::client_registry::register_client,
|
||||
api::v1::gateway::client_interfaces::wireguard::client_registry::get_all_clients,
|
||||
api::v1::gateway::client_interfaces::wireguard::client_registry::get_client,
|
||||
api::v1::mixnode::root::root_mixnode,
|
||||
api::v1::network_requester::root::root_network_requester,
|
||||
api::v1::network_requester::exit_policy::node_exit_policy,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub use crate::api::v1::gateway::client_interfaces::wireguard::WireguardAppState;
|
||||
use crate::error::NymNodeHttpError;
|
||||
use crate::middleware::logging;
|
||||
use crate::state::AppState;
|
||||
@@ -156,11 +155,7 @@ pub struct NymNodeRouter {
|
||||
|
||||
impl NymNodeRouter {
|
||||
// TODO: move the wg state to a builder
|
||||
pub fn new(
|
||||
config: Config,
|
||||
app_state: Option<AppState>,
|
||||
initial_wg_state: Option<WireguardAppState>,
|
||||
) -> NymNodeRouter {
|
||||
pub fn new(config: Config, app_state: Option<AppState>) -> NymNodeRouter {
|
||||
let state = app_state.unwrap_or(AppState::new());
|
||||
|
||||
NymNodeRouter {
|
||||
@@ -189,10 +184,7 @@ impl NymNodeRouter {
|
||||
}),
|
||||
)
|
||||
.nest(routes::LANDING_PAGE, landing_page::routes(config.landing))
|
||||
.nest(
|
||||
routes::API,
|
||||
api::routes(config.api, initial_wg_state.unwrap_or_default()),
|
||||
)
|
||||
.nest(routes::API, api::routes(config.api))
|
||||
.layer(axum::middleware::from_fn(logging::logger))
|
||||
.with_state(state),
|
||||
}
|
||||
|
||||
@@ -24,19 +24,6 @@ impl RequestError {
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_status(status: StatusCode) -> Self {
|
||||
RequestError {
|
||||
inner: ErrorResponse {
|
||||
message: String::new(),
|
||||
},
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_err<E: std::error::Error>(err: E, status: StatusCode) -> Self {
|
||||
Self::new(err.to_string(), status)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for RequestError {
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::node::helpers::{
|
||||
store_x25519_sphinx_keypair, DisplayDetails,
|
||||
};
|
||||
use crate::node::http::{sign_host_details, system_info::get_system_info};
|
||||
use ipnetwork::IpNetwork;
|
||||
use nym_bin_common::bin_info_owned;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_gateway::Gateway;
|
||||
@@ -26,7 +25,6 @@ use nym_node::config::{
|
||||
use nym_node::error::{EntryGatewayError, ExitGatewayError, MixnodeError, NymNodeError};
|
||||
use nym_node_http_api::api::api_requests;
|
||||
use nym_node_http_api::api::api_requests::v1::node::models::NodeDescription;
|
||||
use nym_node_http_api::router::WireguardAppState;
|
||||
use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats};
|
||||
use nym_node_http_api::state::AppState;
|
||||
use nym_node_http_api::{NymNodeHTTPServer, NymNodeRouter};
|
||||
@@ -68,7 +66,6 @@ impl MixnodeData {
|
||||
pub struct EntryGatewayData {
|
||||
mnemonic: Zeroizing<bip39::Mnemonic>,
|
||||
client_storage: nym_gateway::node::PersistentStorage,
|
||||
wireguard_data: WireguardGatewayData,
|
||||
}
|
||||
|
||||
impl EntryGatewayData {
|
||||
@@ -86,10 +83,7 @@ impl EntryGatewayData {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn new(
|
||||
config: &EntryGatewayConfig,
|
||||
wireguard_data: WireguardGatewayData,
|
||||
) -> Result<EntryGatewayData, EntryGatewayError> {
|
||||
async fn new(config: &EntryGatewayConfig) -> Result<EntryGatewayData, EntryGatewayError> {
|
||||
Ok(EntryGatewayData {
|
||||
mnemonic: config.storage_paths.load_mnemonic_from_file()?,
|
||||
client_storage: nym_gateway::node::PersistentStorage::init(
|
||||
@@ -98,7 +92,6 @@ impl EntryGatewayData {
|
||||
)
|
||||
.await
|
||||
.map_err(nym_gateway::GatewayError::from)?,
|
||||
wireguard_data: wireguard_data.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -386,11 +379,7 @@ 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_data.inner.clone(),
|
||||
)
|
||||
.await?,
|
||||
entry_gateway: EntryGatewayData::new(&config.entry_gateway).await?,
|
||||
exit_gateway: ExitGatewayData::new(&config.exit_gateway)?,
|
||||
wireguard: wireguard_data,
|
||||
config,
|
||||
@@ -599,18 +588,6 @@ impl NymNode {
|
||||
policy: None,
|
||||
};
|
||||
|
||||
let wireguard_private_network = IpNetwork::new(
|
||||
self.config.wireguard.private_ip,
|
||||
self.config.wireguard.private_network_prefix,
|
||||
)?;
|
||||
|
||||
let wg_state = WireguardAppState::new(
|
||||
self.entry_gateway.wireguard_data.clone(),
|
||||
Default::default(),
|
||||
self.config.wireguard.bind_address.port(),
|
||||
wireguard_private_network,
|
||||
)?;
|
||||
|
||||
let mut config = nym_node_http_api::Config::new(bin_info_owned!(), host_details)
|
||||
.with_landing_page_assets(self.config.http.landing_page_assets_path.as_ref())
|
||||
.with_mixnode_details(mixnode_details)
|
||||
@@ -642,7 +619,7 @@ impl NymNode {
|
||||
.with_verloc_stats(self.verloc_stats.clone())
|
||||
.with_metrics_key(self.config.http.access_token.clone());
|
||||
|
||||
Ok(NymNodeRouter::new(config, Some(app_state), Some(wg_state))
|
||||
Ok(NymNodeRouter::new(config, Some(app_state))
|
||||
.build_server(&self.config.http.bind_address)
|
||||
.await?)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user