diff --git a/gateway/src/http/mod.rs b/gateway/src/http/mod.rs index 8a8457f1c6..7b63a522fb 100644 --- a/gateway/src/http/mod.rs +++ b/gateway/src/http/mod.rs @@ -103,10 +103,50 @@ fn load_network_requester_details( ) } +fn load_ip_packet_router_details( + config: &Config, + ip_packet_router_config: &nym_ip_packet_router::Config, +) -> Result { + let identity_public_key: identity::PublicKey = load_public_key( + &ip_packet_router_config + .storage_paths + .common_paths + .keys + .public_identity_key_file, + "ip packet router identity", + )?; + + let dh_public_key: encryption::PublicKey = load_public_key( + &ip_packet_router_config + .storage_paths + .common_paths + .keys + .public_encryption_key_file, + "ip packet router diffie hellman", + )?; + + let gateway_identity_public_key: identity::PublicKey = load_public_key( + &config.storage_paths.keys.public_identity_key_file, + "gateway identity", + )?; + + Ok(api_requests::v1::ip_packet_router::models::IpPacketRouter { + encoded_identity_key: identity_public_key.to_base58_string(), + encoded_x25519_key: dh_public_key.to_base58_string(), + address: Recipient::new( + identity_public_key, + dh_public_key, + gateway_identity_public_key, + ) + .to_string(), + }) +} + pub(crate) struct HttpApiBuilder<'a> { gateway_config: &'a Config, network_requester_config: Option<&'a nym_network_requester::Config>, exit_policy: Option, + ip_packet_router_config: Option<&'a nym_ip_packet_router::Config>, identity_keypair: &'a identity::KeyPair, // TODO: this should be a wg specific key and not re-used sphinx @@ -124,6 +164,7 @@ impl<'a> HttpApiBuilder<'a> { HttpApiBuilder { gateway_config, network_requester_config: None, + ip_packet_router_config: None, exit_policy: None, identity_keypair, sphinx_keypair, @@ -187,6 +228,15 @@ impl<'a> HttpApiBuilder<'a> { self } + #[must_use] + pub(crate) fn with_maybe_ip_packet_router( + mut self, + ip_packet_router_config: Option<&'a nym_ip_packet_router::Config>, + ) -> Self { + self.ip_packet_router_config = ip_packet_router_config; + self + } + #[must_use] pub(crate) fn with_wireguard_client_registry( mut self, @@ -225,6 +275,13 @@ impl<'a> HttpApiBuilder<'a> { } } + if let Some(ipr_config) = self.ip_packet_router_config { + config = config.with_ip_packet_router(load_ip_packet_router_details( + self.gateway_config, + ipr_config, + )?); + } + let wireguard_private_network = IpNetwork::new( IpAddr::from(Ipv4Addr::new(10, 1, 0, 0)), self.gateway_config.wireguard.private_network_prefix, diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index fa0f8b35b0..dc35050785 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -519,6 +519,7 @@ impl Gateway { .with_wireguard_client_registry(self.client_registry.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)) .start(shutdown.subscribe().named("http-api"))?; // Once this is a bit more mature, make this a commandline flag instead of a compile time diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index 35fad2b7c4..142a607e5a 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -366,6 +366,9 @@ pub struct NymNodeDescription { #[serde(default)] pub network_requester: Option, + #[serde(default)] + pub ip_packet_router: Option, + // for now we only care about their ws/wss situation, nothing more pub mixnet_websockets: WebSockets, } @@ -393,3 +396,9 @@ pub struct NetworkRequesterDetails { /// flag indicating whether this network requester uses the exit policy rather than the deprecated allow list pub uses_exit_policy: bool, } + +#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)] +pub struct IpPacketRouterDetails { + /// address of the embedded ip packet router + pub address: String, +} diff --git a/nym-api/src/node_describe_cache/mod.rs b/nym-api/src/node_describe_cache/mod.rs index 0a11a5224a..21b621f428 100644 --- a/nym-api/src/node_describe_cache/mod.rs +++ b/nym-api/src/node_describe_cache/mod.rs @@ -7,7 +7,9 @@ use crate::support::caching::refresher::{CacheItemProvider, CacheRefresher}; use crate::support::config; use crate::support::config::DEFAULT_NODE_DESCRIBE_BATCH_SIZE; use futures_util::{stream, StreamExt}; -use nym_api_requests::models::{NetworkRequesterDetails, NymNodeDescription}; +use nym_api_requests::models::{ + IpPacketRouterDetails, NetworkRequesterDetails, NymNodeDescription, +}; use nym_config::defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT}; use nym_contracts_common::IdentityKey; use nym_mixnet_contract_common::Gateway; @@ -170,10 +172,19 @@ async fn get_gateway_description( None }; + let ip_packet_router = if let Ok(ipr) = client.get_ip_packet_router().await { + Some(IpPacketRouterDetails { + address: ipr.address, + }) + } else { + None + }; + let description = NymNodeDescription { host_information: host_info.data, build_information: build_info, network_requester, + ip_packet_router, mixnet_websockets: websockets, }; diff --git a/nym-node/nym-node-requests/src/api/client.rs b/nym-node/nym-node-requests/src/api/client.rs index c4e6dde33a..c4c89e2514 100644 --- a/nym-node/nym-node-requests/src/api/client.rs +++ b/nym-node/nym-node-requests/src/api/client.rs @@ -11,6 +11,7 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned; use nym_wireguard_types::{ClientMessage, ClientRegistrationResponse}; use crate::api::v1::health::models::NodeHealth; +use crate::api::v1::ip_packet_router::models::IpPacketRouter; use crate::api::v1::network_requester::exit_policy::models::UsedExitPolicy; use crate::api::v1::network_requester::models::NetworkRequester; pub use http_api_client::Client; @@ -54,6 +55,11 @@ pub trait NymNodeApiClientExt: ApiClient { .await } + async fn get_ip_packet_router(&self) -> Result { + self.get_json_from(routes::api::v1::ip_packet_router_absolute()) + .await + } + async fn post_gateway_register_client( &self, client_message: &ClientMessage, diff --git a/nym-node/nym-node-requests/src/api/v1/ip_packet_router/mod.rs b/nym-node/nym-node-requests/src/api/v1/ip_packet_router/mod.rs new file mode 100644 index 0000000000..c57081432b --- /dev/null +++ b/nym-node/nym-node-requests/src/api/v1/ip_packet_router/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod models; diff --git a/nym-node/nym-node-requests/src/api/v1/ip_packet_router/models.rs b/nym-node/nym-node-requests/src/api/v1/ip_packet_router/models.rs new file mode 100644 index 0000000000..eea0783b67 --- /dev/null +++ b/nym-node/nym-node-requests/src/api/v1/ip_packet_router/models.rs @@ -0,0 +1,18 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +pub struct IpPacketRouter { + /// Base58 encoded ed25519 EdDSA public key of the ip-packet-router. + pub encoded_identity_key: String, + + /// Base58-encoded x25519 public key used for performing key exchange with remote clients. + pub encoded_x25519_key: String, + + /// Nym address of this ip packet router. + pub address: String, +} diff --git a/nym-node/nym-node-requests/src/api/v1/mod.rs b/nym-node/nym-node-requests/src/api/v1/mod.rs index b07fdbb7aa..33d67d598e 100644 --- a/nym-node/nym-node-requests/src/api/v1/mod.rs +++ b/nym-node/nym-node-requests/src/api/v1/mod.rs @@ -3,6 +3,7 @@ pub mod gateway; pub mod health; +pub mod ip_packet_router; pub mod mixnode; pub mod network_requester; pub mod node; diff --git a/nym-node/nym-node-requests/src/api/v1/node/models.rs b/nym-node/nym-node-requests/src/api/v1/node/models.rs index 260752436b..3b79601a00 100644 --- a/nym-node/nym-node-requests/src/api/v1/node/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/node/models.rs @@ -14,6 +14,7 @@ pub struct NodeRoles { pub mixnode_enabled: bool, pub gateway_enabled: bool, pub network_requester_enabled: bool, + pub ip_packet_router_enabled: bool, } #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)] diff --git a/nym-node/nym-node-requests/src/lib.rs b/nym-node/nym-node-requests/src/lib.rs index cb7942f861..9bb345eca9 100644 --- a/nym-node/nym-node-requests/src/lib.rs +++ b/nym-node/nym-node-requests/src/lib.rs @@ -33,6 +33,7 @@ pub mod routes { pub const GATEWAY: &str = "/gateway"; pub const MIXNODE: &str = "/mixnode"; pub const NETWORK_REQUESTER: &str = "/network-requester"; + pub const IP_PACKET_ROUTER: &str = "/ip-packet-router"; pub const SWAGGER: &str = "/swagger"; // define helper functions to get absolute routes @@ -43,6 +44,7 @@ pub mod routes { absolute_route!(gateway_absolute, v1_absolute(), GATEWAY); absolute_route!(mixnode_absolute, v1_absolute(), MIXNODE); absolute_route!(network_requester_absolute, v1_absolute(), NETWORK_REQUESTER); + absolute_route!(ip_packet_router_absolute, v1_absolute(), IP_PACKET_ROUTER); absolute_route!(swagger_absolute, v1_absolute(), SWAGGER); pub mod gateway { @@ -96,6 +98,10 @@ pub mod routes { EXIT_POLICY ); } + + pub mod ip_packet_router { + // use super::*; + } } } } @@ -146,5 +152,9 @@ mod tests { "/api/v1/network-requester", routes::api::v1::network_requester_absolute() ); + assert_eq!( + "/api/v1/ip-packet-router", + routes::api::v1::ip_packet_router_absolute() + ); } } diff --git a/nym-node/src/http/router/api/v1/ip_packet_router/mod.rs b/nym-node/src/http/router/api/v1/ip_packet_router/mod.rs new file mode 100644 index 0000000000..ab6b2ed72e --- /dev/null +++ b/nym-node/src/http/router/api/v1/ip_packet_router/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use axum::routing::get; +use axum::Router; +use nym_node_requests::api::v1::ip_packet_router::models; + +pub mod root; + +#[derive(Debug, Clone, Default)] +pub struct Config { + pub details: Option, +} + +pub(crate) fn routes(config: Config) -> Router { + Router::new().route( + "/", + get({ + let ip_packet_router_details = config.details; + move |query| root::root_ip_packet_router(ip_packet_router_details, query) + }), + ) +} diff --git a/nym-node/src/http/router/api/v1/ip_packet_router/root.rs b/nym-node/src/http/router/api/v1/ip_packet_router/root.rs new file mode 100644 index 0000000000..498bf7c084 --- /dev/null +++ b/nym-node/src/http/router/api/v1/ip_packet_router/root.rs @@ -0,0 +1,33 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use crate::http::router::api::{FormattedResponse, OutputParams}; +use axum::extract::Query; +use axum::http::StatusCode; +use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter; + +/// Returns root network requester information +#[utoipa::path( + get, + path = "", + context_path = "/api/v1/ip-packet-router", + tag = "IP Packet Router", + responses( + (status = 501, description = "the endpoint hasn't been implemented yet"), + (status = 200, content( + ("application/json" = IpPacketRouter), + ("application/yaml" = IpPacketRouter) + )) + ), + params(OutputParams) +)] +pub(crate) async fn root_ip_packet_router( + details: Option, + Query(output): Query, +) -> Result { + let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(details)) +} + +pub type IpPacketRouterResponse = FormattedResponse; diff --git a/nym-node/src/http/router/api/v1/mod.rs b/nym-node/src/http/router/api/v1/mod.rs index f9415d5309..97d7aff172 100644 --- a/nym-node/src/http/router/api/v1/mod.rs +++ b/nym-node/src/http/router/api/v1/mod.rs @@ -9,6 +9,7 @@ use nym_node_requests::routes::api::v1; pub mod gateway; pub mod health; +pub mod ip_packet_router; pub mod mixnode; pub mod network_requester; pub mod node; @@ -20,6 +21,7 @@ pub struct Config { pub gateway: gateway::Config, pub mixnode: mixnode::Config, pub network_requester: network_requester::Config, + pub ip_packet_router: ip_packet_router::Config, } pub(super) fn routes(config: Config, initial_wg_state: WireguardAppState) -> Router { @@ -34,6 +36,10 @@ pub(super) fn routes(config: Config, initial_wg_state: WireguardAppState) -> Rou v1::NETWORK_REQUESTER, network_requester::routes(config.network_requester), ) + .nest( + v1::IP_PACKET_ROUTER, + ip_packet_router::routes(config.ip_packet_router), + ) .merge(node::routes(config.node)) .merge(openapi::route()) } diff --git a/nym-node/src/http/router/api/v1/openapi.rs b/nym-node/src/http/router/api/v1/openapi.rs index d016de72fb..e23eca84f3 100644 --- a/nym-node/src/http/router/api/v1/openapi.rs +++ b/nym-node/src/http/router/api/v1/openapi.rs @@ -27,6 +27,7 @@ use utoipa_swagger_ui::SwaggerUi; api::v1::mixnode::root::root_mixnode, api::v1::network_requester::root::root_network_requester, api::v1::network_requester::exit_policy::node_exit_policy, + api::v1::ip_packet_router::root::root_ip_packet_router, ), components( schemas( @@ -56,6 +57,7 @@ use utoipa_swagger_ui::SwaggerUi; api_requests::v1::network_requester::exit_policy::models::AddressPortPattern, api_requests::v1::network_requester::exit_policy::models::PortRange, api_requests::v1::network_requester::exit_policy::models::UsedExitPolicy, + api_requests::v1::ip_packet_router::models::IpPacketRouter, ), responses(RequestError) ) diff --git a/nym-node/src/http/router/mod.rs b/nym-node/src/http/router/mod.rs index e1d9e03b35..71de3c3c8d 100644 --- a/nym-node/src/http/router/mod.rs +++ b/nym-node/src/http/router/mod.rs @@ -8,6 +8,7 @@ use crate::http::state::AppState; use crate::http::NymNodeHTTPServer; use axum::Router; use nym_node_requests::api::v1::gateway::models::{Gateway, Wireguard}; +use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter; use nym_node_requests::api::v1::mixnode::models::Mixnode; use nym_node_requests::api::v1::network_requester::exit_policy::models::UsedExitPolicy; use nym_node_requests::api::v1::network_requester::models::NetworkRequester; @@ -45,6 +46,7 @@ impl Config { gateway: Default::default(), mixnode: Default::default(), network_requester: Default::default(), + ip_packet_router: Default::default(), }, }, } @@ -94,6 +96,13 @@ impl Config { self.api.v1_config.network_requester.exit_policy = Some(exit_policy); self } + + #[must_use] + pub fn with_ip_packet_router(mut self, ip_packet_router: IpPacketRouter) -> Self { + self.api.v1_config.node.roles.ip_packet_router_enabled = true; + self.api.v1_config.ip_packet_router.details = Some(ip_packet_router); + self + } } pub struct NymNodeRouter {