Files
nym/common/wireguard/src/lib.rs
T
Jędrzej Stuczyński 6b2bb3029b feat: merge intermediate upgrade mode changes (#6174)
* squashing feat: merge intermediate upgrade mode changes #6174 to more easily resolve merge conflicts during rebasing

added additional v2 query for metadata endpoint for requesting upgrade mode recheck

added additional message to v6 authenticator to request explicit upgrade mode recheck

clippy

test fixes due to updated keys

updated assertion for upgrading v1 top up request to v2

compare attester public key against the expected value within the credential proxy

use pre-generated attestation public keys within nym-nodes

remove version deprecation

bugfix: default bandwidth response for authenticator

expose upgrade mode information in authenticator responses

adding tests for new v2 server

passing upgrade mode information in metadata endpoint

v2 wireguard private metadata

bugfix: make sure to immediately poll for attestation after spawning task

fix gateway probe and remove code duplication for finalizing registration

squashing before rebasing

post rebasing fixes

AuthenticatorVersion helpers

additional nits

allow unwraps in mocks

fixed linux build

clippy

integrating upgrade mode into authenticator

fixed build after adding wrappers to response types

conditionally updating peer handle bandwidth

cleanup

negotiate initial protocol during registration

change auth to use highest protocol

handler for JWT message

dont meter client bandwidth in upgrade mode

handling recheck requests

sending information about upgrade_mode on client messages

gateway watching for upgrade mode attestation

wip: gateways to disable bandwidth metering on upgrade mode

* fixed ServerResponse deserialisation

* fixed incorrect swagger path for upgrade mode check endpoint

* moved upgrade mode endpoint out of bandwidth routes

* chore: remove unused error variant

* removed re-export of UpgradeModeAttestation from credentials-interface

* chore: define single source of truth for minimum bandwidth threshold value

* moved type definitions out of traits.rs

* updated v6 versioning to point to niolo release instead

* fixed incorrect error mapping
2025-11-14 13:13:15 +00:00

265 lines
7.8 KiB
Rust

// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
// #![warn(clippy::pedantic)]
// #![warn(clippy::expect_used)]
// #![warn(clippy::unwrap_used)]
use defguard_wireguard_rs::{WGApi, WireguardInterfaceApi, host::Peer, key::Key, net::IpAddrMask};
use nym_crypto::asymmetric::x25519::KeyPair;
use nym_wireguard_types::Config;
use peer_controller::PeerControlRequest;
use std::sync::Arc;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tracing::error;
#[cfg(target_os = "linux")]
use nym_credential_verification::ecash::EcashManager;
#[cfg(target_os = "linux")]
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
pub mod error;
pub mod peer_controller;
pub mod peer_handle;
pub mod peer_storage_manager;
pub use error::Error;
pub const CONTROL_CHANNEL_SIZE: usize = 256;
pub struct WgApiWrapper {
inner: WGApi,
}
impl WireguardInterfaceApi for WgApiWrapper {
fn create_interface(
&self,
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.create_interface()
}
fn assign_address(
&self,
address: &IpAddrMask,
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.assign_address(address)
}
fn configure_peer_routing(
&self,
peers: &[Peer],
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.configure_peer_routing(peers)
}
#[cfg(not(target_os = "windows"))]
fn configure_interface(
&self,
config: &defguard_wireguard_rs::InterfaceConfiguration,
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.configure_interface(config)
}
#[cfg(target_os = "windows")]
fn configure_interface(
&self,
config: &defguard_wireguard_rs::InterfaceConfiguration,
dns: &[std::net::IpAddr],
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.configure_interface(config, dns)
}
fn remove_interface(
&self,
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.remove_interface()
}
fn configure_peer(
&self,
peer: &Peer,
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.configure_peer(peer)
}
fn remove_peer(
&self,
peer_pubkey: &Key,
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.remove_peer(peer_pubkey)
}
fn read_interface_data(
&self,
) -> Result<
defguard_wireguard_rs::host::Host,
defguard_wireguard_rs::error::WireguardInterfaceError,
> {
self.inner.read_interface_data()
}
fn configure_dns(
&self,
dns: &[std::net::IpAddr],
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
self.inner.configure_dns(dns)
}
}
impl WgApiWrapper {
pub fn new(wg_api: WGApi) -> Self {
WgApiWrapper { inner: wg_api }
}
}
impl Drop for WgApiWrapper {
fn drop(&mut self) {
if let Err(e) = defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&self.inner)
{
error!("Could not remove the wireguard interface: {e:?}");
}
}
}
#[derive(Clone)]
pub struct WireguardGatewayData {
config: Config,
keypair: Arc<KeyPair>,
peer_tx: Sender<PeerControlRequest>,
}
impl WireguardGatewayData {
pub fn new(config: Config, keypair: Arc<KeyPair>) -> (Self, Receiver<PeerControlRequest>) {
let (peer_tx, peer_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
(
WireguardGatewayData {
config,
keypair,
peer_tx,
},
peer_rx,
)
}
pub fn config(&self) -> Config {
self.config
}
pub fn keypair(&self) -> &Arc<KeyPair> {
&self.keypair
}
pub fn peer_tx(&self) -> &Sender<PeerControlRequest> {
&self.peer_tx
}
}
pub struct WireguardData {
pub inner: WireguardGatewayData,
pub peer_rx: Receiver<PeerControlRequest>,
}
/// Start wireguard device
#[cfg(target_os = "linux")]
pub async fn start_wireguard(
ecash_manager: Arc<EcashManager>,
metrics: nym_node_metrics::NymNodeMetrics,
peers: Vec<Peer>,
upgrade_mode_status: nym_credential_verification::upgrade_mode::UpgradeModeStatus,
shutdown_token: nym_task::ShutdownToken,
wireguard_data: WireguardData,
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
use base64::{Engine, prelude::BASE64_STANDARD};
use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi};
use ip_network::IpNetwork;
use nym_credential_verification::ecash::traits::EcashManager;
use peer_controller::PeerController;
use std::collections::HashMap;
use tokio::sync::RwLock;
use tracing::info;
let ifname = String::from(WG_TUN_BASE_NAME);
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?;
let mut peer_bandwidth_managers = HashMap::with_capacity(peers.len());
for peer in peers.iter() {
let bandwidth_manager = peer_handle::SharedBandwidthStorageManager::new(
Arc::new(RwLock::new(
PeerController::generate_bandwidth_manager(
ecash_manager.storage(),
&peer.public_key,
)
.await?,
)),
peer.allowed_ips.clone(),
);
peer_bandwidth_managers.insert(peer.public_key.clone(), (bandwidth_manager, peer.clone()));
}
wg_api.create_interface()?;
let interface_config = InterfaceConfiguration {
name: ifname.clone(),
prvkey: BASE64_STANDARD.encode(wireguard_data.inner.keypair().private_key().to_bytes()),
address: wireguard_data.inner.config().private_ipv4.to_string(),
port: wireguard_data.inner.config().announced_tunnel_port as u32,
peers,
mtu: None,
};
info!(
"attempting to configure wireguard interface '{ifname}': address={}, port={}",
interface_config.address, interface_config.port
);
wg_api.configure_interface(&interface_config)?;
std::process::Command::new("ip")
.args([
"-6",
"addr",
"add",
&format!(
"{}/{}",
wireguard_data.inner.config().private_ipv6,
wireguard_data.inner.config().private_network_prefix_v6
),
"dev",
(&ifname),
])
.output()?;
// Use a dummy peer to create routing rule for the entire network space
let mut catch_all_peer = Peer::new(Key::new([0; 32]));
let network_v4 = IpNetwork::new_truncate(
wireguard_data.inner.config().private_ipv4,
wireguard_data.inner.config().private_network_prefix_v4,
)?;
let network_v6 = IpNetwork::new_truncate(
wireguard_data.inner.config().private_ipv6,
wireguard_data.inner.config().private_network_prefix_v6,
)?;
catch_all_peer.set_allowed_ips(vec![
IpAddrMask::new(network_v4.network_address(), network_v4.netmask()),
IpAddrMask::new(network_v6.network_address(), network_v6.netmask()),
]);
wg_api.configure_peer_routing(&[catch_all_peer])?;
let host = wg_api.read_interface_data()?;
let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api));
let mut controller = PeerController::new(
ecash_manager,
metrics,
wg_api.clone(),
host,
peer_bandwidth_managers,
wireguard_data.inner.peer_tx.clone(),
wireguard_data.peer_rx,
upgrade_mode_status,
shutdown_token,
);
tokio::spawn(async move { controller.run().await });
Ok(wg_api)
}