From e8d601df67625a3bf85cd2a4714d05848c7066eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 2 Oct 2023 15:07:16 +0100 Subject: [PATCH] expanded on gateway-related endpoints --- Cargo.lock | 53 +++++--- gateway/src/http/mod.rs | 17 ++- nym-node/Cargo.toml | 7 +- .../api/v1/gateway/client_interfaces/mod.rs | 119 ++++++++++++++++++ .../src/http/router/api/v1/gateway/mod.rs | 21 ++-- .../src/http/router/api/v1/gateway/root.rs | 1 + .../src/http/router/api/v1/gateway/types.rs | 31 ++++- .../router/api/v1/network_requester/types.rs | 15 ++- nym-node/src/http/router/api/v1/node/types.rs | 3 +- nym-node/src/http/router/api/v1/openapi.rs | 6 + 10 files changed, 236 insertions(+), 37 deletions(-) create mode 100644 nym-node/src/http/router/api/v1/gateway/client_interfaces/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 731a84b0c4..20367b6860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2111,6 +2111,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", + "digest 0.10.7", "fiat-crypto", "platforms", "rustc_version 0.4.0", @@ -2706,6 +2707,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek 4.1.0", + "ed25519 2.2.2", + "rand_core 0.6.4", + "serde", + "sha2 0.10.7", + "zeroize", +] + [[package]] name = "ed25519-zebra" version = "3.1.0" @@ -2868,7 +2883,7 @@ dependencies = [ "libp2p 0.51.3", "libp2p-identity", "log", - "lru 0.10.0", + "lru 0.10.1", "nym-config", "nym-ephemera-common", "nym-task", @@ -4610,7 +4625,7 @@ source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c dependencies = [ "asn1_der", "bs58 0.4.0", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "either", "fnv", "futures", @@ -4777,12 +4792,12 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" +checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce" dependencies = [ "bs58 0.4.0", - "ed25519-dalek", + "ed25519-dalek 2.0.0", "log", "multiaddr 0.17.1", "multihash", @@ -5384,9 +5399,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ "hashbrown 0.13.2", ] @@ -6390,7 +6405,7 @@ dependencies = [ "cipher 0.4.4", "ctr 0.9.2", "digest 0.10.7", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "generic-array 0.14.7", "hkdf 0.12.3", "hmac 0.12.1", @@ -8712,9 +8727,9 @@ dependencies = [ [[package]] name = "refinery" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb0436d0dd7bd8d4fce1e828751fa79742b08e35f27cfea7546f8a322b5ef24" +checksum = "529664dbccc0a296947615c997a857912d72d1c44be1fafb7bae54ecfa7a8c24" dependencies = [ "refinery-core", "refinery-macros", @@ -8722,9 +8737,9 @@ dependencies = [ [[package]] name = "refinery-core" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19206547cd047e8f4dfa6b20c30d3ecaf24be05841b6aa0aa926a47a3d0662bb" +checksum = "e895cb870cf06e92318cbbeb701f274d022d5ca87a16fa8244e291cd035ef954" dependencies = [ "async-trait", "cfg-if", @@ -8743,9 +8758,9 @@ dependencies = [ [[package]] name = "refinery-macros" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94d4b9241859ba19eaa5c04c86e782eb3aa0aae2c5868e0cfa90c856e58a174" +checksum = "123e8b80f8010c3ae38330c81e76938fc7adf6cdbfbaad20295bb8c22718b4f1" dependencies = [ "proc-macro2", "quote", @@ -9705,6 +9720,12 @@ dependencies = [ "dirs 4.0.0", ] +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + [[package]] name = "signal-hook" version = "0.3.17" @@ -9757,9 +9778,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "54ac45299ccbd390721be55b412d41931911f654fa99e2cb8bfb57184b2061fe" [[package]] name = "slab" diff --git a/gateway/src/http/mod.rs b/gateway/src/http/mod.rs index 8d2c64d37b..b74e2dd55c 100644 --- a/gateway/src/http/mod.rs +++ b/gateway/src/http/mod.rs @@ -11,9 +11,17 @@ use nym_sphinx::addressing::clients::Recipient; use nym_task::TaskClient; fn load_gateway_details( - _config: &Config, + config: &Config, ) -> Result { - Ok(http::api::v1::gateway::types::Gateway {}) + Ok(http::api::v1::gateway::types::Gateway { + client_interfaces: http::api::v1::gateway::types::ClientInterfaces { + wireguard: None, + mixnet_websockets: Some(http::api::v1::gateway::types::WebSockets { + ws_port: config.gateway.clients_port, + wss_port: None, + }), + }, + }) } fn load_host_details( @@ -31,7 +39,7 @@ fn load_host_details( Ok(http::api::v1::node::types::HostInformation { // TODO: this should be extracted differently, i.e. it's the issue of the public/private address - ip_address: vec![config.gateway.listening_address], + ip_address: vec![config.gateway.listening_address.to_string()], hostname: None, keys: http::api::v1::node::types::HostKeys { ed25519: identity_public_key.to_base58_string(), @@ -69,7 +77,7 @@ fn load_network_requester_details( Ok(http::api::v1::network_requester::types::NetworkRequester { encoded_identity_key: identity_public_key.to_base58_string(), - encoded_sphinx_key: dh_public_key.to_base58_string(), + encoded_x25519_key: dh_public_key.to_base58_string(), address: Recipient::new( identity_public_key, dh_public_key, @@ -91,6 +99,7 @@ pub(crate) fn start_http_api( // and makes the code a bit nicer to manage. on top of it, all of it will refactored anyway at some point // (famous last words, eh? - 22.09.23) + // TODO: load private key, set zeroizing wrapper and sign whatever responses we need to sign let mut config = nym_node::http::Config::new(bin_info_owned!(), load_host_details(gateway_config)?) .with_gateway(load_gateway_details(gateway_config)?); diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index 8b4c87ae1d..13cbfe4c3d 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -29,7 +29,12 @@ mime = "0.3.17" hyper = { workspace = true } tower = { version = "0.4.13" } tower-http = { version = "0.4.4", features = ["fs"] } -utoipa = { workspace = true, features = [] } # can't use `"axum_extras"` feature because ephemera uses `"actix_extras"` -.-' + +# `actix_extras`? what the hell? +# hear me out first! +# we can't use `axum_extras` because of freaking ephemera that depends on `actix_extras`. +# however, it seems that pulling in `actix_extras` pulls in just enough shared features to improve `IntoParams` for our Query attributes +utoipa = { workspace = true, features = ["actix_extras"] } # can't use `"axum_extras"` feature because ephemera uses `"actix_extras"` -.-' utoipa-swagger-ui = { workspace = true, features = ["axum"] } # if we ever wanted redoc/rapidoc bridges: diff --git a/nym-node/src/http/router/api/v1/gateway/client_interfaces/mod.rs b/nym-node/src/http/router/api/v1/gateway/client_interfaces/mod.rs new file mode 100644 index 0000000000..ee7f879993 --- /dev/null +++ b/nym-node/src/http/router/api/v1/gateway/client_interfaces/mod.rs @@ -0,0 +1,119 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::http::api::v1::gateway::types::{ClientInterfaces, WebSockets, Wireguard}; +use crate::http::api::{FormattedResponse, OutputParams}; +use crate::http::state::AppState; +use axum::extract::Query; +use axum::http::StatusCode; +use axum::routing::get; +use axum::Router; + +pub(crate) mod routes { + pub(crate) const INTERFACES: &str = "/"; + pub(crate) const WIREGUARD: &str = "/wireguard"; + pub(crate) const WEBSOCKETS: &str = "/mixnet-websockets"; +} + +pub(crate) fn routes(interfaces: Option) -> Router { + Router::new() + .route( + routes::INTERFACES, + get({ + let interfaces = interfaces.clone(); + move |query| client_interfaces(interfaces, query) + }), + ) + .route( + routes::WEBSOCKETS, + get({ + let websockets = interfaces.as_ref().and_then(|i| i.mixnet_websockets); + move |query| mixnet_websockets(websockets, query) + }), + ) + .route( + routes::WIREGUARD, + get({ + let wireguard_info = interfaces.and_then(|i| i.wireguard); + move |query| wireguard(wireguard_info, query) + }), + ) +} + +/// Returns client interfaces supported by this gateway. +#[utoipa::path( + get, + path = "/client-interfaces", + context_path = "/api/v1/gateway", + tag = "Gateway", + responses( + (status = 501, description = "the endpoint hasn't been implemented yet"), + (status = 200, content( + ("application/json" = ClientInterfaces), + ("application/yaml" = ClientInterfaces) + )) + ), + params(OutputParams) +)] +pub(crate) async fn client_interfaces( + interfaces: Option, + Query(output): Query, +) -> Result { + let interfaces = interfaces.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(interfaces)) +} + +pub type ClientInterfacesResponse = FormattedResponse; + +/// 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( + wireguard: Option, + Query(output): Query, +) -> Result { + let wireguard = wireguard.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(wireguard)) +} + +pub type WireguardResponse = FormattedResponse; + +/// Returns client interfaces supported by this gateway. +#[utoipa::path( + get, + path = "/mixnet-websockets", + 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" = WebSockets), + ("application/yaml" = WebSockets) + )) + ), + params(OutputParams) +)] +pub(crate) async fn mixnet_websockets( + websockets: Option, + Query(output): Query, +) -> Result { + let websockets = websockets.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(websockets)) +} + +pub type MixnetWebSocketsResponse = FormattedResponse; diff --git a/nym-node/src/http/router/api/v1/gateway/mod.rs b/nym-node/src/http/router/api/v1/gateway/mod.rs index 8946b18f51..fc8fcda65a 100644 --- a/nym-node/src/http/router/api/v1/gateway/mod.rs +++ b/nym-node/src/http/router/api/v1/gateway/mod.rs @@ -5,11 +5,13 @@ use crate::http::state::AppState; use axum::routing::get; use axum::Router; +pub mod client_interfaces; pub mod root; pub mod types; pub(crate) mod routes { pub(crate) const ROOT: &str = "/"; + pub(crate) const CLIENT_INTERFACES: &str = "/client-interfaces"; } #[derive(Debug, Clone, Default)] @@ -18,11 +20,16 @@ pub struct Config { } pub(crate) fn routes(config: Config) -> Router { - Router::new().route( - routes::ROOT, - get({ - let gateway_details = config.details; - move |query| root::root_gateway(gateway_details, query) - }), - ) + Router::new() + .route( + routes::ROOT, + get({ + let gateway_details = config.details.clone(); + move |query| root::root_gateway(gateway_details, query) + }), + ) + .nest( + routes::CLIENT_INTERFACES, + client_interfaces::routes(config.details.map(|g| g.client_interfaces)), + ) } diff --git a/nym-node/src/http/router/api/v1/gateway/root.rs b/nym-node/src/http/router/api/v1/gateway/root.rs index 7044213f1f..1904c0ddc4 100644 --- a/nym-node/src/http/router/api/v1/gateway/root.rs +++ b/nym-node/src/http/router/api/v1/gateway/root.rs @@ -25,6 +25,7 @@ pub(crate) async fn root_gateway( details: Option, Query(output): Query, ) -> Result { + println!("query: {:?}", output); let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?; let output = output.output.unwrap_or_default(); Ok(output.to_response(details)) diff --git a/nym-node/src/http/router/api/v1/gateway/types.rs b/nym-node/src/http/router/api/v1/gateway/types.rs index 19893fb3f2..9ae012ea9a 100644 --- a/nym-node/src/http/router/api/v1/gateway/types.rs +++ b/nym-node/src/http/router/api/v1/gateway/types.rs @@ -6,10 +6,29 @@ use utoipa::ToSchema; #[derive(Serialize, Debug, Clone, ToSchema)] pub struct Gateway { - // /// Base58 encoded ed25519 EdDSA public key of the gateway used for deriving shared keys with clients - // /// and for signing any messages - // pub encoded_identity_key: String, - // - // /// Base58-encoded x25519 public key used for sphinx key derivation. - // pub encoded_sphinx_key: String, + pub client_interfaces: ClientInterfaces, +} + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct Wireguard { + #[schema(example = 1234, default = 51820)] + pub port: u16, + + pub public_key: String, +} + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct ClientInterfaces { + pub wireguard: Option, + + pub mixnet_websockets: Option, + // pub mixnet_tcp: +} + +#[derive(Serialize, Debug, Clone, Copy, ToSchema)] +pub struct WebSockets { + #[schema(example = 1234, default = 9000)] + pub ws_port: u16, + + pub wss_port: Option, } diff --git a/nym-node/src/http/router/api/v1/network_requester/types.rs b/nym-node/src/http/router/api/v1/network_requester/types.rs index af84874fbc..370e807a30 100644 --- a/nym-node/src/http/router/api/v1/network_requester/types.rs +++ b/nym-node/src/http/router/api/v1/network_requester/types.rs @@ -10,8 +10,21 @@ pub struct NetworkRequester { pub encoded_identity_key: String, /// Base58-encoded x25519 public key used for performing key exchange with remote clients. - pub encoded_sphinx_key: String, + pub encoded_x25519_key: String, /// Nym address of this network requester. pub address: String, } + +// #[derive(Serialize, Debug, Clone, ToSchema)] +// pub struct ExitPolicy { +// // pub allowed_ports: +// // pub deny_list: DenyList, +// } +// +// #[derive(Serialize, Debug, Clone, ToSchema)] +// pub struct DenyListEntry { +// // pub ports: +// // pub ips: +// pub description: String, +// } diff --git a/nym-node/src/http/router/api/v1/node/types.rs b/nym-node/src/http/router/api/v1/node/types.rs index 8d78ffbb9a..e852fdb8c3 100644 --- a/nym-node/src/http/router/api/v1/node/types.rs +++ b/nym-node/src/http/router/api/v1/node/types.rs @@ -3,7 +3,6 @@ pub(crate) use nym_bin_common::build_information::BinaryBuildInformationOwned; use serde::Serialize; -use std::net::IpAddr; use utoipa::ToSchema; #[derive(Clone, Default, Debug, Copy, ToSchema, Serialize)] @@ -16,7 +15,7 @@ pub struct NodeRoles { #[derive(Clone, Default, Debug, ToSchema, Serialize)] pub struct HostInformation { /// Ip address(es) of this host, such as `1.1.1.1`. - pub ip_address: Vec, + pub ip_address: Vec, /// Optional hostname of this node, for example `nymtech.net`. pub hostname: Option, diff --git a/nym-node/src/http/router/api/v1/openapi.rs b/nym-node/src/http/router/api/v1/openapi.rs index bf3d4d13b6..581d293a90 100644 --- a/nym-node/src/http/router/api/v1/openapi.rs +++ b/nym-node/src/http/router/api/v1/openapi.rs @@ -15,6 +15,9 @@ use utoipa_swagger_ui::SwaggerUi; api::v1::node::host_information::host_information, api::v1::node::roles::roles, api::v1::gateway::root::root_gateway, + api::v1::gateway::client_interfaces::client_interfaces, + api::v1::gateway::client_interfaces::wireguard, + api::v1::gateway::client_interfaces::mixnet_websockets, api::v1::mixnode::root::root_mixnode, api::v1::network_requester::root::root_network_requester, ), @@ -26,6 +29,9 @@ use utoipa_swagger_ui::SwaggerUi; api::v1::node::types::HostKeys, api::v1::node::types::NodeRoles, api::v1::gateway::types::Gateway, + api::v1::gateway::types::Wireguard, + api::v1::gateway::types::ClientInterfaces, + api::v1::gateway::types::WebSockets, api::v1::mixnode::types::Mixnode, api::v1::network_requester::types::NetworkRequester, ))