Compare commits

...

4 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacşu f008c33eb5 Remove filtering of gateways 2023-11-08 12:42:20 +02:00
Bogdan-Ștefan Neacşu deb214d176 Enable wireguard for testing purposes 2023-11-06 15:12:47 +02:00
Bogdan-Ștefan Neacşu a376fec990 Merge remote-tracking branch 'origin/develop' into feature/wg_private_ip 2023-11-06 15:11:49 +02:00
Bogdan-Ștefan Neacşu d1303947c0 Add private ip assignment 2023-11-01 14:24:08 +02:00
13 changed files with 93 additions and 7 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --release args: --workspace --release --features wireguard
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -51,7 +51,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --release args: --workspace --release --features wireguard
- name: Prepare build output - name: Prepare build output
shell: bash shell: bash
Generated
+11
View File
@@ -4282,6 +4282,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "ipnetwork"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "ipnetwork" name = "ipnetwork"
version = "0.18.0" version = "0.18.0"
@@ -6532,6 +6541,7 @@ dependencies = [
"futures", "futures",
"humantime-serde", "humantime-serde",
"hyper", "hyper",
"ipnetwork 0.16.0",
"lazy_static", "lazy_static",
"log", "log",
"nym-api-requests", "nym-api-requests",
@@ -6898,6 +6908,7 @@ dependencies = [
"fastrand 2.0.1", "fastrand 2.0.1",
"hmac 0.12.1", "hmac 0.12.1",
"hyper", "hyper",
"ipnetwork 0.16.0",
"mime", "mime",
"nym-config", "nym-config",
"nym-crypto", "nym-crypto",
+2 -2
View File
@@ -74,8 +74,8 @@ pub async fn current_gateways<R: Rng>(
.collect::<Vec<gateway::Node>>(); .collect::<Vec<gateway::Node>>();
// we were always filtering by version so I'm not removing that 'feature' // we were always filtering by version so I'm not removing that 'feature'
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION")); // let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
Ok(filtered_gateways) Ok(valid_gateways)
} }
pub async fn current_mixnodes<R: Rng>( pub async fn current_mixnodes<R: Rng>(
+16 -1
View File
@@ -6,6 +6,7 @@ use crate::PeerPublicKey;
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use dashmap::DashMap; use dashmap::DashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::{fmt, ops::Deref, str::FromStr}; use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
@@ -17,11 +18,13 @@ use sha2::Sha256;
pub type GatewayClientRegistry = DashMap<PeerPublicKey, GatewayClient>; pub type GatewayClientRegistry = DashMap<PeerPublicKey, GatewayClient>;
pub type PendingRegistrations = DashMap<PeerPublicKey, Nonce>; pub type PendingRegistrations = DashMap<PeerPublicKey, Nonce>;
pub type PrivateIPs = DashMap<IpAddr, Free>;
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
pub type HmacSha256 = Hmac<Sha256>; pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64; pub type Nonce = u64;
pub type Free = bool;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
@@ -72,6 +75,9 @@ pub struct GatewayClient {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))] #[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
/// Assigned private IP
pub private_ip: IpAddr,
/// Sha256 hmac on the data (alongside the prior nonce) /// Sha256 hmac on the data (alongside the prior nonce)
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))] #[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub mac: ClientMac, pub mac: ClientMac,
@@ -79,7 +85,12 @@ pub struct GatewayClient {
impl GatewayClient { impl GatewayClient {
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
pub fn new(local_secret: &PrivateKey, remote_public: PublicKey, nonce: u64) -> Self { pub fn new(
local_secret: &PrivateKey,
remote_public: PublicKey,
private_ip: IpAddr,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek // convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]
let static_secret = boringtun::x25519::StaticSecret::try_from(local_secret.to_bytes()) let static_secret = boringtun::x25519::StaticSecret::try_from(local_secret.to_bytes())
@@ -96,10 +107,12 @@ impl GatewayClient {
.expect("x25519 shared secret is always 32 bytes long"); .expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes()); mac.update(local_public.as_bytes());
mac.update(private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes()); mac.update(&nonce.to_le_bytes());
GatewayClient { GatewayClient {
pub_key: PeerPublicKey::new(local_public), pub_key: PeerPublicKey::new(local_public),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()), mac: ClientMac(mac.finalize().into_bytes().to_vec()),
} }
} }
@@ -121,6 +134,7 @@ impl GatewayClient {
.expect("x25519 shared secret is always 32 bytes long"); .expect("x25519 shared secret is always 32 bytes long");
mac.update(self.pub_key.as_bytes()); mac.update(self.pub_key.as_bytes());
mac.update(self.private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes()); mac.update(&nonce.to_le_bytes());
mac.verify_slice(&self.mac) mac.verify_slice(&self.mac)
@@ -209,6 +223,7 @@ mod tests {
let client = GatewayClient::new( let client = GatewayClient::new(
client_key_pair.private_key(), client_key_pair.private_key(),
*gateway_key_pair.public_key(), *gateway_key_pair.public_key(),
"10.0.0.42".parse().unwrap(),
nonce, nonce,
); );
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok()) assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
+1
View File
@@ -27,6 +27,7 @@ dirs = "4.0"
dotenvy = { workspace = true } dotenvy = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
humantime-serde = "1.0.1" humantime-serde = "1.0.1"
ipnetwork = "0.16"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = { workspace = true } log = { workspace = true }
once_cell = "1.7.2" once_cell = "1.7.2"
+1
View File
@@ -128,6 +128,7 @@ impl From<ConfigV1_1_31> for Config {
enabled: value.wireguard.enabled, enabled: value.wireguard.enabled,
bind_address: value.wireguard.bind_address, bind_address: value.wireguard.bind_address,
announced_port: value.wireguard.announced_port, announced_port: value.wireguard.announced_port,
private_network_prefix: Default::default(),
storage_paths: nym_node::config::persistence::WireguardPaths { storage_paths: nym_node::config::persistence::WireguardPaths {
// no fields (yet) // no fields (yet)
}, },
+6
View File
@@ -134,6 +134,12 @@ pub(crate) enum GatewayError {
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors // TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
#[error(transparent)] #[error(transparent)]
NymNodeError(#[from] nym_node::error::NymNodeError), NymNodeError(#[from] nym_node::error::NymNodeError),
#[error("there was an issue with wireguard IP network: {source}")]
IpNetworkError {
#[from]
source: ipnetwork::IpNetworkError,
},
} }
impl From<ClientCoreError> for GatewayError { impl From<ClientCoreError> for GatewayError {
+7
View File
@@ -4,6 +4,7 @@
use crate::config::Config; use crate::config::Config;
use crate::error::GatewayError; use crate::error::GatewayError;
use crate::node::helpers::load_public_key; use crate::node::helpers::load_public_key;
use ipnetwork::IpNetwork;
use log::warn; use log::warn;
use nym_bin_common::bin_info_owned; use nym_bin_common::bin_info_owned;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
@@ -16,6 +17,7 @@ use nym_node::http::router::WireguardAppState;
use nym_node::wireguard::types::GatewayClientRegistry; use nym_node::wireguard::types::GatewayClientRegistry;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient; use nym_task::TaskClient;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc; use std::sync::Arc;
fn load_gateway_details( fn load_gateway_details(
@@ -223,12 +225,17 @@ impl<'a> HttpApiBuilder<'a> {
} }
} }
let wireguard_private_network = IpNetwork::new(
IpAddr::from(Ipv4Addr::new(10, 0, 0, 0)),
self.gateway_config.wireguard.private_network_prefix,
)?;
let wg_state = self.client_registry.map(|client_registry| { let wg_state = self.client_registry.map(|client_registry| {
WireguardAppState::new( WireguardAppState::new(
self.sphinx_keypair, self.sphinx_keypair,
client_registry, client_registry,
Default::default(), Default::default(),
self.gateway_config.wireguard.bind_address.port(), self.gateway_config.wireguard.bind_address.port(),
wireguard_private_network,
) )
}); });
+2
View File
@@ -14,6 +14,8 @@ license.workspace = true
anyhow = { workspace = true } anyhow = { workspace = true }
bytes = "1.5.0" bytes = "1.5.0"
colored = "2" colored = "2"
ipnetwork = "0.16"
rand = "0.7.3"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_yaml = "0.9.25" serde_yaml = "0.9.25"
serde_json = { workspace = true } serde_json = { workspace = true }
+6
View File
@@ -12,6 +12,7 @@ pub mod persistence;
mod serde_helpers; mod serde_helpers;
pub const DEFAULT_WIREGUARD_PORT: u16 = WG_PORT; pub const DEFAULT_WIREGUARD_PORT: u16 = WG_PORT;
pub const DEFAULT_WIREGUARD_PREFIX: u8 = 16;
pub const DEFAULT_HTTP_PORT: u16 = DEFAULT_NYM_NODE_HTTP_PORT; pub const DEFAULT_HTTP_PORT: u16 = DEFAULT_NYM_NODE_HTTP_PORT;
// TODO: this is very much a WIP. we need proper ssl certificate support here // TODO: this is very much a WIP. we need proper ssl certificate support here
@@ -75,6 +76,10 @@ pub struct Wireguard {
/// Useful in the instances where the node is behind a proxy. /// Useful in the instances where the node is behind a proxy.
pub announced_port: u16, pub announced_port: u16,
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard.
/// The maximum value for IPv4 is 32 and for IPv6 is 128
pub private_network_prefix: u8,
/// Paths for wireguard keys, client registries, etc. /// Paths for wireguard keys, client registries, etc.
pub storage_paths: persistence::WireguardPaths, pub storage_paths: persistence::WireguardPaths,
} }
@@ -88,6 +93,7 @@ impl Default for Wireguard {
DEFAULT_WIREGUARD_PORT, DEFAULT_WIREGUARD_PORT,
), ),
announced_port: DEFAULT_WIREGUARD_PORT, announced_port: DEFAULT_WIREGUARD_PORT,
private_network_prefix: DEFAULT_WIREGUARD_PREFIX,
storage_paths: persistence::WireguardPaths {}, storage_paths: persistence::WireguardPaths {},
} }
} }
@@ -14,6 +14,7 @@ use nym_crypto::asymmetric::encryption::PublicKey;
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{ use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey,
}; };
use rand::{prelude::IteratorRandom, thread_rng};
async fn process_final_message( async fn process_final_message(
client: GatewayClient, client: GatewayClient,
@@ -91,8 +92,23 @@ pub(crate) async fn register_client(
let remote_public = PublicKey::from_bytes(init.pub_key().as_bytes()) let remote_public = PublicKey::from_bytes(init.pub_key().as_bytes())
.map_err(|_| RequestError::new_status(StatusCode::BAD_REQUEST))?; .map_err(|_| RequestError::new_status(StatusCode::BAD_REQUEST))?;
let nonce = process_init_message(init, state).await; let nonce = process_init_message(init, state).await;
let gateway_data = let mut private_ip_ref = state
GatewayClient::new(state.dh_keypair.private_key(), remote_public, nonce); .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.dh_keypair.private_key(),
remote_public,
*private_ip_ref.key(),
nonce,
);
let response = ClientRegistrationResponse::PendingRegistration { let response = ClientRegistrationResponse::PendingRegistration {
nonce, nonce,
gateway_data, gateway_data,
@@ -7,8 +7,10 @@ use crate::http::api::v1::gateway::client_interfaces::wireguard::client_registry
use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations}; use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
use axum::routing::{get, post}; use axum::routing::{get, post};
use axum::Router; use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption; use nym_crypto::asymmetric::encryption;
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard; use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard_types::registration::PrivateIPs;
use std::sync::Arc; use std::sync::Arc;
pub(crate) mod client_registry; pub(crate) mod client_registry;
@@ -26,6 +28,7 @@ impl WireguardAppState {
client_registry: Arc<GatewayClientRegistry>, client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>, registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16, binding_port: u16,
private_ip_network: IpNetwork,
) -> Self { ) -> Self {
WireguardAppState { WireguardAppState {
inner: Some(WireguardAppStateInner { inner: Some(WireguardAppStateInner {
@@ -33,6 +36,9 @@ impl WireguardAppState {
client_registry, client_registry,
registration_in_progress, registration_in_progress,
binding_port, binding_port,
free_private_network_ips: Arc::new(
private_ip_network.iter().map(|ip| (ip, true)).collect(),
),
}), }),
} }
} }
@@ -77,6 +83,7 @@ pub(crate) struct WireguardAppStateInner {
client_registry: Arc<GatewayClientRegistry>, client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>, registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16, binding_port: u16,
free_private_network_ips: Arc<PrivateIPs>,
} }
pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> { pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
@@ -98,6 +105,7 @@ mod test {
use axum::http::StatusCode; use axum::http::StatusCode;
use dashmap::DashMap; use dashmap::DashMap;
use hmac::Mac; use hmac::Mac;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption; use nym_crypto::asymmetric::encryption;
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{ use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage,
@@ -105,6 +113,8 @@ mod test {
}; };
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard; use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard_types::registration::HmacSha256; use nym_wireguard_types::registration::HmacSha256;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use tower::Service; use tower::Service;
use tower::ServiceExt; use tower::ServiceExt;
@@ -136,6 +146,14 @@ mod test {
let registration_in_progress = Arc::new(DashMap::new()); let registration_in_progress = Arc::new(DashMap::new());
let client_registry = Arc::new(DashMap::new()); let client_registry = Arc::new(DashMap::new());
let free_private_network_ips = Arc::new(
IpNetwork::from_str("10.0.0.0/24")
.unwrap()
.iter()
.map(|ip| (ip, true))
.collect(),
);
let client_private_ip = IpAddr::from_str("10.0.0.42").unwrap();
let state = WireguardAppState { let state = WireguardAppState {
inner: Some(WireguardAppStateInner { inner: Some(WireguardAppStateInner {
@@ -143,6 +161,7 @@ mod test {
dh_keypair: Arc::new(gateway_key_pair), dh_keypair: Arc::new(gateway_key_pair),
registration_in_progress: Arc::clone(&registration_in_progress), registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080, binding_port: 8080,
free_private_network_ips,
}), }),
}; };
@@ -186,11 +205,13 @@ mod test {
let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap(); let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap();
mac.update(client_static_public.as_bytes()); mac.update(client_static_public.as_bytes());
mac.update(client_private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes()); mac.update(&nonce.to_le_bytes());
let mac = mac.finalize().into_bytes(); let mac = mac.finalize().into_bytes();
let finalized_message = ClientMessage::Final(GatewayClient { let finalized_message = ClientMessage::Final(GatewayClient {
pub_key: PeerPublicKey::new(client_static_public), pub_key: PeerPublicKey::new(client_static_public),
private_ip: client_private_ip,
mac: ClientMac::new(mac.as_slice().to_vec()), mac: ClientMac::new(mac.as_slice().to_vec()),
}); });