mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 14:33:37 -08:00
docs(wireguard-broker): add docs and examples
This commit is contained in:
@@ -1,3 +1,52 @@
|
||||
//! Asynchronous WireGuard PSK broker client using mio for non-blocking I/O.
|
||||
//!
|
||||
//! This module provides a client implementation that communicates with a WireGuard broker
|
||||
//! through Unix domain sockets using non-blocking I/O operations. It's designed to be used
|
||||
//! in event-driven applications using the mio event framework.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use mio::net::UnixStream;
|
||||
//! # use rosenpass_wireguard_broker::brokers::mio_client::MioBrokerClient;
|
||||
//! # use rosenpass_wireguard_broker::{WireGuardBroker, WireguardBrokerMio};
|
||||
//! # use mio::{Events, Interest, Poll, Token};
|
||||
//! # use rosenpass_secret_memory::{Public, Secret};
|
||||
//! # use rosenpass_wireguard_broker::api::config::NetworkBrokerConfig;
|
||||
//! # use rosenpass_wireguard_broker::SerializedBrokerConfig;
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let socket = UnixStream::connect("/path/to/broker.sock")?;
|
||||
//! let mut client = MioBrokerClient::new(socket);
|
||||
//!
|
||||
//! // Set up mio polling
|
||||
//! let mut poll = Poll::new()?;
|
||||
//! let mut events = Events::with_capacity(128);
|
||||
//! client.register(&poll.registry(), Token(0))?;
|
||||
//!
|
||||
//! // Prepare PSK configuration
|
||||
//! let network_config = NetworkBrokerConfig {
|
||||
//! iface: "wg0",
|
||||
//! peer_id: &Public::zero(), // Replace with actual peer ID
|
||||
//! psk: &Secret::zero(), // Replace with actual PSK
|
||||
//! };
|
||||
//!
|
||||
//! // Convert to serialized format and send
|
||||
//! let config: SerializedBrokerConfig = network_config.into();
|
||||
//! client.set_psk(config)?;
|
||||
//!
|
||||
//! // Process responses in event loop
|
||||
//! loop {
|
||||
//! poll.poll(&mut events, None)?;
|
||||
//! for event in &events {
|
||||
//! if event.token() == Token(0) {
|
||||
//! client.process_poll()?;
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use mio::Interest;
|
||||
use rosenpass_secret_memory::Secret;
|
||||
@@ -13,12 +62,44 @@ use crate::api::client::{
|
||||
};
|
||||
use crate::{SerializedBrokerConfig, WireGuardBroker, WireguardBrokerMio};
|
||||
|
||||
/// WireGuard broker client using mio for non-blocking I/O operations.
|
||||
///
|
||||
/// This client communicates with a WireGuard broker through a Unix domain socket,
|
||||
/// using length-prefixed messages for communication. It supports both the basic
|
||||
/// `WireGuardBroker` operations and non-blocking I/O through the
|
||||
/// `WireguardBrokerMio` trait.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use mio::net::UnixStream;
|
||||
/// use rosenpass_wireguard_broker::brokers::mio_client::MioBrokerClient;
|
||||
/// use rosenpass_wireguard_broker::{WireGuardBroker, SerializedBrokerConfig};
|
||||
/// use rosenpass_secret_memory::{Public, Secret};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let socket = UnixStream::connect("/path/to/broker.sock")?;
|
||||
/// let mut client = MioBrokerClient::new(socket);
|
||||
///
|
||||
/// // Set a PSK
|
||||
/// let config = SerializedBrokerConfig {
|
||||
/// interface: "wg0".as_bytes(),
|
||||
/// peer_id: &Public::zero(), // Replace with actual peer ID
|
||||
/// psk: &Secret::zero(), // Replace with actual PSK
|
||||
/// additional_params: &[],
|
||||
/// };
|
||||
///
|
||||
/// client.set_psk(config)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct MioBrokerClient {
|
||||
inner: BrokerClient<MioBrokerClientIo>,
|
||||
mio_token: Option<mio::Token>,
|
||||
}
|
||||
|
||||
/// A buffer wrapper that provides secure memory for sensitive data.
|
||||
#[derive(Debug)]
|
||||
struct SecretBuffer<const N: usize>(pub Secret<N>);
|
||||
|
||||
@@ -43,6 +124,10 @@ impl<const N: usize> BorrowMut<[u8]> for SecretBuffer<N> {
|
||||
type ReadBuffer = LengthPrefixDecoder<SecretBuffer<4096>>;
|
||||
type WriteBuffer = LengthPrefixEncoder<SecretBuffer<4096>>;
|
||||
|
||||
/// I/O implementation for the broker client using non-blocking operations.
|
||||
///
|
||||
/// This type handles the low-level details of sending and receiving length-prefixed
|
||||
/// messages over a Unix domain socket.
|
||||
#[derive(Debug)]
|
||||
struct MioBrokerClientIo {
|
||||
socket: mio::net::UnixStream,
|
||||
@@ -51,6 +136,10 @@ struct MioBrokerClientIo {
|
||||
}
|
||||
|
||||
impl MioBrokerClient {
|
||||
/// Creates a new client from a Unix domain socket.
|
||||
///
|
||||
/// The socket should be connected to a WireGuard broker server that speaks
|
||||
/// the same protocol.
|
||||
pub fn new(socket: mio::net::UnixStream) -> Self {
|
||||
let read_buffer = LengthPrefixDecoder::new(SecretBuffer::new());
|
||||
let write_buffer = LengthPrefixEncoder::from_buffer(SecretBuffer::new());
|
||||
@@ -66,6 +155,10 @@ impl MioBrokerClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls for and processes any pending responses from the broker.
|
||||
///
|
||||
/// This method should be called when the socket becomes readable according
|
||||
/// to mio events.
|
||||
fn poll(&mut self) -> anyhow::Result<()> {
|
||||
self.inner.io_mut().flush()?;
|
||||
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
//! Native Unix implementation of the WireGuard PSK broker using the `wg` command-line tool.
|
||||
//!
|
||||
//! This module provides an implementation that works on Unix systems by executing the `wg`
|
||||
//! command-line tool to set pre-shared keys. It requires the `wg` tool to be installed and
|
||||
//! accessible in the system PATH.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use rosenpass_secret_memory::{Public, Secret};
|
||||
//! use rosenpass_wireguard_broker::brokers::native_unix::{NativeUnixBroker, NativeUnixBrokerConfigBase};
|
||||
//! use rosenpass_wireguard_broker::{WireGuardBroker, WireguardBrokerCfg, WG_KEY_LEN, WG_PEER_LEN};
|
||||
//!
|
||||
//! # fn main() -> Result<(), anyhow::Error> {
|
||||
//! // Create a broker instance
|
||||
//! let mut broker = NativeUnixBroker::new();
|
||||
//!
|
||||
//! // Create configuration
|
||||
//! let config = NativeUnixBrokerConfigBase {
|
||||
//! interface: "wg0".to_string(),
|
||||
//! peer_id: Public::zero(), // Replace with actual peer ID
|
||||
//! extra_params: Vec::new(),
|
||||
//! };
|
||||
//!
|
||||
//! // Set PSK using the broker
|
||||
//! let psk = Secret::<WG_KEY_LEN>::zero(); // Replace with actual PSK
|
||||
//! let serialized_config = config.create_config(&psk);
|
||||
//! broker.set_psk(serialized_config)?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::thread;
|
||||
@@ -12,9 +44,21 @@ use rosenpass_util::{b64::B64Display, file::StoreValueB64Writer};
|
||||
use crate::{SerializedBrokerConfig, WireGuardBroker, WireguardBrokerCfg, WireguardBrokerMio};
|
||||
use crate::{WG_KEY_LEN, WG_PEER_LEN};
|
||||
|
||||
/// Maximum size of a base64-encoded WireGuard key in bytes
|
||||
const MAX_B64_KEY_SIZE: usize = WG_KEY_LEN * 5 / 3;
|
||||
/// Maximum size of a base64-encoded WireGuard peer ID in bytes
|
||||
const MAX_B64_PEER_ID_SIZE: usize = WG_PEER_LEN * 5 / 3;
|
||||
|
||||
/// A WireGuard broker implementation that uses the native `wg` command-line tool.
|
||||
///
|
||||
/// This broker executes the `wg` command to set pre-shared keys. It supports both synchronous
|
||||
/// operations through the `WireGuardBroker` trait and asynchronous operations through the
|
||||
/// `WireguardBrokerMio` trait.
|
||||
///
|
||||
/// # Requirements
|
||||
///
|
||||
/// - The `wg` command-line tool must be installed and in the system PATH
|
||||
/// - The user running the broker must have sufficient permissions to execute `wg` commands
|
||||
#[derive(Debug)]
|
||||
pub struct NativeUnixBroker {
|
||||
mio_token: Option<mio::Token>,
|
||||
@@ -110,16 +154,63 @@ impl WireguardBrokerMio for NativeUnixBroker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Base configuration for the native Unix WireGuard broker.
|
||||
///
|
||||
/// This configuration type is used to store persistent broker settings and create
|
||||
/// serialized configurations for individual PSK operations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass_wireguard_broker::brokers::native_unix::NativeUnixBrokerConfigBase;
|
||||
/// use rosenpass_secret_memory::Public;
|
||||
/// use rosenpass_wireguard_broker::WG_PEER_LEN;
|
||||
///
|
||||
/// let config = NativeUnixBrokerConfigBase {
|
||||
/// interface: "wg0".to_string(),
|
||||
/// peer_id: Public::zero(),
|
||||
/// extra_params: Vec::new(),
|
||||
/// };
|
||||
/// ```
|
||||
#[derive(Debug, Builder)]
|
||||
#[builder(pattern = "mutable")]
|
||||
pub struct NativeUnixBrokerConfigBase {
|
||||
/// Name of the WireGuard interface (e.g., "wg0")
|
||||
pub interface: String,
|
||||
/// Public key of the peer
|
||||
pub peer_id: Public<WG_PEER_LEN>,
|
||||
/// Additional parameters to pass to the wg command
|
||||
#[builder(private)]
|
||||
pub extra_params: Vec<u8>,
|
||||
}
|
||||
|
||||
impl NativeUnixBrokerConfigBaseBuilder {
|
||||
/// Sets the peer ID from a base64-encoded string.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `peer_id` - Base64-encoded peer public key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use rosenpass_wireguard_broker::brokers::native_unix::{NativeUnixBrokerConfigBaseBuilder};
|
||||
/// let mut peer_cfg = NativeUnixBrokerConfigBaseBuilder::default();
|
||||
/// // set peer id to [48;32] encoded as base64
|
||||
/// peer_cfg.peer_id_b64("MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA=")?;
|
||||
/// peer_cfg.interface("wg0".to_string());
|
||||
/// peer_cfg.extra_params_ser(&vec![])?;
|
||||
/// let peer_cfg = peer_cfg.build()?;
|
||||
/// assert_eq!(peer_cfg.peer_id.value, [48u8;32]);
|
||||
///
|
||||
/// let error = NativeUnixBrokerConfigBaseBuilder::default()
|
||||
/// .peer_id_b64("MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA") // invalid base64 encoding
|
||||
/// .err().unwrap();
|
||||
/// assert_eq!(error.to_string(), "Failed to parse peer id b64");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn peer_id_b64(
|
||||
&mut self,
|
||||
peer_id: &str,
|
||||
@@ -133,6 +224,29 @@ impl NativeUnixBrokerConfigBaseBuilder {
|
||||
Ok(self.peer_id(peer_id_b64))
|
||||
}
|
||||
|
||||
/// Sets additional parameters for the wg command.
|
||||
///
|
||||
/// Note: This function cannot fail as `Vec<String>` is always serializable.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use rosenpass_wireguard_broker::brokers::native_unix::NativeUnixBrokerConfigBaseBuilder;
|
||||
///
|
||||
/// let mut peer_cfg = NativeUnixBrokerConfigBaseBuilder::default();
|
||||
/// // Set typical wireguard parameters
|
||||
/// peer_cfg.interface("wg0".to_string());
|
||||
/// peer_cfg.peer_id_b64("Zm9v")?;
|
||||
/// peer_cfg.extra_params_ser(&vec![
|
||||
/// "persistent-keepalive".to_string(),
|
||||
/// "25".to_string(),
|
||||
/// "allowed-ips".to_string(),
|
||||
/// "10.0.0.2/32".to_string(),
|
||||
/// ])?;
|
||||
/// let peer_cfg = peer_cfg.build()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn extra_params_ser(
|
||||
&mut self,
|
||||
extra_params: &Vec<String>,
|
||||
@@ -157,12 +271,17 @@ impl WireguardBrokerCfg for NativeUnixBrokerConfigBase {
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime configuration for a single PSK operation.
|
||||
#[derive(Debug, Builder)]
|
||||
#[builder(pattern = "mutable")]
|
||||
pub struct NativeUnixBrokerConfig<'a> {
|
||||
/// WireGuard interface name
|
||||
pub interface: &'a str,
|
||||
/// Public key of the peer
|
||||
pub peer_id: &'a Public<WG_PEER_LEN>,
|
||||
/// Pre-shared key to set
|
||||
pub psk: &'a Secret<WG_KEY_LEN>,
|
||||
/// Additional wg command parameters
|
||||
pub extra_params: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
#![cfg(target_os = "linux")]
|
||||
//! Linux-specific WireGuard PSK broker implementation using netlink.
|
||||
//!
|
||||
//! This module provides direct kernel communication through netlink sockets for managing
|
||||
//! WireGuard pre-shared keys. It's more efficient than the command-line implementation
|
||||
//! but only available on Linux systems.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use rosenpass_secret_memory::{Public, Secret};
|
||||
//! use rosenpass_wireguard_broker::{WireGuardBroker, SerializedBrokerConfig, WG_KEY_LEN, WG_PEER_LEN};
|
||||
//! use rosenpass_wireguard_broker::brokers::netlink::NetlinkWireGuardBroker;
|
||||
//!
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let mut broker = NetlinkWireGuardBroker::new()?;
|
||||
//!
|
||||
//! let config = SerializedBrokerConfig {
|
||||
//! interface: "wg0".as_bytes(),
|
||||
//! peer_id: &Public::zero(), // Replace with actual peer ID
|
||||
//! psk: &Secret::zero(), // Replace with actual PSK
|
||||
//! additional_params: &[],
|
||||
//! };
|
||||
//!
|
||||
//! broker.set_psk(config)?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -8,12 +35,14 @@ use crate::api::config::NetworkBrokerConfig;
|
||||
use crate::api::msgs;
|
||||
use crate::{SerializedBrokerConfig, WireGuardBroker};
|
||||
|
||||
/// Error that can occur when connecting to the WireGuard netlink interface.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ConnectError {
|
||||
#[error(transparent)]
|
||||
ConnectError(#[from] wg::err::ConnectError),
|
||||
}
|
||||
|
||||
/// Errors that can occur during netlink operations.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum NetlinkError {
|
||||
#[error(transparent)]
|
||||
@@ -22,6 +51,7 @@ pub enum NetlinkError {
|
||||
GetDevice(#[from] wg::err::GetDeviceError),
|
||||
}
|
||||
|
||||
/// Errors that can occur when setting a pre-shared key.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SetPskError {
|
||||
#[error("The indicated wireguard interface does not exist")]
|
||||
@@ -55,11 +85,33 @@ impl From<SetPskNetlinkError> for SetPskMsgsError {
|
||||
}
|
||||
}
|
||||
|
||||
/// WireGuard broker implementation using Linux netlink sockets.
|
||||
///
|
||||
/// This implementation communicates directly with the kernel through netlink sockets,
|
||||
/// providing better performance than command-line based implementations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use rosenpass_wireguard_broker::brokers::netlink::NetlinkWireGuardBroker;
|
||||
/// use rosenpass_wireguard_broker::WireGuardBroker;
|
||||
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut broker = NetlinkWireGuardBroker::new()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Platform Support
|
||||
///
|
||||
/// This implementation is only available on Linux systems and requires appropriate
|
||||
/// permissions to use netlink sockets.
|
||||
pub struct NetlinkWireGuardBroker {
|
||||
sock: wg::WgSocket,
|
||||
}
|
||||
|
||||
impl NetlinkWireGuardBroker {
|
||||
/// Opens a netlink socket to the WireGuard kernel module
|
||||
/// and returns a new netlink-based WireGuard broker.
|
||||
pub fn new() -> Result<Self, ConnectError> {
|
||||
let sock = wg::WgSocket::connect()?;
|
||||
Ok(Self { sock })
|
||||
|
||||
Reference in New Issue
Block a user