Skip to content

Commit

Permalink
Add private ip assignment (#4089)
Browse files Browse the repository at this point in the history
* Add private ip assignment

* Update wg IPs
  • Loading branch information
neacsu authored Nov 9, 2023
1 parent 46d1ef7 commit d38139b
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 3 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion common/wireguard-types/src/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::PeerPublicKey;
use base64::{engine::general_purpose, Engine};
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::{fmt, ops::Deref, str::FromStr};

#[cfg(feature = "verify")]
Expand All @@ -17,11 +18,13 @@ use sha2::Sha256;

pub type GatewayClientRegistry = DashMap<PeerPublicKey, GatewayClient>;
pub type PendingRegistrations = DashMap<PeerPublicKey, Nonce>;
pub type PrivateIPs = DashMap<IpAddr, Free>;

#[cfg(feature = "verify")]
pub type HmacSha256 = Hmac<Sha256>;

pub type Nonce = u64;
pub type Free = bool;

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "camelCase")]
Expand Down Expand Up @@ -72,14 +75,22 @@ pub struct GatewayClient {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub pub_key: PeerPublicKey,

/// Assigned private IP
pub private_ip: IpAddr,

/// Sha256 hmac on the data (alongside the prior nonce)
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub mac: ClientMac,
}

impl GatewayClient {
#[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
#[allow(clippy::expect_used)]
let static_secret = boringtun::x25519::StaticSecret::try_from(local_secret.to_bytes())
Expand All @@ -96,10 +107,12 @@ impl GatewayClient {
.expect("x25519 shared secret is always 32 bytes long");

mac.update(local_public.as_bytes());
mac.update(private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());

GatewayClient {
pub_key: PeerPublicKey::new(local_public),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
}
Expand All @@ -121,6 +134,7 @@ impl GatewayClient {
.expect("x25519 shared secret is always 32 bytes long");

mac.update(self.pub_key.as_bytes());
mac.update(self.private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());

mac.verify_slice(&self.mac)
Expand Down Expand Up @@ -209,6 +223,7 @@ mod tests {
let client = GatewayClient::new(
client_key_pair.private_key(),
*gateway_key_pair.public_key(),
"10.0.0.42".parse().unwrap(),
nonce,
);
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
Expand Down
1 change: 1 addition & 0 deletions gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dirs = "4.0"
dotenvy = { workspace = true }
futures = { workspace = true }
humantime-serde = "1.0.1"
ipnetwork = "0.16"
lazy_static = "1.4.0"
log = { workspace = true }
once_cell = "1.7.2"
Expand Down
1 change: 1 addition & 0 deletions gateway/src/config/old_config_v1_1_31.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ impl From<ConfigV1_1_31> for Config {
enabled: value.wireguard.enabled,
bind_address: value.wireguard.bind_address,
announced_port: value.wireguard.announced_port,
private_network_prefix: Default::default(),
storage_paths: nym_node::config::persistence::WireguardPaths {
// no fields (yet)
},
Expand Down
6 changes: 6 additions & 0 deletions gateway/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
#[error(transparent)]
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 {
Expand Down
7 changes: 7 additions & 0 deletions gateway/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use crate::config::Config;
use crate::error::GatewayError;
use crate::node::helpers::load_public_key;
use ipnetwork::IpNetwork;
use log::warn;
use nym_bin_common::bin_info_owned;
use nym_crypto::asymmetric::{encryption, identity};
Expand All @@ -16,6 +17,7 @@ use nym_node::http::router::WireguardAppState;
use nym_node::wireguard::types::GatewayClientRegistry;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;

fn load_gateway_details(
Expand Down Expand Up @@ -223,12 +225,17 @@ impl<'a> HttpApiBuilder<'a> {
}
}

let wireguard_private_network = IpNetwork::new(
IpAddr::from(Ipv4Addr::new(10, 1, 0, 0)),
self.gateway_config.wireguard.private_network_prefix,
)?;
let wg_state = self.client_registry.map(|client_registry| {
WireguardAppState::new(
self.sphinx_keypair,
client_registry,
Default::default(),
self.gateway_config.wireguard.bind_address.port(),
wireguard_private_network,
)
});

Expand Down
2 changes: 2 additions & 0 deletions nym-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ license.workspace = true
anyhow = { workspace = true }
bytes = "1.5.0"
colored = "2"
ipnetwork = "0.16"
rand = "0.7.3"
serde = { workspace = true, features = ["derive"] }
serde_yaml = "0.9.25"
serde_json = { workspace = true }
Expand Down
6 changes: 6 additions & 0 deletions nym-node/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod persistence;
mod serde_helpers;

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;

// TODO: this is very much a WIP. we need proper ssl certificate support here
Expand Down Expand Up @@ -75,6 +76,10 @@ pub struct Wireguard {
/// Useful in the instances where the node is behind a proxy.
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.
pub storage_paths: persistence::WireguardPaths,
}
Expand All @@ -88,6 +93,7 @@ impl Default for Wireguard {
DEFAULT_WIREGUARD_PORT,
),
announced_port: DEFAULT_WIREGUARD_PORT,
private_network_prefix: DEFAULT_WIREGUARD_PREFIX,
storage_paths: persistence::WireguardPaths {},
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use nym_crypto::asymmetric::encryption::PublicKey;
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey,
};
use rand::{prelude::IteratorRandom, thread_rng};

async fn process_final_message(
client: GatewayClient,
Expand Down Expand Up @@ -91,8 +92,23 @@ pub(crate) async fn register_client(
let remote_public = PublicKey::from_bytes(init.pub_key().as_bytes())
.map_err(|_| RequestError::new_status(StatusCode::BAD_REQUEST))?;
let nonce = process_init_message(init, state).await;
let gateway_data =
GatewayClient::new(state.dh_keypair.private_key(), remote_public, nonce);
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.dh_keypair.private_key(),
remote_public,
*private_ip_ref.key(),
nonce,
);
let response = ClientRegistrationResponse::PendingRegistration {
nonce,
gateway_data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ use crate::http::api::v1::gateway::client_interfaces::wireguard::client_registry
use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
use axum::routing::{get, post};
use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption;
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard_types::registration::PrivateIPs;
use std::sync::Arc;

pub(crate) mod client_registry;
Expand All @@ -26,13 +28,17 @@ impl WireguardAppState {
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
private_ip_network: IpNetwork,
) -> Self {
WireguardAppState {
inner: Some(WireguardAppStateInner {
dh_keypair,
client_registry,
registration_in_progress,
binding_port,
free_private_network_ips: Arc::new(
private_ip_network.iter().map(|ip| (ip, true)).collect(),
),
}),
}
}
Expand Down Expand Up @@ -77,6 +83,7 @@ pub(crate) struct WireguardAppStateInner {
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
free_private_network_ips: Arc<PrivateIPs>,
}

pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
Expand All @@ -98,13 +105,16 @@ mod test {
use axum::http::StatusCode;
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_types::registration::HmacSha256;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
use tower::Service;
use tower::ServiceExt;
Expand Down Expand Up @@ -136,13 +146,22 @@ mod test {

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()
.iter()
.map(|ip| (ip, true))
.collect(),
);
let client_private_ip = IpAddr::from_str("10.1.0.42").unwrap();

let state = WireguardAppState {
inner: Some(WireguardAppStateInner {
client_registry: Arc::clone(&client_registry),
dh_keypair: Arc::new(gateway_key_pair),
registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080,
free_private_network_ips,
}),
};

Expand Down Expand Up @@ -186,11 +205,13 @@ mod test {

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()),
});

Expand Down

0 comments on commit d38139b

Please sign in to comment.