Compare commits

...

16 Commits

Author SHA1 Message Date
Mark Sinclair d0462eb4ad Add URL to operator T&Cs to default memos used during bonding for wallet and CLI 2024-05-29 17:15:17 +01:00
Mark Sinclair fef2e1afd0 nym-wallet: add checkbox to accept T&Cs for operators 2024-05-29 17:14:49 +01:00
Bogdan-Ștefan Neacşu 3acf521fc1 Refine wireguard on gateway side (#4615)
* Include wireguard in gw config

* Support nym node first

* Create wg keypair

* Activate wg feature in gw dep

* Move key paths to separate structure

* Use client reg

* Generate and use own private key

* Rename network to ip for wg gw

* Propagate wireguard setup error message

* Remove logs

* Bump gateway version number

* Remove upgrade code

* Init wireguard on migration

* Upgrade code for nym-node too

* Wireguard paths upgrade

* Init wg keys on upgrade

* Simplify pub key translatations

* Fix clippy

* Undo comment change

* Fix tests

* Don't bump version just yet

* Remove redundant source attr

* Remove unused wg details

* Rename wg device

* Init for mixnode migration as well

* Add upgrade for gw wireguard deleted field

* Move interface removal to Drop trait impl

* Fix clippy

* Wgapi could be included on other platforms
2024-05-28 11:52:14 +02:00
benedetta davico 759e2fa2c5 Merge pull request #4617 from nymtech/master
Merge Master into Develop with the recent Ragusa changes
2024-05-24 14:06:57 +02:00
Tommy Verrall 489914fb42 Merge pull request #4608 from nymtech/release/2024.5-ragusa
Release 2024.5-ragusa into master - DO NO MERGE
2024-05-24 12:35:01 +01:00
import this bca8992115 [DOCs/operators]: 2024.5-ragusa updates (#4609)
* initialise changelog updates

* fix formatting

* finalise changelog

* change formating - PR ready for review

* review comments resolved
2024-05-23 16:49:17 +00:00
mx f94d900d18 update mixfetch docs (#4614)
* * added info on gateway habourmaster
* fixed broken examples with new infra addresses
* bump node version in ts sdk docs ci script

---------

Co-authored-by: mfahampshire <mfahampshire@pm.me>
2024-05-23 15:15:51 +00:00
Tommy Verrall dab55a12c7 update changelog 2024-05-23 16:26:09 +02:00
Tommy Verrall 82f722936f Merge branch 'release/2024.5-ragusa' of https://github.com/nymtech/nym into release/2024.5-ragusa 2024-05-23 16:09:07 +02:00
benedettadavico 7f08020d4f update changelog and bump versions 2024-05-23 16:08:37 +02:00
Tommy Verrall 579e41d57e Merge pull request #4613 from nymtech/bugfix/last_polled-backwards-compat
make sure 'OffsetDateTimeJsonSchemaWrapper' is serialised with legacy format
2024-05-23 15:00:38 +01:00
Jędrzej Stuczyński 06953298eb ibid for nym-connect 2024-05-23 12:10:41 +01:00
Jędrzej Stuczyński 1d78f8747f update wallet 'time' dep due to broken semver 2024-05-23 10:21:08 +01:00
Tommy Verrall a6e9414cb8 Update ci-contracts-upload-binaries.yml
use version 1.77 so it's compatible with ecash
2024-05-23 11:03:40 +02:00
Jędrzej Stuczyński 23d7230d33 make sure 'OffsetDateTimeJsonSchemaWrapper' is serialised with legacy format 2024-05-23 10:01:46 +01:00
benedettadavico 496f172070 update changelog and bump versions 2024-05-22 10:28:38 +02:00
60 changed files with 2169 additions and 522 deletions
@@ -35,7 +35,7 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.77
target: wasm32-unknown-unknown
override: true
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
node-version: 18.17
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
+39
View File
@@ -4,6 +4,45 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.5-ragusa] (2024-05-22)
- Feature/nym node api location ([#4605])
- Add optional signature to IPR request/response ([#4604])
- Feature/unstable tested nodes endpoint ([#4601])
- nym-api: make report/avg_uptime endpoints ignore blacklist ([#4599])
- removed blocking for coconut in the final epoch state ([#4598])
- allow using explicit admin address for issuing freepasses ([#4595])
- Use rfc3339 for last_polled in described nym-api endpoint ([#4591])
- Explicitly handle constraint unique violation when importing credential ([#4588])
- [bugfix] noop flag for nym-api for nymvisor compatibility ([#4586])
- Chore/additional helpers ([#4585])
- Feature/wasm coconut ([#4584])
- upgraded axum and related deps to the most recent version ([#4573])
- Feature/nyxd scraper pruning ([#4564])
- Run cargo autoinherit on the main workspace ([#4553])
- Add rustls-tls to reqwest in validator-client ([#4552])
- Feature/rewarder voucher issuance ([#4548])
- make sure 'OffsetDateTimeJsonSchemaWrapper' is serialised with legacy format ([#4613])
[#4613]: https://github.com/nymtech/nym/pull/4613
[#4605]: https://github.com/nymtech/nym/pull/4605
[#4604]: https://github.com/nymtech/nym/pull/4604
[#4601]: https://github.com/nymtech/nym/pull/4601
[#4599]: https://github.com/nymtech/nym/pull/4599
[#4598]: https://github.com/nymtech/nym/pull/4598
[#4595]: https://github.com/nymtech/nym/pull/4595
[#4591]: https://github.com/nymtech/nym/pull/4591
[#4588]: https://github.com/nymtech/nym/pull/4588
[#4586]: https://github.com/nymtech/nym/pull/4586
[#4585]: https://github.com/nymtech/nym/pull/4585
[#4584]: https://github.com/nymtech/nym/pull/4584
[#4573]: https://github.com/nymtech/nym/pull/4573
[#4564]: https://github.com/nymtech/nym/pull/4564
[#4553]: https://github.com/nymtech/nym/pull/4553
[#4552]: https://github.com/nymtech/nym/pull/4552
[#4548]: https://github.com/nymtech/nym/pull/4548
## [2024.4-nutella] (2024-05-08)
- [fix] apply disable_poisson_rate from internal NR/IPR cfgs ([#4579])
Generated
+11 -7
View File
@@ -3929,7 +3929,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.37"
version = "1.1.38"
dependencies = [
"anyhow",
"async-trait",
@@ -4017,6 +4017,7 @@ dependencies = [
"nym-node-requests",
"schemars",
"serde",
"serde_json",
"tendermint",
"time",
"ts-rs",
@@ -4091,7 +4092,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.35"
version = "1.1.36"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -4172,7 +4173,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.34"
version = "1.1.35"
dependencies = [
"bs58 0.5.1",
"clap 4.5.4",
@@ -5005,7 +5006,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.35"
version = "1.1.36"
dependencies = [
"addr",
"anyhow",
@@ -5074,7 +5075,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.1.1"
version = "1.1.2"
dependencies = [
"anyhow",
"bip39",
@@ -5120,6 +5121,7 @@ version = "0.1.0"
dependencies = [
"axum 0.7.5",
"axum-extra",
"base64 0.21.7",
"colored",
"dashmap",
"fastrand 2.1.0",
@@ -5347,7 +5349,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.34"
version = "1.1.35"
dependencies = [
"bs58 0.5.1",
"clap 4.5.4",
@@ -5859,7 +5861,9 @@ dependencies = [
"dashmap",
"hmac 0.12.1",
"log",
"nym-config",
"nym-crypto",
"nym-network-defaults",
"rand 0.7.3",
"serde",
"serde_json",
@@ -5871,7 +5875,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"anyhow",
"bytes",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.34"
version = "1.1.35"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.34"
version = "1.1.35"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -324,10 +324,10 @@ impl ExecuteMsg {
ExecuteMsg::AdvanceCurrentEpoch { .. } => "advancing current epoch".into(),
ExecuteMsg::ReconcileEpochEvents { .. } => "reconciling epoch events".into(),
ExecuteMsg::BondMixnode { mix_node, .. } => {
format!("bonding mixnode {}", mix_node.identity_key)
format!("bonding mixnode {}, accepted https://nymtech.net/terms-and-conditions/operators/v1.0.0", mix_node.identity_key)
}
ExecuteMsg::BondMixnodeOnBehalf { mix_node, .. } => {
format!("bonding mixnode {} on behalf", mix_node.identity_key)
format!("bonding mixnode {} on behalf, accepted https://nymtech.net/terms-and-conditions/operators/v1.0.0", mix_node.identity_key)
}
ExecuteMsg::PledgeMore {} => "pledging additional tokens".into(),
ExecuteMsg::PledgeMoreOnBehalf { .. } => "pledging additional tokens on behalf".into(),
@@ -346,10 +346,10 @@ impl ExecuteMsg {
"updating mixnode configuration on behalf".into()
}
ExecuteMsg::BondGateway { gateway, .. } => {
format!("bonding gateway {}", gateway.identity_key)
format!("bonding gateway {}, accepted https://nymtech.net/terms-and-conditions/operators/v1.0.0", gateway.identity_key)
}
ExecuteMsg::BondGatewayOnBehalf { gateway, .. } => {
format!("bonding gateway {} on behalf", gateway.identity_key)
format!("bonding gateway {} on behalf, accepted https://nymtech.net/terms-and-conditions/operators/v1.0.0", gateway.identity_key)
}
ExecuteMsg::UnbondGateway { .. } => "unbonding gateway".into(),
ExecuteMsg::UnbondGatewayOnBehalf { .. } => "unbonding gateway on behalf".into(),
+2
View File
@@ -17,7 +17,9 @@ log = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
nym-config = { path = "../config" }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
# feature-specific dependencies:
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::{IpAddr, SocketAddr};
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct Config {
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Private IP address of the wireguard gateway.
/// default: `10.1.0.1`
pub private_ip: IpAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// 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,
}
+37 -1
View File
@@ -1,15 +1,51 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use dashmap::DashMap;
use nym_crypto::asymmetric::encryption::KeyPair;
use std::sync::Arc;
pub mod config;
pub mod error;
pub mod public_key;
pub mod registration;
pub use config::Config;
pub use error::Error;
pub use public_key::PeerPublicKey;
pub use registration::{
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce,
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, GatewayClientRegistry,
InitMessage, Nonce,
};
#[cfg(feature = "verify")]
pub use registration::HmacSha256;
#[derive(Clone)]
pub struct WireguardGatewayData {
config: Config,
keypair: Arc<KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
}
impl WireguardGatewayData {
pub fn new(config: Config, keypair: Arc<KeyPair>) -> Self {
WireguardGatewayData {
config,
keypair,
client_registry: Arc::new(DashMap::default()),
}
}
pub fn config(&self) -> Config {
self.config
}
pub fn keypair(&self) -> &Arc<KeyPair> {
&self.keypair
}
pub fn client_registry(&self) -> &Arc<GatewayClientRegistry> {
&self.client_registry
}
}
+4
View File
@@ -24,6 +24,10 @@ impl PeerPublicKey {
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
pub fn inner(&self) -> PublicKey {
self.0
}
}
impl fmt::Display for PeerPublicKey {
+3 -5
View File
@@ -12,7 +12,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
use nym_crypto::asymmetric::encryption::PrivateKey;
#[cfg(feature = "verify")]
use sha2::Sha256;
@@ -87,7 +87,7 @@ impl GatewayClient {
#[cfg(feature = "verify")]
pub fn new(
local_secret: &PrivateKey,
remote_public: PublicKey,
remote_public: x25519_dalek::PublicKey,
private_ip: IpAddr,
nonce: u64,
) -> Self {
@@ -96,8 +96,6 @@ impl GatewayClient {
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let remote_public = x25519_dalek::PublicKey::from(remote_public.to_bytes());
let dh = static_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
@@ -220,7 +218,7 @@ mod tests {
let client = GatewayClient::new(
client_key_pair.private_key(),
*gateway_key_pair.public_key(),
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
"10.0.0.42".parse().unwrap(),
nonce,
);
+1 -3
View File
@@ -12,6 +12,7 @@ license.workspace = true
[dependencies]
base64 = "0.21.3"
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed" }
# The latest version on crates.io at the time of writing this (6.0.0) has a
# version mismatch with x25519-dalek/curve25519-dalek that is resolved in the
# latest commit. So pick that for now.
@@ -22,6 +23,3 @@ nym-network-defaults = { path = "../network-defaults" }
nym-task = { path = "../task" }
nym-wireguard-types = { path = "../wireguard-types" }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
[target."cfg(target_os = \"linux\")".dependencies]
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed" }
+40 -20
View File
@@ -3,44 +3,64 @@
// #![warn(clippy::expect_used)]
// #![warn(clippy::unwrap_used)]
pub mod setup;
use defguard_wireguard_rs::WGApi;
const WG_TUN_NAME: &str = "nymwg";
pub struct WgApiWrapper {
wg_api: WGApi,
}
impl WgApiWrapper {
pub fn new(wg_api: WGApi) -> Self {
WgApiWrapper { wg_api }
}
}
impl Drop for WgApiWrapper {
fn drop(&mut self) {
if let Err(e) = defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&self.wg_api)
{
log::error!("Could not remove the wireguard interface: {:?}", e);
}
}
}
/// Start wireguard device
#[cfg(target_os = "linux")]
pub async fn start_wireguard(
mut task_client: nym_task::TaskClient,
_gateway_client_registry: std::sync::Arc<
nym_wireguard_types::registration::GatewayClientRegistry,
>,
) -> Result<defguard_wireguard_rs::WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
use crate::setup::{peer_allowed_ips, peer_static_public_key, PRIVATE_KEY};
wireguard_data: std::sync::Arc<nym_wireguard_types::WireguardGatewayData>,
) -> Result<WgApiWrapper, Box<dyn std::error::Error + Send + Sync + 'static>> {
use base64::{prelude::BASE64_STANDARD, Engine};
use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
};
use nym_network_defaults::{WG_PORT, WG_TUN_DEVICE_ADDRESS};
let ifname = String::from("wg0");
let mut peers = vec![];
for peer_client in wireguard_data.client_registry().iter() {
let mut peer = Peer::new(Key::new(peer_client.pub_key.to_bytes()));
let peer_ip_mask = IpAddrMask::new(peer_client.private_ip, 32);
peer.set_allowed_ips(vec![peer_ip_mask]);
peers.push(peer);
}
let ifname = String::from(WG_TUN_NAME);
let wgapi = WGApi::new(ifname.clone(), false)?;
wgapi.create_interface()?;
let interface_config = InterfaceConfiguration {
name: ifname.clone(),
prvkey: PRIVATE_KEY.to_string(),
address: WG_TUN_DEVICE_ADDRESS.to_string(),
port: WG_PORT as u32,
peers: vec![],
prvkey: BASE64_STANDARD.encode(wireguard_data.keypair().private_key().to_bytes()),
address: wireguard_data.config().private_ip.to_string(),
port: wireguard_data.config().announced_port as u32,
peers,
};
wgapi.configure_interface(&interface_config)?;
let peer = peer_static_public_key();
let mut peer = Peer::new(Key::new(peer.to_bytes()));
let peer_ip = peer_allowed_ips();
let peer_ip_mask = IpAddrMask::new(peer_ip.network_address(), peer_ip.netmask());
peer.set_allowed_ips(vec![peer_ip_mask]);
wgapi.configure_peer(&peer)?;
wgapi.configure_peer_routing(&[peer.clone()])?;
// wgapi.configure_peer_routing(&peers)?;
tokio::spawn(async move { task_client.recv().await });
Ok(wgapi)
Ok(WgApiWrapper::new(wgapi))
}
#[cfg(not(target_os = "linux"))]
-56
View File
@@ -1,56 +0,0 @@
use std::net::IpAddr;
use base64::{engine::general_purpose, Engine as _};
use log::info;
// The wireguard UDP listener
pub const WG_ADDRESS: &str = "0.0.0.0";
// The private key of the listener
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
pub(crate) const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
// The AllowedIPs for the connected peer, which is one a single IP and the same as the IP that the
// peer has configured on their side.
const ALLOWED_IPS: &str = "10.1.0.2";
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
general_purpose::STANDARD
.decode(base64_key)
.unwrap()
.try_into()
.unwrap()
}
pub fn server_static_private_key() -> x25519_dalek::StaticSecret {
// TODO: this is a temporary solution for development
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
let static_private = x25519_dalek::StaticSecret::from(static_private_bytes);
let static_public = x25519_dalek::PublicKey::from(&static_private);
info!(
"wg public key: {}",
general_purpose::STANDARD.encode(static_public)
);
static_private
}
pub fn peer_static_public_key() -> x25519_dalek::PublicKey {
// A single static public key is used during development
// Read from NYM_PEER_PUBLIC_KEY env variable
let peer = std::env::var("NYM_PEER_PUBLIC_KEY").expect("NYM_PEER_PUBLIC_KEY must be set");
let peer_static_public_bytes: [u8; 32] = decode_base64_key(&peer);
let peer_static_public = x25519_dalek::PublicKey::from(peer_static_public_bytes);
info!(
"Adding wg peer public key: {}",
general_purpose::STANDARD.encode(peer_static_public)
);
peer_static_public
}
pub fn peer_allowed_ips() -> ip_network::IpNetwork {
let key: IpAddr = ALLOWED_IPS.parse().unwrap();
let cidr = 32u8;
ip_network::IpNetwork::new_truncate(key, cidr).unwrap()
}
+157
View File
@@ -2,6 +2,160 @@
This page displays a full list of all the changes during our release cycle from [`v2024.3-eclipse`](https://github.com/nymtech/nym/blob/nym-binaries-v2024.3-eclipse/CHANGELOG.md) onwards. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first.
## `v2024.5-ragusa`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.5-ragusa)
- [Release CHANGELOG.md](https://github.com/nymtech/nym/blob/nym-binaries-v2024.5-ragusa/CHANGELOG.md)
- [`nym-node`](nodes/nym-node.md) version `1.1.2`
~~~admonish example collapsible=true title="CHANGELOG.md"
- Feature/nym node api location ([#4605])
- Add optional signature to IPR request/response ([#4604])
- Feature/unstable tested nodes endpoint ([#4601])
- nym-api: make report/avg_uptime endpoints ignore blacklist ([#4599])
- removed blocking for coconut in the final epoch state ([#4598])
- allow using explicit admin address for issuing freepasses ([#4595])
- Use rfc3339 for last_polled in described nym-api endpoint ([#4591])
- Explicitly handle constraint unique violation when importing credential ([#4588])
- [bugfix] noop flag for nym-api for nymvisor compatibility ([#4586])
- Chore/additional helpers ([#4585])
- Feature/wasm coconut ([#4584])
- upgraded axum and related deps to the most recent version ([#4573])
- Feature/nyxd scraper pruning ([#4564])
- Run cargo autoinherit on the main workspace ([#4553])
- Add rustls-tls to reqwest in validator-client ([#4552])
- Feature/rewarder voucher issuance ([#4548])
[#4605]: https://github.com/nymtech/nym/pull/4605
[#4604]: https://github.com/nymtech/nym/pull/4604
[#4601]: https://github.com/nymtech/nym/pull/4601
[#4599]: https://github.com/nymtech/nym/pull/4599
[#4598]: https://github.com/nymtech/nym/pull/4598
[#4595]: https://github.com/nymtech/nym/pull/4595
[#4591]: https://github.com/nymtech/nym/pull/4591
[#4588]: https://github.com/nymtech/nym/pull/4588
[#4586]: https://github.com/nymtech/nym/pull/4586
[#4585]: https://github.com/nymtech/nym/pull/4585
[#4584]: https://github.com/nymtech/nym/pull/4584
[#4573]: https://github.com/nymtech/nym/pull/4573
[#4564]: https://github.com/nymtech/nym/pull/4564
[#4553]: https://github.com/nymtech/nym/pull/4553
[#4552]: https://github.com/nymtech/nym/pull/4552
[#4548]: https://github.com/nymtech/nym/pull/4548
~~~
### Features
- New `nym-node` API endpoint `/api/v1/auxiliary-details`: to expose any additional information. Currently it's just the location. `nym-api` will then query all nodes for that information and put it in the `self-described` endpoint.
- New `nym-node` location available - use one of the three options to add this to your node config:
1. Update the `location` field under `[host]` section of `config.toml`
2. For new nodes: Initialise the node with `--location` flag, where they have to provide the country info. Either full country name (e.g. 'Jamaica'), two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided.
3. For existing nodes: It's also possible to use exactly the same `--location` argument as above, but make sure to also provide `--write-changes` (or `-w`) flag to persist those changes!
- [Feature/unstable tested nodes endpoint](https://github.com/nymtech/nym/pull/4601): Adds new data structures (`TestNode`, `TestRoute`, `PartialTestResult`) to handle test results for Mixnodes and Gateways. With the inclusion of pagination to handle large API responses efficiently. Lastly, introducing a new route with the tag `unstable` thus meaning not to be consumed without a user risk, prefixes in endpoints with unstable, are what it says on the tin.
~~~admonish example collapsible=true title="Testing steps performed"
- Deploy new api changes to sandbox environment
- Ensure current operations are transactional and standed operations are working
- Run a script to ensure that the new endpoints are working as expected with pagination
<img width="719" alt="image" src="https://github.com/nymtech/nym/assets/60836166/91285971-e82a-4e5a-8a58-880505ae1be9">
~~~
- [`nym-api`: make report/avg_uptime endpoints ignore blacklist](https://github.com/nymtech/nym/pull/4599): When querying for node specific data, it's no longer going to go through the entire list of all cached (and filtered nodes) to find it; instead it will attempt to retrieve a single unfiltered entry.
~~~admonish example collapsible=true title="Testing steps performed"
- Build the project and deployed it in a test environment.
- Manually test API endpoints for mixnode and gateway data.
- Verify that the endpoints return the expected data and handle blacklists correctly.
- API performance improved due to the efficient `HashMap` lookups
- Data in mainnet will differ from test nets due to the increased amount of gateways and mixnodes in that environment
- Test standard uptime routes:
```sh
curl -X 'GET' 'https://validator.nymtech.net/api/v1/status/gateway/Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf/avg_uptime' -H 'accept: application/json'
```
~~~
- [Use rfc3339 for last_polled in described nym-api endpoint](https://github.com/nymtech/nym/pull/4591): Fix issue where the validator-client can't parse the nym-api response for the described endpoint, in particular the `latest_polled` field that was recently added, by making the field use `rfc3339`
- **Note:** This will require upgrading `nym-api` and everything that depends on the described endpoint.
~~~admonish example collapsible=true title="Testing steps performed"
- Update a `nym-api` to the binary built from this branch, then restart the api
- Check the `journalctl` for error messages
- Connected via client and could not see the error messages, this is backwards compatible
- Local testing using sdk examples:
```sh
cd <PATH_TO>/nym/sdk/rust/nym-sdk
cargo run --example simple
# outcome
thread 'main' panicked at sdk/rust/nym-sdk/examples/simple.rs:9:64:
called Result::unwrap() on an Err value: ClientCoreError(ValidatorClientError(NymAPIError { source: ReqwestClientError { source: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None,
```
~~~
- [Upgrade `axum` and related dependencies to the most recent version](https://github.com/nymtech/nym/pull/4573)
- [Run cargo autoinherit on the main workspace](https://github.com/nymtech/nym/pull/4553): Move several dependencies to the workspace level using cargo autoinherit, to make it easier to keep our dependencies up to date.
- Run cargo autoinherit in the root
- Merge in the new workspace deps in the main list
- We made sure to not mix in other changes as well - all features flags for all crates should be the same as before
~~~admonish example collapsible=true title="Testing steps performed"
- Run `cargo autoinherit` in the root directory to move dependencies to the workspace level
- Merge the new workspace dependencies into the main list
- Ensure no other changes were mixed in during the process
- Verify that all feature flags for all crates remained the same as before
- Build all the binaries from this branch to confirm successful compilation
- Deploy the built binaries across different environments to ensure there were no issues
~~~
- [Add rustls-tls to reqwest in validator-client](https://github.com/nymtech/nym/pull/4552): An attempt to make possible to end up in a situation where use use the validator-client but without functioning TLS support. For the monorepo this is masked by cargo feature unification, but becomes a problem for outside consumers, as as been noticed in many of the vpn client implementations.
- In `validator-client`: `reqwest`, enable `rustls-tls` for `non-wasm32`
- In `client-core`: Use default features enabled for `non-wasm32` and switch to `webpki` roots, since that's what we're using with `reqwest` anyway
- In `gateway-client`: Switch to `webpki` roots, since that's what we're using with `reqwest` anyway
#### Crypto
- [Remove blocking for coconut in the final epoch state](https://github.com/nymtech/nym/pull/4598)
~~~admonish example collapsible=true title="Testing steps performed"
- Build the project to ensure no compilation errors
- Run tests to verify the functionality of the `issue_credential` function
- Execute integration tests to check the behaviour during an epoch transition.
~~~
- [Allow using explicit admin address for issuing freepasses](https://github.com/nymtech/nym/pull/4595)
- [Explicitly handle constraint unique violation when importing credential](https://github.com/nymtech/nym/pull/4588): Add a strong type for when a duplicate credential is imported so the vpn lib can handle this.
- [Feature/wasm coconut](https://github.com/nymtech/nym/pull/4584): This pull request requires [\#4585](https://github.com/nymtech/nym/pull/4585) to be merged first
- [Feature/nyxd scraper pruning](https://github.com/nymtech/nym/pull/4564): This PR introduces storage pruning to `nyxd` scraper which is then used by the validators rewarder.
~~~admonish example collapsible=true title="Testing steps performed"
- Add a `main.rs` file in the `nyxd` scraper dir, underneath `lib.rs`, amend `config.pruning_options.validate()?;` to be `let _ = config.pruning_options.validate();` in the mod.rs file
- Test the different variations of `pruning_options`:
- Check the *default* option: `pruning_options: PruningOptions::default()`
- Check the *nothing* option: `pruning_options: PruningOptions::nothing()`
- Check the *custom* option, example: `pruning_options: PruningOptions { keep_recent: (500), interval: (10), strategy: (PruningStrategy::Custom) }`
- Check the pruning *in real life* for the validator rewarder
- Validate that the database table `blocks` was being updated accordingly
~~~
- [Feature/rewarder voucher issuance](https://github.com/nymtech/nym/pull/4548)
- Introduces signature checks on issued credential data
- Stores evidence of any failures/malicious behaviour in the internal db
### Bugfix
- [`noop` flag for `nym-api` for `nymvisor` compatibility](https://github.com/nymtech/nym/pull/4586)
- The application starts correctly and logs the starting message
- The `--no_banner` flag works as intended, providing compatibility with `nymvisor`
~~~admonish example collapsible=true title="Testing steps performed"
- Build the project to ensure no compilation errors
- Run the binary with different command-line arguments to verify the CLI functionality
- Test with and without the `--no_banner` flag to ensure compatibility and expected behavior
- Verify logging setup and configuration file parsing
~~~
### Operators Guide updates
- [`nym-gateway-probe`](testing/gateway-probe.md): A CLI tool to check in-real-time networking status of any Gateway locally.
- [Where to host your `nym-node`?](legal/isp-list.md): A list of Internet Service Providers (ISPs) by Nym Operators community. We invite all operators to add their experiences with different ISPs to strengthen the community knowledge and Nym mixnet performance.
- Make sure you run `nym-node` with `--wireguard_enabled false` and add a location description to your `config.toml`, both documented in [`nym-node` setup manual](nodes/setup.md#mode-exit-gateway).
---
## `v2024.4-nutella`
- [Merged PRs](https://github.com/nymtech/nym/milestone/59?closed=1)
@@ -14,6 +168,9 @@ This page displays a full list of all the changes during our release cycle from
- [Network configuration](nodes/configuration.md#connectivity-test-and-configuration) section updates, in particular for `--mode mixnode` operators
- [VPS IPv6 troubleshooting](troubleshooting/vps-isp.md#ipv6-troubleshooting) updates
---
## `v2024.3-eclipse`
- Release [Changelog.md](https://github.com/nymtech/nym/blob/nym-binaries-v2024.3-eclipse/CHANGELOG.md)
@@ -26,7 +26,7 @@ Once VPS and Nym wallet are configured, binaries ready, the operators of `nym-no
During the testing events series [Fast and Furious](https://nymtech.net/events/fast-and-furious) we found out, that after introducing IP Packet Router and [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) as default features, only a fragment of Exit Gateways routes correctly through IPv4 and IPv6. We built a useful monitor to check out your Gateway (`nym-node --mode exit-gateway`) at [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
Below is a fast - ten commands - deployment for seasoned operators to migrate and setup the node, configure networking and connectivity and verify that it all works as it should by getting two free jokes through the Mixnet.
Below is a fast - *ten command* - deployment for seasoned operators to migrate and setup the node, configure networking and connectivity and verify that it all works as it should by getting two free jokes through the Mixnet.
```admonish caution
If you are not well familiar with `nym-node` setup, automation, and `nymtun0` configuration, follow the [steps above](#steps-for-nym-node-operators) page by page. You can use this flow as a reference later on.
+27 -34
View File
@@ -54,7 +54,11 @@ https://<DOMAIN>/api/v1/swagger/#/
### Help Command
There are a few changes from the individual binaries used in the past. For example by default `run` command does `init` function as well, local node `--id` will be set by default unless specified otherwise etcetera.
There are a few changes from the individual binaries used in the past. For example by default `run` command does `init` function as well, local node `--id` will be set by default unless specified otherwise etcetera.
```admonish info
You can always use `--help` flag to see the commands or arguments associated with a given command.
```
Run `./nym-node --help` to see all available commands:
@@ -64,31 +68,6 @@ Run `./nym-node --help` to see all available commands:
```
~~~
<!--
IN CASE CMD-RUN DOESN'T WORK HAR PASTE THIS:
Usage: nym-node [OPTIONS] <COMMAND>
Commands:
build-info Show build information of this binary
bonding-information Show bonding information of this node depending on its currently selected mode
node-details Show details of this node
migrate Attempt to migrate an existing mixnode or gateway into a nym-node
run Start this nym-node
sign Use identity key of this node to sign provided message
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file that configures the nym-node and overrides any preconfigured values [env: NYMNODE_CONFIG_ENV_FILE_ARG=]
--no-banner
Flag used for disabling the printed banner in tty [env: NYMNODE_NO_BANNER=]
-h, --help
Print help
-V, --version
Print version
-->
To list all available flags for each command, run `./nym-node <COMMAND> --help` for example `./nym-node run --help`:
~~~admonish example collapsible=true title="`./nym-node run --help` output:"
@@ -148,8 +127,8 @@ Options:
Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS>
Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-private-network-ip <WIREGUARD_PRIVATE_NETWORK_IP>
Ip address of the private wireguard network. default: `10.1.0.0` [env: NYMNODE_WG_IP_NETWORK=]
--wireguard-private-gw-ip <WIREGUARD_PRIVATE_IP>
Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=]
--wireguard-announced-port <WIREGUARD_ANNOUNCED_PORT>
Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX>
@@ -192,7 +171,9 @@ Some of the most useful flags and their explanation:
- `--expose-crypto-hardware <true/false>`: Sets your crypto hardware info visibility on the network.
## Commands & Examples
## Usage
### Commands & Examples
**`nym-node` introduces a default human readible ID (local only) `default-nym-node`, which is used if there is not an explicit custom `--id <ID>` specified. All configuration is stored in `~/.nym/nym-nodes/default-nym-node/config/config.toml` or `~/.nym/nym-nodes/<ID>/config/config.toml` respectively.**
@@ -216,22 +197,34 @@ To prevent over-flooding of our documentation we cannot provide with every singl
As part of the transition, `allowed.list` on Exit Gateway embedded Network Requester was depreciated.
**Initialise and run:**
**Initialise and run** in one command:
```sh
# simple default
./nym-node run --mode exit-gateway
# with other options
./nym-node run --id <ID> --mode exit-gateway --public-ips "$(curl -4 https://ifconfig.me)" --hostname "<YOUR_DOMAIN>" --http-bind-address 0.0.0.0:8080 --mixnet-bind-address 0.0.0.0:1789 --wireguard-enabled true
./nym-node run --id <ID> --mode exit-gateway --public-ips "$(curl -4 https://ifconfig.me)" --hostname "<YOUR_DOMAIN>" --http-bind-address 0.0.0.0:8080 --mixnet-bind-address 0.0.0.0:1789 --location <COUNTRY_FULL_NAME> --wireguard-enabled false
# <YOUR_DOMAIN> is in format without 'https://' prefix
# <COUNTRY_FULL_NAME> is format like 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided.
# keep wireguard disabled
```
Initialise only with a custom `--id` and `--init-only` command :
**Initialise only** without running the node with `--init-only` command :
```sh
./nym-node run --id <ID> --init-only --mode exit-gateway --public-ips "$(curl -4 https://ifconfig.me)" --hostname "<YOUR_DOMAIN>" --http-bind-address 0.0.0.0:8080 --mixnet-bind-address 0.0.0.0:1789 true --wireguard-enabled true
# simple default
./nym-node run --init-only --mode exit-gateway
# with a custom `--id` and other options
./nym-node run --id <ID> --init-only --mode exit-gateway --public-ips "$(curl -4 https://ifconfig.me)" --hostname "<YOUR_DOMAIN>" --http-bind-address 0.0.0.0:8080 --mixnet-bind-address 0.0.0.0:1789 true --location <COUNTRY_FULL_NAME> --wireguard-enabled false
# <YOUR_DOMAIN> is in format without 'https://' prefix
# <COUNTRY_FULL_NAME> is format like 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided.
# keep wireguard disabled
```
Run the node with custom `--id` without initialising
Run the node with custom `--id` without initialising, using `--deny-init` command
```sh
./nym-node run --id <ID> --deny-init --mode exit-gateway
```
@@ -34,7 +34,7 @@ cargo build --release -p nym-gateway-probe
```sh
./target/release/nym-gateway-probe --help
```
~~~admonish collapsible=true
~~~admonish example collapsible=true title="`nym-gateway-probe --help`"
```
Usage: nym-gateway-probe [OPTIONS]
+35 -5
View File
@@ -6,6 +6,7 @@ use nym_gateway::config::old_config_v1_1_20::ConfigV1_1_20;
use nym_gateway::config::old_config_v1_1_28::ConfigV1_1_28;
use nym_gateway::config::old_config_v1_1_29::ConfigV1_1_29;
use nym_gateway::config::old_config_v1_1_31::ConfigV1_1_31;
use nym_gateway::config::old_config_v1_1_36::ConfigV1_1_36;
use nym_gateway::config::{default_config_filepath, Config};
use nym_gateway::error::GatewayError;
@@ -24,7 +25,8 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
let updated_step1: ConfigV1_1_28 = old_config.into();
let updated_step2: ConfigV1_1_29 = updated_step1.into();
let updated_step3: ConfigV1_1_31 = updated_step2.into();
let updated: Config = updated_step3.into();
let updated_step4: ConfigV1_1_36 = updated_step3.into();
let updated: Config = updated_step4.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
@@ -48,7 +50,8 @@ fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
let updated_step1: ConfigV1_1_29 = old_config.into();
let updated_step2: ConfigV1_1_31 = updated_step1.into();
let updated: Config = updated_step2.into();
let updated_step3: ConfigV1_1_36 = updated_step2.into();
let updated: Config = updated_step3.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
@@ -71,7 +74,8 @@ fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_31 = old_config.into();
let updated: Config = updated_step1.into();
let updated_step2: ConfigV1_1_36 = updated_step1.into();
let updated: Config = updated_step2.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
@@ -84,13 +88,36 @@ fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
}
fn try_upgrade_v1_1_31_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.30 (which is incompatible with the current, i.e. 1.1.31+)
// explicitly load it as v1.1.31 (which is incompatible with the current, i.e. 1.1.32+)
let Ok(old_config) = ConfigV1_1_31::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.30 config template.");
info!("It seems the gateway is using <= v1.1.31 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_36 = old_config.into();
let updated: Config = updated_step1.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_36_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.36 (which is incompatible with the current, i.e. 1.1.37+)
let Ok(old_config) = ConfigV1_1_36::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.36 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
@@ -118,6 +145,9 @@ pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
if try_upgrade_v1_1_31_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_36_config(id)? {
return Ok(());
}
Ok(())
}
+3 -45
View File
@@ -11,7 +11,7 @@ use nym_config::{
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
};
use nym_network_defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT, WG_PORT};
use nym_network_defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT};
use serde::{Deserialize, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
@@ -20,12 +20,13 @@ use std::time::Duration;
use url::Url;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub use crate::config::persistence::paths::{GatewayPaths, WireguardPaths};
pub use crate::config::persistence::paths::GatewayPaths;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_28;
pub mod old_config_v1_1_29;
pub mod old_config_v1_1_31;
pub mod old_config_v1_1_36;
pub mod persistence;
mod template;
@@ -85,10 +86,6 @@ pub struct Config {
pub gateway: Gateway,
// currently not really used for anything useful
#[serde(default)]
pub wireguard: Wireguard,
pub storage_paths: GatewayPaths,
pub network_requester: NetworkRequester,
@@ -121,7 +118,6 @@ impl Config {
},
http: Default::default(),
gateway: default_gateway,
wireguard: Default::default(),
storage_paths: GatewayPaths::new_default(id.as_ref()),
network_requester: Default::default(),
ip_packet_router: Default::default(),
@@ -135,7 +131,6 @@ impl Config {
host: impl Into<Host>,
http: impl Into<Http>,
gateway: impl Into<Gateway>,
wireguard: impl Into<Wireguard>,
storage_paths: impl Into<GatewayPaths>,
network_requester: impl Into<NetworkRequester>,
ip_packet_router: impl Into<IpPacketRouter>,
@@ -147,7 +142,6 @@ impl Config {
host: host.into(),
http: http.into(),
gateway: gateway.into(),
wireguard: wireguard.into(),
storage_paths: storage_paths.into(),
network_requester: network_requester.into(),
ip_packet_router: ip_packet_router.into(),
@@ -262,8 +256,6 @@ impl Config {
let http_port = self.http.bind_address.port();
self.http.bind_address = SocketAddr::new(listening_address, http_port);
let wg_port = self.wireguard.bind_address.port();
self.wireguard.bind_address = SocketAddr::new(listening_address, wg_port);
self
}
@@ -347,40 +339,6 @@ impl Default for Http {
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Wireguard {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// 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: WireguardPaths,
}
impl Default for Wireguard {
fn default() -> Self {
Wireguard {
enabled: false,
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), WG_PORT),
announced_port: WG_PORT,
private_network_prefix: 16,
storage_paths: WireguardPaths {},
}
}
}
// we only really care about the mnemonic being zeroized
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Zeroize, ZeroizeOnDrop)]
pub struct Gateway {
+14 -14
View File
@@ -1,8 +1,6 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::paths::{GatewayPaths, WireguardPaths};
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
@@ -14,8 +12,11 @@ use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use super::persistence::paths::KeysPaths;
use super::{Config, Debug, Gateway, Host, Http, NetworkRequester, Wireguard};
use super::old_config_v1_1_36::{
ConfigV1_1_36, DebugV1_1_36, GatewayPathsV1_1_36, GatewayV1_1_36, KeysPathsV1_1_36,
LoggingSettingsV1_1_36, NetworkRequesterV1_1_36, WireguardPathsV1_1_36, WireguardV1_1_36,
};
use super::{Host, Http};
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
@@ -105,13 +106,13 @@ impl ConfigV1_1_31 {
}
}
impl From<ConfigV1_1_31> for Config {
impl From<ConfigV1_1_31> for ConfigV1_1_36 {
fn from(value: ConfigV1_1_31) -> Self {
Self {
save_path: value.save_path,
host: value.host,
http: value.http,
gateway: Gateway {
gateway: GatewayV1_1_36 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
@@ -125,17 +126,17 @@ impl From<ConfigV1_1_31> for Config {
nyxd_urls: value.gateway.nyxd_urls,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
wireguard: Wireguard {
wireguard: WireguardV1_1_36 {
enabled: value.wireguard.enabled,
bind_address: value.wireguard.bind_address,
announced_port: value.wireguard.announced_port,
private_network_prefix: Default::default(),
storage_paths: WireguardPaths {
storage_paths: WireguardPathsV1_1_36 {
// no fields (yet)
},
},
storage_paths: GatewayPaths {
keys: KeysPaths {
storage_paths: GatewayPathsV1_1_36 {
keys: KeysPathsV1_1_36 {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
@@ -147,16 +148,16 @@ impl From<ConfigV1_1_31> for Config {
ip_packet_router_config: Default::default(),
// /\ ADDED
},
network_requester: NetworkRequester {
network_requester: NetworkRequesterV1_1_36 {
enabled: value.network_requester.enabled,
},
// \/ ADDED
ip_packet_router: Default::default(),
// /\ ADDED
logging: LoggingSettings {
logging: LoggingSettingsV1_1_36 {
// no fields (yet)
},
debug: Debug {
debug: DebugV1_1_36 {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,
@@ -165,7 +166,6 @@ impl From<ConfigV1_1_31> for Config {
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
..Default::default()
},
}
}
+378
View File
@@ -0,0 +1,378 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::paths::GatewayPaths;
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use nym_network_defaults::WG_PORT;
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use super::persistence::paths::KeysPaths;
use super::{Config, Debug, Gateway, Host, Http, NetworkRequester};
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let port = u16::deserialize(deserializer)?;
if port == 0 {
Ok(None)
} else {
Ok(Some(port))
}
}
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: Deserializer<'de>,
{
let path = PathBuf::deserialize(deserializer)?;
if path.as_os_str().is_empty() {
Ok(None)
} else {
Ok(Some(path))
}
}
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to gateways's config file.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_36 {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub host: Host,
#[serde(default)]
pub http: Http,
pub gateway: GatewayV1_1_36,
#[serde(default)]
// currently not really used for anything useful
pub wireguard: WireguardV1_1_36,
pub storage_paths: GatewayPathsV1_1_36,
pub network_requester: NetworkRequesterV1_1_36,
#[serde(default)]
pub ip_packet_router: IpPacketRouterV1_1_36,
#[serde(default)]
pub logging: LoggingSettingsV1_1_36,
#[serde(default)]
pub debug: DebugV1_1_36,
}
impl ConfigV1_1_36 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
read_config_from_toml_file(default_config_filepath(id))
}
}
impl From<ConfigV1_1_36> for Config {
fn from(value: ConfigV1_1_36) -> Self {
Self {
save_path: value.save_path,
host: value.host,
http: value.http,
gateway: Gateway {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
clients_wss_port: value.gateway.clients_wss_port,
enabled_statistics: value.gateway.enabled_statistics,
statistics_service_url: value.gateway.statistics_service_url,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPaths {
keys: KeysPaths {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
},
clients_storage: value.storage_paths.clients_storage,
network_requester_config: value.storage_paths.network_requester_config,
// \/ ADDED
ip_packet_router_config: Default::default(),
// /\ ADDED
},
network_requester: NetworkRequester {
enabled: value.network_requester.enabled,
},
// \/ ADDED
ip_packet_router: Default::default(),
// /\ ADDED
logging: LoggingSettings {
// no fields (yet)
},
debug: Debug {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
presence_sending_delay: value.debug.presence_sending_delay,
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
..Default::default()
},
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_36 {
/// Version of the gateway for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular gateway.
pub id: String,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
#[serde(default)]
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
pub clients_port: u16,
/// If applicable, announced port for listening for secure websocket client traffic.
/// (default: None)
#[serde(deserialize_with = "de_maybe_port")]
pub clients_wss_port: Option<u16>,
/// Whether gateway collects and sends anonymized statistics
pub enabled_statistics: bool,
/// Domain address of the statistics service
pub statistics_service_url: Url,
/// Addresses to APIs from which the node gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[serde(alias = "validator_nymd_urls")]
pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct WireguardV1_1_36 {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51820`
pub bind_address: SocketAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// 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: WireguardPathsV1_1_36,
}
impl Default for WireguardV1_1_36 {
fn default() -> Self {
Self {
enabled: false,
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), WG_PORT),
announced_port: WG_PORT,
storage_paths: WireguardPathsV1_1_36 {},
private_network_prefix: 16,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPathsV1_1_36 {
// pub keys:
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPathsV1_1_36 {
pub keys: KeysPathsV1_1_36,
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
/// Path to the configuration of the embedded network requester.
#[serde(deserialize_with = "de_maybe_path")]
pub network_requester_config: Option<PathBuf>,
// pub node_description: PathBuf,
// pub cosmos_bip39_mnemonic: PathBuf,
/// Path to the configuration of the embedded ip packet router.
#[serde(deserialize_with = "de_maybe_path")]
pub ip_packet_router_config: Option<PathBuf>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct KeysPathsV1_1_36 {
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
/// Path to file containing private sphinx key.
pub private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
pub public_sphinx_key_file: PathBuf,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct NetworkRequesterV1_1_36 {
/// Specifies whether network requester service is enabled in this process.
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV1_1_36 {
fn default() -> Self {
Self { enabled: false }
}
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1_1_36 {
// well, we need to implement something here at some point...
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_36 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
}
impl Default for DebugV1_1_36 {
fn default() -> Self {
Self {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
use_legacy_framed_packet_version: false,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct IpPacketRouterV1_1_36 {
/// Specifies whether ip packet router service is enabled in this process.
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for IpPacketRouterV1_1_36 {
fn default() -> Self {
Self { enabled: false }
}
}
-12
View File
@@ -169,15 +169,3 @@ impl KeysPaths {
&self.public_sphinx_key_file
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPaths {
// pub keys:
}
impl WireguardPaths {
pub fn new_empty() -> Self {
WireguardPaths {}
}
}
+21 -9
View File
@@ -38,7 +38,6 @@ pub enum GatewayError {
ConfigLoadFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
@@ -48,7 +47,6 @@ pub enum GatewayError {
NetworkRequesterConfigLoadFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
@@ -59,7 +57,16 @@ pub enum GatewayError {
IpPacketRouterConfigLoadFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to load config file for wireguard (gateway-id: '{id}') using path '{}'. detailed message: {source}",
path.display()
)]
WireguardConfigLoadFailure {
id: String,
path: PathBuf,
source: io::Error,
},
@@ -69,7 +76,6 @@ pub enum GatewayError {
ConfigSaveFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
@@ -80,10 +86,7 @@ pub enum GatewayError {
},
#[error("could not obtain the information about current gateways on the network: {source}")]
NetworkGatewaysQueryFailure {
#[source]
source: ValidatorClientError,
},
NetworkGatewaysQueryFailure { source: ValidatorClientError },
#[error("address {account} has an invalid bech32 prefix. it uses '{actual_prefix}' while '{expected_prefix}' was expected")]
InvalidBech32AccountPrefix {
@@ -145,7 +148,6 @@ pub enum GatewayError {
#[error("failed to catch an interrupt: {source}")]
ShutdownFailure {
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
@@ -167,6 +169,16 @@ pub enum GatewayError {
#[cfg(all(feature = "wireguard", target_os = "linux"))]
#[error("failed to remove wireguard interface: {0}")]
WireguardInterfaceError(#[from] defguard_wireguard_rs::error::WireguardInterfaceError),
#[cfg(all(feature = "wireguard", target_os = "linux"))]
#[error("wireguard not set")]
WireguardNotSet,
#[cfg(all(feature = "wireguard", target_os = "linux"))]
#[error("failed to catch an interrupt: {source}")]
StdError {
source: Box<dyn std::error::Error + Send + Sync>,
},
}
impl From<ClientCoreError> for GatewayError {
+8 -34
View File
@@ -4,7 +4,6 @@
use crate::config::Config;
use crate::error::GatewayError;
use crate::helpers::load_public_key;
use ipnetwork::IpNetwork;
use log::{debug, error, warn};
use nym_bin_common::bin_info_owned;
use nym_crypto::asymmetric::{encryption, identity};
@@ -12,30 +11,19 @@ use nym_network_requester::RequestFilter;
use nym_node_http_api::api::api_requests;
use nym_node_http_api::api::api_requests::v1::network_requester::exit_policy::models::UsedExitPolicy;
use nym_node_http_api::api::api_requests::SignedHostInformation;
use nym_node_http_api::router::WireguardAppState;
use nym_node_http_api::NymNodeHttpError;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient;
use nym_wireguard_types::registration::GatewayClientRegistry;
use std::net::{IpAddr, Ipv4Addr};
use nym_wireguard_types::WireguardGatewayData;
use std::sync::Arc;
fn load_gateway_details(
config: &Config,
) -> Result<api_requests::v1::gateway::models::Gateway, GatewayError> {
let wireguard = if config.wireguard.enabled {
Some(api_requests::v1::gateway::models::Wireguard {
port: config.wireguard.announced_port,
public_key: "placeholder key value".to_string(),
})
} else {
None
};
Ok(api_requests::v1::gateway::models::Gateway {
enforces_zk_nyms: config.gateway.only_coconut_credentials,
client_interfaces: api_requests::v1::gateway::models::ClientInterfaces {
wireguard,
wireguard: None,
mixnet_websockets: Some(api_requests::v1::gateway::models::WebSockets {
ws_port: config.gateway.clients_port,
wss_port: config.gateway.clients_wss_port,
@@ -154,7 +142,7 @@ pub(crate) struct HttpApiBuilder<'a> {
// TODO: this should be a wg specific key and not re-used sphinx
sphinx_keypair: Arc<encryption::KeyPair>,
client_registry: Option<Arc<GatewayClientRegistry>>,
wireguard_data: Option<Arc<WireguardGatewayData>>,
}
impl<'a> HttpApiBuilder<'a> {
@@ -170,7 +158,7 @@ impl<'a> HttpApiBuilder<'a> {
exit_policy: None,
identity_keypair,
sphinx_keypair,
client_registry: None,
wireguard_data: None,
}
}
@@ -235,11 +223,11 @@ impl<'a> HttpApiBuilder<'a> {
}
#[must_use]
pub(crate) fn with_wireguard_client_registry(
pub(crate) fn with_wireguard_data(
mut self,
client_registry: Arc<GatewayClientRegistry>,
wireguard_data: Option<Arc<WireguardGatewayData>>,
) -> Self {
self.client_registry = Some(client_registry);
self.wireguard_data = wireguard_data;
self
}
@@ -281,22 +269,8 @@ 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.and_then(|client_registry| {
WireguardAppState::new(
client_registry,
Default::default(),
self.gateway_config.wireguard.bind_address.port(),
wireguard_private_network,
)
.ok()
});
let bind_address = self.gateway_config.http.bind_address;
let router = nym_node_http_api::NymNodeRouter::new(config, None, wg_state);
let router = nym_node_http_api::NymNodeRouter::new(config, None, None);
tokio::spawn(async move {
let server = match router.build_server(&bind_address).await {
+28 -17
View File
@@ -16,7 +16,6 @@ use crate::node::client_handling::websocket::connection_handler::coconut::Coconu
use crate::node::helpers::{initialise_main_storage, load_network_requester_config};
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
use crate::node::statistics::collector::GatewayStatisticsCollector;
use dashmap::DashMap;
use futures::channel::{mpsc, oneshot};
use log::*;
use nym_crypto::asymmetric::{encryption, identity};
@@ -28,7 +27,7 @@ use nym_task::{TaskClient, TaskHandle, TaskManager};
use nym_types::gateway::GatewayNodeDetailsResponse;
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
use nym_wireguard_types::registration::GatewayClientRegistry;
use nym_wireguard_types::WireguardGatewayData;
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::net::SocketAddr;
@@ -126,9 +125,10 @@ pub struct Gateway<St = PersistentStorage> {
/// x25519 keypair used for Diffie-Hellman. Currently only used for sphinx key derivation.
sphinx_keypair: Arc<encryption::KeyPair>,
storage: St,
client_registry: Arc<GatewayClientRegistry>,
wireguard_data: Option<Arc<WireguardGatewayData>>,
run_http_server: bool,
task_client: Option<TaskClient>,
@@ -149,7 +149,7 @@ impl<St> Gateway<St> {
config,
network_requester_opts,
ip_packet_router_opts,
client_registry: Arc::new(DashMap::new()),
wireguard_data: None,
run_http_server: true,
task_client: None,
})
@@ -170,7 +170,7 @@ impl<St> Gateway<St> {
identity_keypair,
sphinx_keypair,
storage,
client_registry: Arc::new(DashMap::new()),
wireguard_data: None,
run_http_server: true,
task_client: None,
}
@@ -184,12 +184,14 @@ impl<St> Gateway<St> {
self.task_client = Some(task_client)
}
pub fn set_wireguard_client_registry(&mut self, client_registry: Arc<GatewayClientRegistry>) {
pub fn set_wireguard_data(&mut self, wireguard_data: Arc<WireguardGatewayData>) {
// sanity check:
if Arc::strong_count(&self.client_registry) != 1 {
panic!("the client registry is already being used elsewhere")
if let Some(wg_data) = self.wireguard_data.as_ref() {
if Arc::strong_count(wg_data) != 1 {
panic!("the client registry is already being used elsewhere")
}
}
self.client_registry = client_registry
self.wireguard_data = Some(wireguard_data)
}
pub async fn node_details(&self) -> Result<GatewayNodeDetailsResponse, GatewayError> {
@@ -229,8 +231,12 @@ impl<St> Gateway<St> {
async fn start_wireguard(
&self,
shutdown: TaskClient,
) -> Result<defguard_wireguard_rs::WGApi, Box<dyn std::error::Error + Send + Sync>> {
nym_wireguard::start_wireguard(shutdown, Arc::clone(&self.client_registry)).await
) -> Result<nym_wireguard::WgApiWrapper, Box<dyn std::error::Error + Send + Sync>> {
if let Some(wireguard_data) = self.wireguard_data.as_ref() {
nym_wireguard::start_wireguard(shutdown, Arc::clone(wireguard_data)).await
} else {
Err(Box::new(GatewayError::WireguardNotSet))
}
}
#[cfg(all(feature = "wireguard", not(target_os = "linux")))]
@@ -555,7 +561,7 @@ impl<St> Gateway<St> {
self.identity_keypair.as_ref(),
self.sphinx_keypair.clone(),
)
.with_wireguard_client_registry(self.client_registry.clone())
.with_wireguard_data(self.wireguard_data.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))
@@ -565,21 +571,26 @@ impl<St> Gateway<St> {
// Once this is a bit more mature, make this a commandline flag instead of a compile time
// flag
#[cfg(all(feature = "wireguard", target_os = "linux"))]
let wg_api = self.start_wireguard(shutdown.fork("wireguard")).await.ok();
let _wg_api = self
.start_wireguard(shutdown.fork("wireguard"))
.await
.map_err(|source| GatewayError::StdError { source })?;
#[cfg(all(feature = "wireguard", not(target_os = "linux")))]
self.start_wireguard(shutdown.fork("wireguard")).await;
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
info!(
"Public key: {:?}",
self.identity_keypair.public_key().to_string()
);
if let Err(source) = shutdown.wait_for_shutdown().await {
// that's a nasty workaround, but anyhow errors are generally nicer, especially on exit
return Err(GatewayError::ShutdownFailure { source });
}
#[cfg(all(feature = "wireguard", target_os = "linux"))]
if let Some(wg_api) = wg_api {
defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&wg_api)?;
}
Ok(())
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.37"
version = "1.1.38"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+4
View File
@@ -27,6 +27,10 @@ nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"]
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-node-requests = { path = "../../nym-node/nym-node-requests", default-features = false }
[dev-dependencies]
serde_json.workspace = true
[features]
default = []
generate-ts = ["ts-rs", "nym-mixnet-contract-common/generate-ts"]
+132 -7
View File
@@ -420,15 +420,110 @@ where
Ok(time::serde::rfc3339::deserialize(deserializer).unwrap_or_else(|_| unix_epoch()))
}
pub(crate) mod overengineered_offset_date_time_serde {
use crate::models::unix_epoch;
use serde::de::Visitor;
use serde::ser::Error;
use serde::{Deserializer, Serialize, Serializer};
use std::fmt::Formatter;
use time::format_description::well_known::Rfc3339;
use time::format_description::{modifier, BorrowedFormatItem, Component};
use time::OffsetDateTime;
struct OffsetDateTimeVisitor;
// copied from time library because they keep it private -.-
const DEFAULT_OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Compound(DATE_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(TIME_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
];
const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Component(Component::Year(modifier::Year::default())),
BorrowedFormatItem::Literal(b"-"),
BorrowedFormatItem::Component(Component::Month(modifier::Month::default())),
BorrowedFormatItem::Literal(b"-"),
BorrowedFormatItem::Component(Component::Day(modifier::Day::default())),
];
const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
BorrowedFormatItem::Literal(b"."),
BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
];
const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Component(Component::OffsetHour({
let mut m = modifier::OffsetHour::default();
m.sign_is_mandatory = true;
m
})),
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::OffsetMinute(
modifier::OffsetMinute::default(),
)),
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::OffsetSecond(
modifier::OffsetSecond::default(),
)),
])),
])),
];
impl<'de> Visitor<'de> for OffsetDateTimeVisitor {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("an rfc3339 or human-readable `OffsetDateTime`")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
// first try rfc3339, if that fails use default human-readable impl from time,
// finally fallback to default unix epoch
Ok(OffsetDateTime::parse(v, &Rfc3339).unwrap_or_else(|_| {
OffsetDateTime::parse(v, &DEFAULT_OFFSET_DATE_TIME_FORMAT)
.unwrap_or_else(|_| unix_epoch())
}))
}
}
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(OffsetDateTimeVisitor)
}
pub(crate) fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// serialize it with human-readable format for compatibility with eclipse and nutella clients
// in the future change it back to rfc3339
datetime
.format(&DEFAULT_OFFSET_DATE_TIME_FORMAT)
.map_err(S::Error::custom)?
.serialize(serializer)
}
}
// for all intents and purposes it's just OffsetDateTime, but we need JsonSchema...
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
pub struct OffsetDateTimeJsonSchemaWrapper(
#[serde(
default = "unix_epoch",
serialize_with = "time::serde::rfc3339::serialize",
deserialize_with = "de_rfc3339_or_default"
)]
pub OffsetDateTime,
#[serde(default = "unix_epoch", with = "overengineered_offset_date_time_serde")]
pub OffsetDateTime,
);
impl Default for OffsetDateTimeJsonSchemaWrapper {
@@ -599,3 +694,33 @@ pub struct PartialTestResult {
pub type MixnodeTestResultResponse = PaginatedResponse<PartialTestResult>;
pub type GatewayTestResultResponse = PaginatedResponse<PartialTestResult>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn offset_date_time_json_schema_wrapper_serde_backwards_compat() {
let mut dummy = OffsetDateTimeJsonSchemaWrapper::default();
dummy.0 += Duration::from_millis(1);
let ser = serde_json::to_string(&dummy).unwrap();
assert_eq!("\"1970-01-01 00:00:00.001 +00:00:00\"", ser);
let human_readable = "\"2024-05-23 07:41:02.756283766 +00:00:00\"";
let rfc3339 = "\"2002-10-02T15:00:00Z\"";
let rfc3339_offset = "\"2002-10-02T10:00:00-05:00\"";
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(human_readable).unwrap();
assert_eq!(de.0.unix_timestamp(), 1716450062);
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(rfc3339).unwrap();
assert_eq!(de.0.unix_timestamp(), 1033570800);
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(rfc3339_offset).unwrap();
assert_eq!(de.0.unix_timestamp(), 1033570800);
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>("\"nonsense\"").unwrap();
assert_eq!(de.0.unix_timestamp(), 0);
}
}
+403 -83
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -41,7 +41,7 @@ tap = "1.0.1"
tauri = { version = "1.4.1", features = ["clipboard-write-text", "macos-private-api", "notification-all", "shell-open", "system-tray", "updater", "window-close", "window-minimize", "window-start-dragging"] }
#tendermint-rpc = "0.23.0"
thiserror = "1.0"
time = { version = "0.3.17", features = ["local-offset"] }
time = { version = "0.3.30", features = ["local-offset"] }
tokio = { version = "1.24.1", features = ["sync", "time"] }
url = "2.4"
yaml-rust = "0.4"
+4 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-node"
version = "1.1.1"
version = "1.1.2"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -60,3 +60,6 @@ nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
[build-dependencies]
# temporary bonding information v1 (to grab and parse nym-mixnode and nym-gateway package versions)
cargo_metadata = { workspace = true }
[features]
wireguard = ["nym-gateway/wireguard"]
+1
View File
@@ -38,6 +38,7 @@ nym-wireguard = { path = "../../common/wireguard" }
nym-wireguard-types = { path = "../../common/wireguard-types", features = ["verify"] }
[dev-dependencies]
base64 = { workspace = true }
hyper.workspace = true
dashmap.workspace = true
serde_json.workspace = true
@@ -10,7 +10,6 @@ use crate::router::types::RequestError;
use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use axum::Json;
use nym_crypto::asymmetric::encryption::PublicKey;
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey,
};
@@ -31,7 +30,10 @@ async fn process_final_message(
}
};
if client.verify(&state.private_key, preshared_nonce).is_ok() {
if client
.verify(state.keypair.private_key(), preshared_nonce)
.is_ok()
{
state.registration_in_progress.remove(&client.pub_key());
state.client_registry.insert(client.pub_key(), client);
@@ -86,8 +88,7 @@ pub(crate) async fn register_client(
match payload {
ClientMessage::Initial(init) => {
let remote_public = PublicKey::from_bytes(init.pub_key().as_bytes())
.map_err(|_| RequestError::new_status(StatusCode::BAD_REQUEST))?;
let remote_public = init.pub_key().inner();
let nonce = process_init_message(init, state).await;
let mut private_ip_ref = state
.free_private_network_ips
@@ -101,7 +102,7 @@ pub(crate) async fn register_client(
// mark it as used, even though it's not final
*private_ip_ref = false;
let gateway_data = GatewayClient::new(
&state.private_key,
state.keypair.private_key(),
remote_public,
*private_ip_ref.key(),
nonce,
@@ -8,11 +8,11 @@ use crate::error::NymNodeHttpError;
use axum::routing::{get, post};
use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::x25519::KeyPair;
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard::setup;
use nym_wireguard_types::registration::PrivateIPs;
use nym_wireguard_types::registration::{GatewayClientRegistry, PendingRegistrations};
use nym_wireguard_types::WireguardGatewayData;
use std::sync::Arc;
pub(crate) mod client_registry;
@@ -27,17 +27,15 @@ pub struct WireguardAppState {
impl WireguardAppState {
pub fn new(
client_registry: Arc<GatewayClientRegistry>,
wireguard_gateway_data: &WireguardGatewayData,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
private_ip_network: IpNetwork,
) -> Result<Self, NymNodeHttpError> {
Ok(WireguardAppState {
inner: Some(WireguardAppStateInner {
private_key: Arc::new(PrivateKey::from_bytes(
setup::server_static_private_key().as_ref(),
)?),
client_registry,
keypair: wireguard_gateway_data.keypair().clone(),
client_registry: wireguard_gateway_data.client_registry().clone(),
registration_in_progress,
binding_port,
free_private_network_ips: Arc::new(
@@ -83,7 +81,7 @@ macro_rules! get_state {
#[derive(Clone)]
pub(crate) struct WireguardAppStateInner {
private_key: Arc<PrivateKey>,
keypair: Arc<KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
@@ -108,6 +106,7 @@ mod test {
use axum::body::Body;
use axum::http::Request;
use axum::http::StatusCode;
use base64::{engine::general_purpose, Engine as _};
use dashmap::DashMap;
use hmac::Mac;
use ipnetwork::IpNetwork;
@@ -117,7 +116,6 @@ mod test {
PeerPublicKey,
};
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard::setup::server_static_private_key;
use nym_wireguard_types::registration::HmacSha256;
use std::net::IpAddr;
use std::str::FromStr;
@@ -126,6 +124,22 @@ mod test {
use tower::ServiceExt;
use x25519_dalek::{PublicKey, StaticSecret};
const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
general_purpose::STANDARD
.decode(base64_key)
.unwrap()
.try_into()
.unwrap()
}
fn server_static_private_key() -> x25519_dalek::StaticSecret {
// TODO: this is a temporary solution for development
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
x25519_dalek::StaticSecret::from(static_private_bytes)
}
#[tokio::test]
async fn registration() {
// 1. Provision random keys for gateway and client
@@ -168,7 +182,7 @@ mod test {
let state = WireguardAppState {
inner: Some(WireguardAppStateInner {
client_registry: Arc::clone(&client_registry),
private_key: Arc::new(gateway_private_key),
keypair: Arc::new(gateway_key_pair),
registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080,
free_private_network_ips,
+5 -10
View File
@@ -18,8 +18,8 @@ use nym_mixnode::MixnodeError;
use nym_network_requester::{CustomGatewayDetails, GatewayDetails};
use nym_node::config;
use nym_node::config::mixnode::DEFAULT_VERLOC_PORT;
use nym_node::config::Config;
use nym_node::config::{default_config_filepath, ConfigBuilder, NodeMode};
use nym_node::config::{Config, DEFAULT_WIREGUARD_NETWORK_IP};
use nym_node::error::{EntryGatewayError, ExitGatewayError, NymNodeError};
use nym_node_http_api::api::api_requests::v1::node::models::NodeDescription;
use rand::rngs::OsRng;
@@ -308,6 +308,8 @@ async fn migrate_mixnode(mut args: Args) -> Result<(), NymNodeError> {
// exit gateway initialisation
crate::node::ExitGatewayData::initialise(&config.exit_gateway, ed25519_public_key).await?;
crate::node::WireguardData::initialise(&config.wireguard)?;
config.save()?;
info!(
@@ -400,15 +402,6 @@ async fn migrate_gateway(mut args: Args) -> Result<(), NymNodeError> {
},
..config::MixnodeConfig::new_default()
}))
.with_wireguard(args.wireguard.override_config_section(config::Wireguard {
enabled: cfg.wireguard.enabled,
bind_address: cfg.wireguard.bind_address,
private_network_ip: DEFAULT_WIREGUARD_NETWORK_IP,
announced_port: cfg.wireguard.announced_port,
private_network_prefix: cfg.wireguard.private_network_prefix,
// this is fine as currently the paths stored inside gateway itself are empty
storage_paths: config::persistence::WireguardPaths::new(&data_dir),
}))
.with_entry_gateway(args.entry_gateway.override_config_section(
config::EntryGatewayConfig {
storage_paths: config::persistence::EntryGatewayPaths::new(&data_dir),
@@ -606,6 +599,8 @@ async fn migrate_gateway(mut args: Args) -> Result<(), NymNodeError> {
.await?;
}
crate::node::WireguardData::initialise(&config.wireguard)?;
save_node_description(
&config.storage_paths.description,
&NodeDescription::default(),
+6 -6
View File
@@ -253,13 +253,13 @@ pub(crate) struct WireguardArgs {
)]
pub(crate) wireguard_bind_address: Option<SocketAddr>,
/// Ip address of the private wireguard network.
/// default: `10.1.0.0`
/// Private IP address of the wireguard gateway.
/// default: `10.1.0.1`
#[clap(
long,
env = NYMNODE_WG_IP_NETWORK_ARG,
env = NYMNODE_WG_IP_ARG,
)]
pub(crate) wireguard_private_network_ip: Option<IpAddr>,
pub(crate) wireguard_private_ip: Option<IpAddr>,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
@@ -300,8 +300,8 @@ impl WireguardArgs {
section.announced_port = announced_port
}
if let Some(private_network_ip) = self.wireguard_private_network_ip {
section.private_network_ip = private_network_ip
if let Some(private_ip) = self.wireguard_private_ip {
section.private_ip = private_ip
}
if let Some(private_network_prefix) = self.wireguard_private_network_prefix {
+16
View File
@@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize};
use std::path::Path;
use url::Url;
use super::LocalWireguardOpts;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ExitGatewayConfig {
@@ -136,6 +138,7 @@ pub struct EphemeralConfig {
pub gateway: nym_gateway::config::Config,
pub nr_opts: LocalNetworkRequesterOpts,
pub ipr_opts: LocalIpPacketRouterOpts,
pub wg_opts: LocalWireguardOpts,
}
fn base_client_config(config: &Config) -> nym_client_core_config_types::Client {
@@ -241,6 +244,18 @@ pub fn ephemeral_exit_gateway_config(
let ipr_enabled = config.exit_gateway.ip_packet_router.debug.enabled;
let nr_enabled = config.exit_gateway.network_requester.debug.enabled;
let wg_opts = LocalWireguardOpts {
config: super::Wireguard {
enabled: config.wireguard.enabled,
bind_address: config.wireguard.bind_address,
private_ip: config.wireguard.private_ip,
announced_port: config.wireguard.announced_port,
private_network_prefix: config.wireguard.private_network_prefix,
storage_paths: config.wireguard.storage_paths.clone(),
},
custom_mixnet_path: None,
};
let mut gateway = ephemeral_gateway_config(config, mnemonic)?;
gateway.ip_packet_router.enabled = ipr_enabled;
gateway.network_requester.enabled = nr_enabled;
@@ -253,6 +268,7 @@ pub fn ephemeral_exit_gateway_config(
Ok(EphemeralConfig {
nr_opts,
ipr_opts,
wg_opts,
gateway,
})
}
-9
View File
@@ -57,19 +57,10 @@ pub fn ephemeral_gateway_config(
cosmos_mnemonic: mnemonic.clone(),
};
let wireguard = nym_gateway::config::Wireguard {
enabled: config.wireguard.enabled,
bind_address: config.wireguard.bind_address,
announced_port: config.wireguard.announced_port,
private_network_prefix: config.wireguard.private_network_prefix,
storage_paths: nym_gateway::config::WireguardPaths::new_empty(),
};
Ok(nym_gateway::config::Config::externally_loaded(
host,
http,
gateway,
wireguard,
nym_gateway::config::GatewayPaths::new_empty(),
nym_gateway::config::NetworkRequester { enabled: false },
nym_gateway::config::IpPacketRouter { enabled: false },
+23 -5
View File
@@ -40,7 +40,7 @@ pub use crate::config::mixnode::MixnodeConfig;
const DEFAULT_NYMNODES_DIR: &str = "nym-nodes";
pub const DEFAULT_WIREGUARD_PORT: u16 = WG_PORT;
pub const DEFAULT_WIREGUARD_NETWORK_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(10, 1, 0, 0));
pub const DEFAULT_WIREGUARD_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(10, 1, 0, 1));
pub const DEFAULT_WIREGUARD_PREFIX: u8 = 16;
pub const DEFAULT_HTTP_PORT: u16 = DEFAULT_NYM_NODE_HTTP_PORT;
pub const DEFAULT_MIXNET_PORT: u16 = DEFAULT_MIX_LISTENING_PORT;
@@ -500,9 +500,9 @@ pub struct Wireguard {
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Ip address of the private wireguard network.
/// default: `10.1.0.0`
pub private_network_ip: IpAddr,
/// Private IP address of the wireguard gateway.
/// default: `10.1.0.1`
pub private_ip: IpAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
@@ -524,10 +524,28 @@ impl Wireguard {
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
DEFAULT_WIREGUARD_PORT,
),
private_network_ip: DEFAULT_WIREGUARD_NETWORK_IP,
private_ip: DEFAULT_WIREGUARD_IP,
announced_port: DEFAULT_WIREGUARD_PORT,
private_network_prefix: DEFAULT_WIREGUARD_PREFIX,
storage_paths: persistence::WireguardPaths::new(data_dir),
}
}
}
impl From<Wireguard> for nym_wireguard_types::Config {
fn from(value: Wireguard) -> Self {
nym_wireguard_types::Config {
bind_address: value.bind_address,
private_ip: value.private_ip,
announced_port: value.announced_port,
private_network_prefix: value.private_network_prefix,
}
}
}
#[derive(Debug, Clone)]
pub struct LocalWireguardOpts {
pub config: Wireguard,
pub custom_mixnet_path: Option<PathBuf>,
}
+19 -3
View File
@@ -43,6 +43,10 @@ pub const DEFAULT_IPR_ACK_KEY_FILENAME: &str = "aes128ctr_ipr_ack";
pub const DEFAULT_IPR_REPLY_SURB_DB_FILENAME: &str = "ipr_persistent_reply_store.sqlite";
pub const DEFAULT_IPR_GATEWAYS_DB_FILENAME: &str = "ipr_gateways_info_store.sqlite";
// Wireguard
pub const DEFAULT_X25519_WG_DH_KEY_FILENAME: &str = "x25519_wg_dh";
pub const DEFAULT_X25519_WG_PUBLIC_DH_KEY_FILENAME: &str = "x25519_wg_dh.pub";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NymNodePaths {
@@ -366,11 +370,23 @@ impl ExitGatewayPaths {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPaths {
// pub keys:
pub private_diffie_hellman_key_file: PathBuf,
pub public_diffie_hellman_key_file: PathBuf,
}
impl WireguardPaths {
pub fn new<P: AsRef<Path>>(_data_dir: P) -> Self {
WireguardPaths {}
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
WireguardPaths {
private_diffie_hellman_key_file: data_dir.join(DEFAULT_X25519_WG_DH_KEY_FILENAME),
public_diffie_hellman_key_file: data_dir.join(DEFAULT_X25519_WG_PUBLIC_DH_KEY_FILENAME),
}
}
pub fn x25519_wireguard_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_diffie_hellman_key_file,
&self.public_diffie_hellman_key_file,
)
}
}
+8 -5
View File
@@ -115,9 +115,9 @@ enabled = {{ wireguard.enabled }}
# default: `0.0.0.0:51822`
bind_address = '{{ wireguard.bind_address }}'
# Ip address of the private wireguard network.
# default: `10.1.0.0`
private_network_ip = '{{ wireguard.private_network_ip }}'
# Private IP address of the wireguard gateway.
# default: `10.1.0.1`
private_ip = '{{ wireguard.private_ip }}'
# Port announced to external clients wishing to connect to the wireguard interface.
# Useful in the instances where the node is behind a proxy.
@@ -127,9 +127,12 @@ announced_port = {{ wireguard.announced_port }}
# The maximum value for IPv4 is 32 and for IPv6 is 128
private_network_prefix = {{ wireguard.private_network_prefix }}
# Paths for wireguard keys, client registries, etc.
[wireguard.storage_paths]
# currently empty
# Path to file containing wireguard x25519 diffie hellman private key.
private_diffie_hellman_key_file = '{{ wireguard.storage_paths.private_diffie_hellman_key_file }}'
# Path to file containing wireguard x25519 diffie hellman public key.
public_diffie_hellman_key_file = '{{ wireguard.storage_paths.public_diffie_hellman_key_file }}'
##### mixnode mode nym-node config options #####
+140 -1
View File
@@ -1,12 +1,151 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::WireguardPaths;
use crate::config::Config;
use crate::error::NymNodeError;
use std::path::Path;
// currently there are no upgrades
async fn try_upgrade_config<P: AsRef<Path>>(_path: P) -> Result<(), NymNodeError> {
async fn try_upgrade_config<P: AsRef<Path>>(path: P) -> Result<(), NymNodeError> {
use crate::config::*;
use crate::error::KeyIOFailure;
use nym_crypto::asymmetric::encryption::KeyPair;
use nym_pemstore::store_keypair;
use rand::rngs::OsRng;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldWireguardPaths {
// pub keys:
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldWireguard {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Ip address of the private wireguard network.
/// default: `10.1.0.0`
pub private_network_ip: IpAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// 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: OldWireguardPaths,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfig {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
/// Human-readable ID of this particular node.
pub id: String,
/// Current mode of this nym-node.
/// Expect this field to be changed in the future to allow running the node in multiple modes (i.e. mixnode + gateway)
pub mode: NodeMode,
pub host: Host,
pub mixnet: Mixnet,
/// Storage paths to persistent nym-node data, such as its long term keys.
pub storage_paths: NymNodePaths,
#[serde(default)]
pub http: Http,
pub wireguard: OldWireguard,
pub mixnode: MixnodeConfig,
pub entry_gateway: EntryGatewayConfig,
pub exit_gateway: ExitGatewayConfig,
#[serde(default)]
pub logging: LoggingSettings,
}
impl NymConfigTemplate for OldConfig {
fn template(&self) -> &'static str {
CONFIG_TEMPLATE
}
}
impl OldConfig {
fn read_from_path<P: AsRef<Path>>(path: P) -> Result<Self, NymNodeError> {
let path = path.as_ref();
let mut loaded: OldConfig = read_config_from_toml_file(path).map_err(|source| {
NymNodeError::ConfigLoadFailure {
path: path.to_path_buf(),
source,
}
})?;
loaded.save_path = Some(path.to_path_buf());
debug!("loaded config file from {}", path.display());
Ok(loaded)
}
}
fn initialise(config: &Wireguard) -> std::io::Result<()> {
let mut rng = OsRng;
let x25519_keys = KeyPair::new(&mut rng);
store_keypair(
&x25519_keys,
&config.storage_paths.x25519_wireguard_storage_paths(),
)?;
Ok(())
}
let old_cfg = OldConfig::read_from_path(&path)?;
let wireguard = Wireguard {
enabled: old_cfg.wireguard.enabled,
bind_address: old_cfg.wireguard.bind_address,
private_ip: old_cfg.wireguard.private_network_ip,
announced_port: old_cfg.wireguard.announced_port,
private_network_prefix: old_cfg.wireguard.private_network_prefix,
storage_paths: WireguardPaths::new(Config::default_data_directory(path)?),
};
initialise(&wireguard).map_err(|err| KeyIOFailure::KeyPairStoreFailure {
keys: "wg-x25519-dh".to_string(),
paths: wireguard.storage_paths.x25519_wireguard_storage_paths(),
err,
})?;
let cfg = Config {
save_path: old_cfg.save_path,
id: old_cfg.id,
mode: old_cfg.mode,
host: old_cfg.host,
mixnet: old_cfg.mixnet,
storage_paths: old_cfg.storage_paths,
http: old_cfg.http,
wireguard,
mixnode: old_cfg.mixnode,
entry_gateway: old_cfg.entry_gateway,
exit_gateway: old_cfg.exit_gateway,
logging: old_cfg.logging,
};
cfg.save()?;
Ok(())
}
+1 -1
View File
@@ -40,7 +40,7 @@ pub mod vars {
// wireguard:
pub const NYMNODE_WG_ENABLED_ARG: &str = "NYMNODE_WG_ENABLED";
pub const NYMNODE_WG_BIND_ADDRESS_ARG: &str = "NYMNODE_WG_BIND_ADDRESS";
pub const NYMNODE_WG_IP_NETWORK_ARG: &str = "NYMNODE_WG_IP_NETWORK";
pub const NYMNODE_WG_IP_ARG: &str = "NYMNODE_WG_IP";
pub const NYMNODE_WG_ANNOUNCED_PORT_ARG: &str = "NYMNODE_WG_ANNOUNCED_PORT";
pub const NYMNODE_WG_PRIVATE_NETWORK_PREFIX_ARG: &str = "NYMNODE_WG_PRIVATE_NETWORK_PREFIX";
+7
View File
@@ -34,6 +34,7 @@ pub(crate) struct DisplayDetails {
pub(crate) ed25519_identity_key: String,
pub(crate) x25519_sphinx_key: String,
pub(crate) x25519_noise_key: String,
pub(crate) x25519_wireguard_key: String,
pub(crate) exit_network_requester_address: String,
pub(crate) exit_ip_packet_router_address: String,
@@ -139,6 +140,12 @@ pub(crate) fn load_x25519_noise_keypair(
Ok(load_keypair(paths, "x25519-noise")?)
}
pub(crate) fn load_x25519_wireguard_keypair(
paths: KeyPairPath,
) -> Result<x25519::KeyPair, NymNodeError> {
Ok(load_keypair(paths, "x25519-wireguard")?)
}
pub(crate) fn load_x25519_sphinx_public_key<P: AsRef<Path>>(
path: P,
) -> Result<x25519::PublicKey, NymNodeError> {
+69 -10
View File
@@ -20,7 +20,9 @@ use nym_network_requester::{
use nym_node::config::entry_gateway::ephemeral_entry_gateway_config;
use nym_node::config::exit_gateway::ephemeral_exit_gateway_config;
use nym_node::config::mixnode::ephemeral_mixnode_config;
use nym_node::config::{Config, EntryGatewayConfig, ExitGatewayConfig, MixnodeConfig, NodeMode};
use nym_node::config::{
Config, EntryGatewayConfig, ExitGatewayConfig, MixnodeConfig, NodeMode, Wireguard,
};
use nym_node::error::{EntryGatewayError, ExitGatewayError, MixnodeError, NymNodeError};
use nym_node_http_api::api::api_requests;
use nym_node_http_api::api::api_requests::v1::node::models::NodeDescription;
@@ -31,7 +33,7 @@ use nym_node_http_api::{NymNodeHTTPServer, NymNodeRouter};
use nym_sphinx_acknowledgements::AckKey;
use nym_sphinx_addressing::Recipient;
use nym_task::{TaskClient, TaskManager};
use nym_wireguard_types::registration::GatewayClientRegistry;
use nym_wireguard_types::WireguardGatewayData;
use rand::rngs::OsRng;
use rand::{CryptoRng, RngCore};
use std::path::Path;
@@ -39,6 +41,8 @@ use std::sync::Arc;
use tracing::{debug, error, info, trace};
use zeroize::Zeroizing;
use self::helpers::load_x25519_wireguard_keypair;
pub mod bonding_information;
pub mod description;
pub mod helpers;
@@ -63,7 +67,7 @@ impl MixnodeData {
pub struct EntryGatewayData {
mnemonic: Zeroizing<bip39::Mnemonic>,
client_storage: nym_gateway::node::PersistentStorage,
client_registry: Arc<GatewayClientRegistry>,
wireguard_data: WireguardGatewayData,
}
impl EntryGatewayData {
@@ -81,7 +85,10 @@ impl EntryGatewayData {
Ok(())
}
async fn new(config: &EntryGatewayConfig) -> Result<EntryGatewayData, EntryGatewayError> {
async fn new(
config: &EntryGatewayConfig,
wireguard_data: WireguardGatewayData,
) -> Result<EntryGatewayData, EntryGatewayError> {
Ok(EntryGatewayData {
mnemonic: config.storage_paths.load_mnemonic_from_file()?,
client_storage: nym_gateway::node::PersistentStorage::init(
@@ -90,7 +97,7 @@ impl EntryGatewayData {
)
.await
.map_err(nym_gateway::GatewayError::from)?,
client_registry: Arc::new(Default::default()),
wireguard_data: wireguard_data.clone(),
})
}
}
@@ -244,6 +251,33 @@ impl ExitGatewayData {
}
}
pub struct WireguardData {
x25519_wireguard_keys: Arc<x25519::KeyPair>,
}
impl WireguardData {
pub(crate) fn new(config: &Wireguard) -> Result<Self, NymNodeError> {
Ok(WireguardData {
x25519_wireguard_keys: Arc::new(load_x25519_wireguard_keypair(
config.storage_paths.x25519_wireguard_storage_paths(),
)?),
})
}
pub(crate) fn initialise(config: &Wireguard) -> Result<(), ExitGatewayError> {
let mut rng = OsRng;
let x25519_keys = x25519::KeyPair::new(&mut rng);
store_keypair(
&x25519_keys,
config.storage_paths.x25519_wireguard_storage_paths(),
"wg-x25519-dh",
)?;
Ok(())
}
}
pub(crate) struct NymNode {
config: Config,
description: NodeDescription,
@@ -259,6 +293,8 @@ pub(crate) struct NymNode {
#[allow(dead_code)]
exit_gateway: ExitGatewayData,
wireguard: WireguardData,
ed25519_identity_keys: Arc<ed25519::KeyPair>,
x25519_sphinx_keys: Arc<x25519::KeyPair>,
@@ -314,10 +350,18 @@ impl NymNode {
ExitGatewayData::initialise(&config.exit_gateway, *ed25519_identity_keys.public_key())
.await?;
// wireguard initialisation
WireguardData::initialise(&config.wireguard)?;
config.save()
}
pub(crate) async fn new(config: Config) -> Result<Self, NymNodeError> {
let wireguard_data = WireguardData::new(&config.wireguard)?;
let wireguard_gateway_data = WireguardGatewayData::new(
config.wireguard.clone().into(),
wireguard_data.x25519_wireguard_keys.clone(),
);
Ok(NymNode {
ed25519_identity_keys: Arc::new(load_ed25519_identity_keypair(
config.storage_paths.keys.ed25519_identity_storage_paths(),
@@ -331,8 +375,10 @@ impl NymNode {
description: load_node_description(&config.storage_paths.description)?,
verloc_stats: Default::default(),
mixnode: MixnodeData::new(&config.mixnode)?,
entry_gateway: EntryGatewayData::new(&config.entry_gateway).await?,
entry_gateway: EntryGatewayData::new(&config.entry_gateway, wireguard_gateway_data)
.await?,
exit_gateway: ExitGatewayData::new(&config.exit_gateway)?,
wireguard: wireguard_data,
config,
})
}
@@ -353,6 +399,10 @@ impl NymNode {
)
}
fn x25519_wireguard_key(&self) -> &x25519::PublicKey {
self.wireguard.x25519_wireguard_keys.public_key()
}
pub(crate) fn display_details(&self) -> DisplayDetails {
DisplayDetails {
current_mode: self.config.mode,
@@ -360,6 +410,7 @@ impl NymNode {
ed25519_identity_key: self.ed25519_identity_key().to_base58_string(),
x25519_sphinx_key: self.x25519_sphinx_key().to_base58_string(),
x25519_noise_key: self.x25519_noise_key().to_base58_string(),
x25519_wireguard_key: self.x25519_wireguard_key().to_base58_string(),
exit_network_requester_address: self.exit_network_requester_address().to_string(),
exit_ip_packet_router_address: self.exit_ip_packet_router_address().to_string(),
}
@@ -409,6 +460,10 @@ impl NymNode {
let config =
ephemeral_entry_gateway_config(self.config.clone(), &self.entry_gateway.mnemonic)?;
let wireguard_data = Arc::new(WireguardGatewayData::new(
self.config.wireguard.clone().into(),
self.wireguard.x25519_wireguard_keys.clone(),
));
let mut entry_gateway = Gateway::new_loaded(
config,
None,
@@ -419,7 +474,7 @@ impl NymNode {
);
entry_gateway.disable_http_server();
entry_gateway.set_task_client(task_client);
entry_gateway.set_wireguard_client_registry(self.entry_gateway.client_registry.clone());
entry_gateway.set_wireguard_data(wireguard_data);
tokio::spawn(async move {
if let Err(err) = entry_gateway.run().await {
@@ -434,6 +489,10 @@ impl NymNode {
let config =
ephemeral_exit_gateway_config(self.config.clone(), &self.entry_gateway.mnemonic)?;
let wireguard_data = Arc::new(WireguardGatewayData::new(
self.config.wireguard.clone().into(),
self.wireguard.x25519_wireguard_keys.clone(),
));
let mut exit_gateway = Gateway::new_loaded(
config.gateway,
@@ -445,7 +504,7 @@ impl NymNode {
);
exit_gateway.disable_http_server();
exit_gateway.set_task_client(task_client);
exit_gateway.set_wireguard_client_registry(self.entry_gateway.client_registry.clone());
exit_gateway.set_wireguard_data(wireguard_data);
tokio::spawn(async move {
if let Err(err) = exit_gateway.run().await {
@@ -521,12 +580,12 @@ impl NymNode {
};
let wireguard_private_network = IpNetwork::new(
self.config.wireguard.private_network_ip,
self.config.wireguard.private_ip,
self.config.wireguard.private_network_prefix,
)?;
let wg_state = WireguardAppState::new(
self.entry_gateway.client_registry.clone(),
&self.entry_gateway.wireguard_data,
Default::default(),
self.config.wireguard.bind_address.port(),
wireguard_private_network,
+348 -67
View File
@@ -68,7 +68,7 @@ dependencies = [
"cipher 0.4.4",
"ctr 0.9.2",
"ghash",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -213,6 +213,12 @@ dependencies = [
"system-deps 6.1.1",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "attohttpc"
version = "0.22.0"
@@ -220,7 +226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7"
dependencies = [
"flate2",
"http",
"http 0.2.9",
"log",
"native-tls",
"serde",
@@ -279,6 +285,12 @@ version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
@@ -299,7 +311,7 @@ dependencies = [
"rand_core 0.6.4",
"ripemd",
"sha2 0.10.8",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -402,7 +414,7 @@ dependencies = [
"group",
"pairing",
"rand_core 0.6.4",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -535,6 +547,15 @@ dependencies = [
"libc",
]
[[package]]
name = "celes"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39b9a21273925d7cc9e8a9a5f068122341336813c607014f5ef64f82b6acba58"
dependencies = [
"serde",
]
[[package]]
name = "cesu8"
version = "1.1.0"
@@ -970,7 +991,7 @@ checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
dependencies = [
"generic-array 0.14.7",
"rand_core 0.6.4",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -997,12 +1018,12 @@ dependencies = [
[[package]]
name = "crypto-mac"
version = "0.11.1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
dependencies = [
"generic-array 0.14.7",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -1070,7 +1091,7 @@ dependencies = [
"digest 0.9.0",
"rand_core 0.5.1",
"serde",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -1086,7 +1107,7 @@ dependencies = [
"fiat-crypto",
"platforms",
"rustc_version",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -1330,7 +1351,7 @@ dependencies = [
"block-buffer 0.10.4",
"const-oid",
"crypto-common",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -1534,7 +1555,7 @@ dependencies = [
"rand_core 0.6.4",
"sec1",
"serdect",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -1595,9 +1616,9 @@ dependencies = [
[[package]]
name = "eyre"
version = "0.6.8"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
@@ -1636,7 +1657,7 @@ checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
"bitvec",
"rand_core 0.6.4",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -1669,9 +1690,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.26"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -2109,7 +2130,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core 0.6.4",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -2178,7 +2199,7 @@ dependencies = [
"futures-core",
"futures-sink",
"futures-util",
"http",
"http 0.2.9",
"indexmap 1.9.3",
"slab",
"tokio",
@@ -2186,6 +2207,25 @@ dependencies = [
"tracing",
]
[[package]]
name = "h2"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.1.0",
"indexmap 2.0.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "handlebars"
version = "3.5.5"
@@ -2273,7 +2313,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
dependencies = [
"crypto-mac 0.11.1",
"crypto-mac 0.11.0",
"digest 0.9.0",
]
@@ -2311,6 +2351,17 @@ dependencies = [
"itoa 1.0.9",
]
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa 1.0.9",
]
[[package]]
name = "http-body"
version = "0.4.5"
@@ -2318,7 +2369,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
"http 0.2.9",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
dependencies = [
"bytes",
"http 1.1.0",
]
[[package]]
name = "http-body-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
dependencies = [
"bytes",
"futures-core",
"http 1.1.0",
"http-body 1.0.0",
"pin-project-lite",
]
@@ -2375,9 +2449,9 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"h2 0.3.20",
"http 0.2.9",
"http-body 0.4.5",
"httparse",
"httpdate",
"itoa 1.0.9",
@@ -2389,6 +2463,26 @@ dependencies = [
"want",
]
[[package]]
name = "hyper"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2 0.4.5",
"http 1.1.0",
"http-body 1.0.0",
"httparse",
"itoa 1.0.9",
"pin-project-lite",
"smallvec",
"tokio",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.24.2"
@@ -2396,24 +2490,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http",
"hyper",
"rustls",
"http 0.2.9",
"hyper 0.14.27",
"rustls 0.21.9",
"tokio",
"tokio-rustls",
"tokio-rustls 0.24.1",
]
[[package]]
name = "hyper-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
dependencies = [
"futures-util",
"http 1.1.0",
"hyper 1.3.1",
"hyper-util",
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.25.0",
"tower-service",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"hyper",
"http-body-util",
"hyper 1.3.1",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]]
name = "hyper-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.3.1",
"pin-project-lite",
"socket2 0.5.5",
"tokio",
"tower",
"tower-service",
"tracing",
]
[[package]]
@@ -2907,6 +3041,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-derive"
version = "0.3.3"
@@ -3174,7 +3314,8 @@ name = "nym-http-api-client"
version = "0.1.0"
dependencies = [
"async-trait",
"reqwest",
"http 1.1.0",
"reqwest 0.12.4",
"serde",
"serde_json",
"thiserror",
@@ -3250,6 +3391,7 @@ name = "nym-node-requests"
version = "0.1.0"
dependencies = [
"base64 0.21.4",
"celes",
"humantime 2.1.0",
"humantime-serde",
"nym-bin-common",
@@ -3321,7 +3463,7 @@ dependencies = [
"nym-mixnet-contract-common",
"nym-validator-client",
"nym-vesting-contract-common",
"reqwest",
"reqwest 0.12.4",
"schemars",
"serde",
"serde_json",
@@ -3370,7 +3512,7 @@ dependencies = [
"nym-service-provider-directory-common",
"nym-vesting-contract-common",
"prost",
"reqwest",
"reqwest 0.12.4",
"serde",
"serde_json",
"sha2 0.9.9",
@@ -3472,7 +3614,7 @@ dependencies = [
"once_cell",
"pretty_env_logger",
"rand_chacha 0.2.2",
"reqwest",
"reqwest 0.12.4",
"serde",
"serde_json",
"serde_repr",
@@ -3689,7 +3831,7 @@ checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -4319,12 +4461,54 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-rustls",
"h2 0.3.20",
"http 0.2.9",
"http-body 0.4.5",
"hyper 0.14.27",
"hyper-rustls 0.24.2",
"ipnet",
"js-sys",
"log",
"mime",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.21.9",
"rustls-native-certs",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
"serde_urlencoded",
"system-configuration",
"tokio",
"tokio-rustls 0.24.1",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg 0.50.0",
]
[[package]]
name = "reqwest"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
dependencies = [
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.4.5",
"http 1.1.0",
"http-body 1.0.0",
"http-body-util",
"hyper 1.3.1",
"hyper-rustls 0.26.0",
"hyper-tls",
"hyper-util",
"ipnet",
"js-sys",
"log",
@@ -4333,23 +4517,24 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls-native-certs",
"rustls-pemfile",
"rustls 0.22.4",
"rustls-pemfile 2.1.2",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-rustls 0.25.0",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"winreg",
"winreg 0.52.0",
]
[[package]]
@@ -4359,7 +4544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
"hmac 0.12.1",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -4445,10 +4630,24 @@ checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
dependencies = [
"log",
"ring",
"rustls-webpki",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.4",
"subtle 2.5.0",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.6.3"
@@ -4456,7 +4655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
dependencies = [
"openssl-probe",
"rustls-pemfile",
"rustls-pemfile 1.0.4",
"schannel",
"security-framework",
]
@@ -4470,6 +4669,22 @@ dependencies = [
"base64 0.21.4",
]
[[package]]
name = "rustls-pemfile"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
dependencies = [
"base64 0.22.1",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -4480,6 +4695,17 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@@ -4574,7 +4800,7 @@ dependencies = [
"generic-array 0.14.7",
"pkcs8",
"serdect",
"subtle 2.4.1",
"subtle 2.5.0",
"zeroize",
]
@@ -4958,7 +5184,7 @@ dependencies = [
"rand 0.7.3",
"rand_distr",
"sha2 0.9.9",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -5076,9 +5302,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "subtle"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "subtle-encoding"
@@ -5117,6 +5343,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "system-configuration"
version = "0.5.1"
@@ -5252,7 +5484,7 @@ dependencies = [
"glob",
"gtk",
"heck 0.4.1",
"http",
"http 0.2.9",
"ignore",
"minisign-verify",
"objc",
@@ -5349,7 +5581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b"
dependencies = [
"gtk",
"http",
"http 0.2.9",
"http-range",
"rand 0.8.5",
"raw-window-handle",
@@ -5447,7 +5679,7 @@ dependencies = [
"serde_repr",
"sha2 0.10.8",
"signature 2.1.0",
"subtle 2.4.1",
"subtle 2.5.0",
"subtle-encoding",
"tendermint-proto",
"time",
@@ -5499,12 +5731,12 @@ dependencies = [
"getrandom 0.2.10",
"peg",
"pin-project",
"reqwest",
"reqwest 0.11.22",
"semver 1.0.22",
"serde",
"serde_bytes",
"serde_json",
"subtle 2.4.1",
"subtle 2.5.0",
"subtle-encoding",
"tendermint",
"tendermint-config",
@@ -5576,13 +5808,14 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.30"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.9",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
@@ -5598,10 +5831,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.15"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",
]
@@ -5666,7 +5900,18 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls",
"rustls 0.21.9",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
@@ -5727,6 +5972,28 @@ dependencies = [
"winnow",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"pin-project",
"pin-project-lite",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
[[package]]
name = "tower-service"
version = "0.3.2"
@@ -5740,6 +6007,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -5879,7 +6147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle 2.4.1",
"subtle 2.5.0",
]
[[package]]
@@ -6139,9 +6407,12 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.25.4"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webview2-com"
@@ -6482,6 +6753,16 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "winreg"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "winres"
version = "0.1.12"
@@ -6508,7 +6789,7 @@ dependencies = [
"glib",
"gtk",
"html5ever",
"http",
"http 0.2.9",
"kuchiki",
"libc",
"log",
+1 -1
View File
@@ -40,7 +40,7 @@ strum = { version = "0.23", features = ["derive"] }
tap = "1"
tauri = { version = "=1.2.3", features = ["clipboard-all", "shell-open", "updater", "window-maximize", "window-print"] }
#tendermint-rpc = "0.23.0"
time = { version = "0.3.17", features = ["local-offset"] }
time = { version = "0.3.30", features = ["local-offset"] }
thiserror = "1.0"
tokio = { version = "1.10", features = ["full"] }
toml = "0.5.8"
@@ -6,6 +6,7 @@ import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyForm
import { yupResolver } from '@hookform/resolvers/yup/dist/yup';
import { GatewayData } from '../../../pages/bonding/types';
import { gatewayValidationSchema } from './gatewayValidationSchema';
import { TermsAndConditions, TermsAndConditionsHelp } from './TermsAndConditions';
const GatewayInitForm = ({
gatewayData,
@@ -21,6 +22,7 @@ const GatewayInitForm = ({
formState: { errors },
handleSubmit,
setValue,
setError,
} = useForm({ resolver: yupResolver(gatewayValidationSchema), defaultValues: gatewayData });
const handleRequestValidation = (event: { detail: { step: number } }) => {
@@ -30,7 +32,11 @@ const GatewayInitForm = ({
...data,
version: clean(data.version) as string,
};
onNext(validatedData);
if (!validatedData.acceptedTermsAndConditions) {
setError('acceptedTermsAndConditions', { message: 'You must accept the terms and conditions' });
} else {
onNext(validatedData);
}
})();
}
};
@@ -117,6 +123,17 @@ const GatewayInitForm = ({
/>
</Stack>
)}
<FormControlLabel
{...register('acceptedTermsAndConditions')}
name="acceptedTermsAndConditions"
required
control={<Checkbox />}
label={<TermsAndConditions error={Boolean(errors.acceptedTermsAndConditions)} />}
/>
<TermsAndConditionsHelp
error={Boolean(errors.acceptedTermsAndConditions)}
helperText={errors.acceptedTermsAndConditions?.message}
/>
</Stack>
);
};
@@ -6,6 +6,7 @@ import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyForm
import { yupResolver } from '@hookform/resolvers/yup/dist/yup';
import { mixnodeValidationSchema } from './mixnodeValidationSchema';
import { MixnodeData } from '../../../pages/bonding/types';
import { TermsAndConditions, TermsAndConditionsHelp } from './TermsAndConditions';
const MixnodeInitForm = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; onNext: (data: any) => void }) => {
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
@@ -15,6 +16,7 @@ const MixnodeInitForm = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; on
formState: { errors },
handleSubmit,
setValue,
setError,
} = useForm({ resolver: yupResolver(mixnodeValidationSchema), defaultValues: mixnodeData });
const handleRequestValidation = (event: { detail: { step: number } }) => {
@@ -24,7 +26,11 @@ const MixnodeInitForm = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; on
...data,
version: clean(data.version),
};
onNext(validatedData);
if (!validatedData.acceptedTermsAndConditions) {
setError('acceptedTermsAndConditions', { message: 'You must accept the terms and conditions' });
} else {
onNext(validatedData);
}
})();
}
};
@@ -110,6 +116,17 @@ const MixnodeInitForm = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; on
/>
</Stack>
)}
<FormControlLabel
{...register('acceptedTermsAndConditions')}
name="acceptedTermsAndConditions"
required
control={<Checkbox />}
label={<TermsAndConditions error={Boolean(errors.acceptedTermsAndConditions)} />}
/>
<TermsAndConditionsHelp
error={Boolean(errors.acceptedTermsAndConditions)}
helperText={errors.acceptedTermsAndConditions?.message}
/>
</Stack>
);
};
@@ -0,0 +1,29 @@
import React from 'react';
import Typography from '@mui/material/Typography';
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
export const TermsAndConditions: React.FC<{
error?: boolean;
}> = ({ error }) => (
<Typography display="inline" color={(theme) => (error ? theme.palette.error.main : undefined)}>
I agree to the{' '}
<a href="https://nymtech.net/terms-and-conditions/operators/v1.0.0" target="_blank" rel="noreferrer">
operator terms and conditions
</a>
</Typography>
);
export const TermsAndConditionsHelp: React.FC<{
error?: boolean;
helperText?: string;
}> = ({ error, helperText }) => {
if (!error || !helperText) {
return null;
}
return (
<Typography color="error.main" display="flex" alignItems="center">
<WarningAmberIcon sx={{ mr: 1 }} />
{helperText}
</Typography>
);
};
+1
View File
@@ -11,6 +11,7 @@ export type NodeIdentity = {
host: string;
version: string;
mixPort: number;
acceptedTermsAndConditions?: boolean;
};
export type MixnodeData = NodeIdentity & {
@@ -15,9 +15,9 @@ const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
+2 -2
View File
@@ -13,9 +13,9 @@ const defaultUrl = 'https://nymtech.net/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -4,7 +4,7 @@ import { Callout } from 'nextra/components'
An easy way to secure parts or all of your web app is to replace calls to [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) with `mixFetch`:
MixFetch works the same as vanilla `fetch` as it's a proxied wrapper around the original function.
MixFetch works the same as vanilla `fetch` as it's a proxied wrapper around the original function.
Sounds great, are there any catches? Well, there are a few (for now):
1. Currently, the operators of Network Requesters that make the final request at the egress part of the Nym mixnet to
@@ -16,15 +16,23 @@ in combination with their own configuration. If you are trying to access somethi
3. If you are using `mixFetch` in a web app with HTTPS you will need to use a gateway that has Secure Websockets to
avoid getting a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error.
4. For now, mixfetch doesn't work with SURBS, altough this may change in the future.
4. For now, mixfetch doesn't work with SURBS, altough this may change in the future.
Read [this article](https://blog.nymtech.net/mixfetch-like-the-fetch-api-but-via-the-mixnet-82acfd435c62) to learn more about mixFetch.
<Callout type="info" emoji="️">
We are currently working on a feature that adds a Secure Websocket (WSS) listener with HTTPS (automatically generated with LetsEncrypt) to Nym's
gateways.
While we are adding this feature, you can use a gateway that has Caddy providing HTTPS/WSS by adding this to the options when setting up `mixFetch`:
Right now Gateways are not required to run a Secure Websocket (WSS) listener, so only a subset of nodes running in Gateway mode have configured their nodes to do so.
For the moment you have to select a Gateway that has WSS enabled from [this list](https://harbourmaster.nymtech.net/v1/services?wss=true).
You can also find WSS-enabled nodes by querying the `gateways/described` endpoint on the Nym API, either via the [Swagger webpage](https://validator.nymtech.net/api/swagger/index.html) or with `curl`:
```
curl -X 'GET' \
'https://validator.nymtech.net/api/v1/gateways/described' \
-H 'accept: application/json'
```
</Callout>
```ts
@@ -32,14 +40,14 @@ Read [this article](https://blog.nymtech.net/mixfetch-like-the-fetch-api-but-via
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
forceTls: true, // force WSS
extra: {},
extra: {},
};
```
@@ -53,7 +61,7 @@ npm create vite@latest
During the environment setup, choose React and subsequently opt for Typescript if you want your application to function smoothly following this tutorial. Next, navigate to your application directory and run the following commands:
```bash
cd < YOUR_APP >
npm i
npm i
npm run dev
```
@@ -74,7 +82,7 @@ import { mixFetch } from "@nymproject/mix-fetch-full-fat";
##### Example: using the `mixFetch` client:
`Get` and `Post` outputs will be observable from your console.
`Get` and `Post` outputs will be observable from your console.
```ts
import "./App.css";
@@ -82,9 +90,9 @@ import { mixFetch, SetupMixFetchOps } from '@nymproject/mix-fetch-full-fat';
import React from 'react';
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -113,7 +121,7 @@ export function HttpGET() {
export function HttpPOST() {
async function post () {
//Make sure the URL is whitelisted (see 'standard allowed list') otherwise you will get a network requester filter check error
const apiResponse = await mixFetch('https://postman-echo.com/post', {
const apiResponse = await mixFetch('https://httpbin.org/post', {
method: 'POST',
body: JSON.stringify({ foo: 'bar' }),
headers: { 'Content-Type': 'application/json' }
@@ -3,9 +3,22 @@
import { MixFetch } from '../../components/mix-fetch'
import Box from '@mui/material/Box';
import FormattedMixFetchExampleCode from '../../code-examples/mixfetch-example-code.mdx';
import { Callout } from 'nextra/components'
<Callout type="info" emoji="️">
Right now Gateways are not required to run a Secure Websocket (WSS) listener, so only a subset of nodes running in Gateway mode have configured their nodes to do so.
For the moment you have to select a Gateway that has WSS enabled from [this list](https://harbourmaster.nymtech.net/v1/services?wss=true).
You can also find WSS-enabled nodes by querying the `gateways/described` endpoint on the Nym API and filtering for `wss_port`, either via the [Swagger webpage](https://validator.nymtech.net/api/swagger/index.html) or with `curl`:
```
curl -X 'GET' \
'https://validator.nymtech.net/api/v1/gateways/described' \
-H 'accept: application/json'
```
</Callout>
Look in the [standard allowed list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) and
use the playground below to do a HTTP GET request to any host in the allowed list:
<MixFetch />
<FormattedMixFetchExampleCode />
@@ -4,7 +4,7 @@
[package]
name = "nym-network-requester"
license = "GPL-3.0"
version = "1.1.35"
version = "1.1.36"
authors.workspace = true
edition.workspace = true
rust-version = "1.70"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.35"
version = "1.1.36"
authors.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nymvisor"
version = "0.1.0"
version = "0.1.1"
authors.workspace = true
repository.workspace = true
homepage.workspace = true