Compare commits
4 Commits
develop
...
wg_testing
| Author | SHA1 | Date | |
|---|---|---|---|
| f008c33eb5 | |||
| deb214d176 | |||
| a376fec990 | |||
| d1303947c0 |
@@ -43,7 +43,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
args: --workspace --release --features wireguard
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
args: --workspace --release --features wireguard
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
|
||||
Generated
+11
@@ -4282,6 +4282,15 @@ version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.18.0"
|
||||
@@ -6532,6 +6541,7 @@ dependencies = [
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"hyper",
|
||||
"ipnetwork 0.16.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
@@ -6898,6 +6908,7 @@ dependencies = [
|
||||
"fastrand 2.0.1",
|
||||
"hmac 0.12.1",
|
||||
"hyper",
|
||||
"ipnetwork 0.16.0",
|
||||
"mime",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
|
||||
@@ -74,8 +74,8 @@ pub async fn current_gateways<R: Rng>(
|
||||
.collect::<Vec<gateway::Node>>();
|
||||
|
||||
// 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"));
|
||||
Ok(filtered_gateways)
|
||||
// let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
Ok(valid_gateways)
|
||||
}
|
||||
|
||||
pub async fn current_mixnodes<R: Rng>(
|
||||
|
||||
@@ -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")]
|
||||
@@ -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")]
|
||||
@@ -72,6 +75,9 @@ 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,
|
||||
@@ -79,7 +85,12 @@ pub struct GatewayClient {
|
||||
|
||||
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())
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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())
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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};
|
||||
@@ -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(
|
||||
@@ -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| {
|
||||
WireguardAppState::new(
|
||||
self.sphinx_keypair,
|
||||
client_registry,
|
||||
Default::default(),
|
||||
self.gateway_config.wireguard.bind_address.port(),
|
||||
wireguard_private_network,
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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 {},
|
||||
}
|
||||
}
|
||||
|
||||
+18
-2
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -26,6 +28,7 @@ impl WireguardAppState {
|
||||
client_registry: Arc<GatewayClientRegistry>,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
private_ip_network: IpNetwork,
|
||||
) -> Self {
|
||||
WireguardAppState {
|
||||
inner: Some(WireguardAppStateInner {
|
||||
@@ -33,6 +36,9 @@ impl WireguardAppState {
|
||||
client_registry,
|
||||
registration_in_progress,
|
||||
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>,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
free_private_network_ips: Arc<PrivateIPs>,
|
||||
}
|
||||
|
||||
pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
|
||||
@@ -98,6 +105,7 @@ 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,
|
||||
@@ -105,6 +113,8 @@ mod test {
|
||||
};
|
||||
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;
|
||||
@@ -136,6 +146,14 @@ 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.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 {
|
||||
inner: Some(WireguardAppStateInner {
|
||||
@@ -143,6 +161,7 @@ mod test {
|
||||
dh_keypair: Arc::new(gateway_key_pair),
|
||||
registration_in_progress: Arc::clone(®istration_in_progress),
|
||||
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();
|
||||
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()),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user