mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-05 20:40:02 -08:00
feat(rosenpass): Add wireguard-broker interface in AppServer (#303)
Dynamically dispatch WireguardBrokerMio trait in AppServer. Also allows for mio event registration and poll processing, logic from dev/broker-architecture branch Co-authored-by: Prabhpreet Dua <615318+prabhpreet@users.noreply.github.com> Co-authored-by: Karolin Varner <karo@cupdev.net>
This commit is contained in:
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -427,6 +427,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.1"
|
||||
@@ -729,6 +735,12 @@ version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.2"
|
||||
@@ -954,6 +966,7 @@ dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
@@ -1515,6 +1528,18 @@ dependencies = [
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@@ -1667,6 +1692,7 @@ dependencies = [
|
||||
"rosenpass-secret-memory",
|
||||
"rosenpass-to",
|
||||
"rosenpass-util",
|
||||
"rosenpass-wireguard-broker",
|
||||
"serde",
|
||||
"serial_test",
|
||||
"stacker",
|
||||
@@ -1772,10 +1798,13 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.4",
|
||||
"derive_builder 0.20.0",
|
||||
"env_logger",
|
||||
"log",
|
||||
"mio",
|
||||
"postcard",
|
||||
"rand",
|
||||
"rosenpass-secret-memory",
|
||||
"rosenpass-to",
|
||||
"rosenpass-util",
|
||||
"thiserror",
|
||||
@@ -1802,6 +1831,7 @@ dependencies = [
|
||||
"rosenpass-ciphers",
|
||||
"rosenpass-secret-memory",
|
||||
"rosenpass-util",
|
||||
"rosenpass-wireguard-broker",
|
||||
"rtnetlink",
|
||||
"stacker",
|
||||
"tempfile",
|
||||
|
||||
@@ -34,6 +34,7 @@ rosenpass-ciphers = { path = "ciphers" }
|
||||
rosenpass-to = { path = "to" }
|
||||
rosenpass-secret-memory = { path = "secret-memory" }
|
||||
rosenpass-oqs = { path = "oqs" }
|
||||
rosenpass-wireguard-broker = { path = "wireguard-broker" }
|
||||
doc-comment = "0.3.3"
|
||||
base64ct = {version = "1.6.0", default-features=false}
|
||||
zeroize = "1.7.0"
|
||||
@@ -60,6 +61,7 @@ zerocopy = { version = "0.7.34", features = ["derive"] }
|
||||
home = "0.5.9"
|
||||
derive_builder = "0.20.0"
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||
postcard= {version = "1.0.8", features = ["alloc"]}
|
||||
|
||||
#Dev dependencies
|
||||
serial_test = "3.1.1"
|
||||
|
||||
@@ -39,6 +39,7 @@ rand = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
home = { workspace = true }
|
||||
derive_builder = {workspace = true}
|
||||
rosenpass-wireguard-broker = {workspace = true}
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
@@ -48,3 +49,6 @@ criterion = { workspace = true }
|
||||
test_bin = { workspace = true }
|
||||
stacker = { workspace = true }
|
||||
serial_test = {workspace = true}
|
||||
|
||||
[features]
|
||||
enable_broker_api = ["rosenpass-wireguard-broker/enable_broker_api"]
|
||||
@@ -2,13 +2,20 @@ use anyhow::bail;
|
||||
|
||||
use anyhow::Result;
|
||||
use derive_builder::Builder;
|
||||
use log::{debug, error, info, warn};
|
||||
use log::{error, info, warn};
|
||||
use mio::Interest;
|
||||
use mio::Token;
|
||||
use rosenpass_util::file::{StoreValueB64, StoreValueB64Writer};
|
||||
use rosenpass_secret_memory::Public;
|
||||
use rosenpass_secret_memory::Secret;
|
||||
use rosenpass_util::file::StoreValueB64;
|
||||
use rosenpass_wireguard_broker::WireguardBrokerMio;
|
||||
use rosenpass_wireguard_broker::{WireguardBrokerCfg, WG_KEY_LEN};
|
||||
use zerocopy::AsBytes;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::io::ErrorKind;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::Ipv6Addr;
|
||||
@@ -17,10 +24,7 @@ use std::net::SocketAddrV4;
|
||||
use std::net::SocketAddrV6;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::slice;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -41,6 +45,8 @@ const IPV6_ANY_ADDR: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
const UNDER_LOAD_RATIO: f64 = 0.5;
|
||||
const DURATION_UPDATE_UNDER_LOAD_STATUS: Duration = Duration::from_millis(500);
|
||||
|
||||
const BROKER_ID_BYTES: usize = 8;
|
||||
|
||||
fn ipv4_any_binding() -> SocketAddr {
|
||||
// addr, port
|
||||
SocketAddr::V4(SocketAddrV4::new(IPV4_ANY_ADDR, 0))
|
||||
@@ -51,10 +57,50 @@ fn ipv6_any_binding() -> SocketAddr {
|
||||
SocketAddr::V6(SocketAddrV6::new(IPV6_ANY_ADDR, 0, 0, 0))
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MioTokenDispenser {
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl MioTokenDispenser {
|
||||
fn dispense(&mut self) -> Token {
|
||||
let r = self.counter;
|
||||
self.counter += 1;
|
||||
Token(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BrokerStore {
|
||||
store: HashMap<
|
||||
Public<BROKER_ID_BYTES>,
|
||||
Box<dyn WireguardBrokerMio<Error = anyhow::Error, MioError = anyhow::Error>>,
|
||||
>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BrokerStorePtr(pub Public<BROKER_ID_BYTES>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BrokerPeer {
|
||||
ptr: BrokerStorePtr,
|
||||
peer_cfg: Box<dyn WireguardBrokerCfg>,
|
||||
}
|
||||
|
||||
impl BrokerPeer {
|
||||
pub fn new(ptr: BrokerStorePtr, peer_cfg: Box<dyn WireguardBrokerCfg>) -> Self {
|
||||
Self { ptr, peer_cfg }
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> &BrokerStorePtr {
|
||||
&self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct AppPeer {
|
||||
pub outfile: Option<PathBuf>,
|
||||
pub outwg: Option<WireguardOut>, // TODO make this a generic command
|
||||
pub broker_peer: Option<BrokerPeer>,
|
||||
pub initial_endpoint: Option<Endpoint>,
|
||||
pub current_endpoint: Option<Endpoint>,
|
||||
}
|
||||
@@ -102,6 +148,8 @@ pub struct AppServer {
|
||||
pub sockets: Vec<mio::net::UdpSocket>,
|
||||
pub events: mio::Events,
|
||||
pub mio_poll: mio::Poll,
|
||||
pub mio_token_dispenser: MioTokenDispenser,
|
||||
pub brokers: BrokerStore,
|
||||
pub peers: Vec<AppPeer>,
|
||||
pub verbosity: Verbosity,
|
||||
pub all_sockets_drained: bool,
|
||||
@@ -159,6 +207,17 @@ impl AppPeerPtr {
|
||||
pub fn get_app_mut<'a>(&self, srv: &'a mut AppServer) -> &'a mut AppPeer {
|
||||
&mut srv.peers[self.0]
|
||||
}
|
||||
|
||||
pub fn set_psk(&self, server: &mut AppServer, psk: &Secret<WG_KEY_LEN>) -> anyhow::Result<()> {
|
||||
if let Some(broker) = server.peers[self.0].broker_peer.as_ref() {
|
||||
let config = broker.peer_cfg.create_config(psk);
|
||||
let broker = server.brokers.store.get_mut(&broker.ptr().0).unwrap();
|
||||
broker.set_psk(config)?;
|
||||
} else {
|
||||
log::warn!("No broker peer found for peer {}", self.0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -457,6 +516,7 @@ impl AppServer {
|
||||
// setup mio
|
||||
let mio_poll = mio::Poll::new()?;
|
||||
let events = mio::Events::with_capacity(20);
|
||||
let mut mio_token_dispenser = MioTokenDispenser::default();
|
||||
|
||||
// bind each SocketAddr to a socket
|
||||
let maybe_sockets: Result<Vec<_>, _> =
|
||||
@@ -530,10 +590,12 @@ impl AppServer {
|
||||
}
|
||||
|
||||
// register all sockets to mio
|
||||
for (i, socket) in sockets.iter_mut().enumerate() {
|
||||
mio_poll
|
||||
.registry()
|
||||
.register(socket, Token(i), Interest::READABLE)?;
|
||||
for socket in sockets.iter_mut() {
|
||||
mio_poll.registry().register(
|
||||
socket,
|
||||
mio_token_dispenser.dispense(),
|
||||
Interest::READABLE,
|
||||
)?;
|
||||
}
|
||||
|
||||
// TODO use mio::net::UnixStream together with std::os::unix::net::UnixStream for Linux
|
||||
@@ -545,6 +607,8 @@ impl AppServer {
|
||||
sockets,
|
||||
events,
|
||||
mio_poll,
|
||||
mio_token_dispenser,
|
||||
brokers: BrokerStore::default(),
|
||||
all_sockets_drained: false,
|
||||
under_load: DoSOperation::Normal,
|
||||
blocking_polls_count: 0,
|
||||
@@ -559,12 +623,50 @@ impl AppServer {
|
||||
matches!(self.verbosity, Verbosity::Verbose)
|
||||
}
|
||||
|
||||
pub fn register_broker(
|
||||
&mut self,
|
||||
broker: Box<dyn WireguardBrokerMio<Error = anyhow::Error, MioError = anyhow::Error>>,
|
||||
) -> Result<BrokerStorePtr> {
|
||||
let ptr = Public::from_slice((self.brokers.store.len() as u64).as_bytes());
|
||||
|
||||
if self.brokers.store.insert(ptr, broker).is_some() {
|
||||
bail!("Broker already registered");
|
||||
}
|
||||
//Register broker
|
||||
self.brokers
|
||||
.store
|
||||
.get_mut(&ptr)
|
||||
.ok_or(anyhow::format_err!("Broker wasn't added to registry"))?
|
||||
.register(
|
||||
self.mio_poll.registry(),
|
||||
self.mio_token_dispenser.dispense(),
|
||||
)?;
|
||||
|
||||
Ok(BrokerStorePtr(ptr))
|
||||
}
|
||||
|
||||
pub fn unregister_broker(&mut self, ptr: BrokerStorePtr) -> Result<()> {
|
||||
//Unregister broker
|
||||
self.brokers
|
||||
.store
|
||||
.get_mut(&ptr.0)
|
||||
.ok_or_else(|| anyhow::anyhow!("Broker not found"))?
|
||||
.unregister(self.mio_poll.registry())?;
|
||||
|
||||
//Remove broker from store
|
||||
self.brokers
|
||||
.store
|
||||
.remove(&ptr.0)
|
||||
.ok_or_else(|| anyhow::anyhow!("Broker not found"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_peer(
|
||||
&mut self,
|
||||
psk: Option<SymKey>,
|
||||
pk: SPk,
|
||||
outfile: Option<PathBuf>,
|
||||
outwg: Option<WireguardOut>,
|
||||
broker_peer: Option<BrokerPeer>,
|
||||
hostname: Option<String>,
|
||||
) -> anyhow::Result<AppPeerPtr> {
|
||||
let PeerPtr(pn) = self.crypt.add_peer(psk, pk)?;
|
||||
@@ -575,7 +677,7 @@ impl AppServer {
|
||||
let current_endpoint = None;
|
||||
self.peers.push(AppPeer {
|
||||
outfile,
|
||||
outwg,
|
||||
broker_peer,
|
||||
initial_endpoint,
|
||||
current_endpoint,
|
||||
});
|
||||
@@ -735,13 +837,12 @@ impl AppServer {
|
||||
}
|
||||
|
||||
pub fn output_key(
|
||||
&self,
|
||||
&mut self,
|
||||
peer: AppPeerPtr,
|
||||
why: KeyOutputReason,
|
||||
key: &SymKey,
|
||||
) -> anyhow::Result<()> {
|
||||
let peerid = peer.lower().get(&self.crypt).pidt()?;
|
||||
let ap = peer.get_app(self);
|
||||
|
||||
if self.verbose() {
|
||||
let msg = match why {
|
||||
@@ -751,6 +852,8 @@ impl AppServer {
|
||||
info!("{} {}", msg, peerid.fmt_b64::<MAX_B64_PEER_ID_SIZE>());
|
||||
}
|
||||
|
||||
let ap = peer.get_app(self);
|
||||
|
||||
if let Some(of) = ap.outfile.as_ref() {
|
||||
// This might leave some fragments of the secret on the stack;
|
||||
// in practice this is likely not a problem because the stack likely
|
||||
@@ -773,46 +876,7 @@ impl AppServer {
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(owg) = ap.outwg.as_ref() {
|
||||
let mut child = match Command::new("wg")
|
||||
.arg("set")
|
||||
.arg(&owg.dev)
|
||||
.arg("peer")
|
||||
.arg(&owg.pk)
|
||||
.arg("preshared-key")
|
||||
.arg("/dev/stdin")
|
||||
.stdin(Stdio::piped())
|
||||
.args(&owg.extra_params)
|
||||
.spawn()
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
anyhow::bail!("Could not find wg command");
|
||||
} else {
|
||||
return Err(anyhow::Error::new(e));
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Err(e) = key.store_b64_writer::<MAX_B64_KEY_SIZE, _>(child.stdin.take().unwrap())
|
||||
{
|
||||
error!("could not write psk to wg: {:?}", e);
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
let status = child.wait();
|
||||
|
||||
if let Ok(status) = status {
|
||||
if status.success() {
|
||||
debug!("successfully passed psk to wg")
|
||||
} else {
|
||||
error!("could not pass psk to wg {:?}", status)
|
||||
}
|
||||
} else {
|
||||
error!("wait failed: {:?}", status)
|
||||
}
|
||||
});
|
||||
}
|
||||
peer.set_psk(self, key)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -944,6 +1008,11 @@ impl AppServer {
|
||||
// if each socket returned WouldBlock, then we drained them all at least once indeed
|
||||
self.all_sockets_drained = would_block_count == self.sockets.len();
|
||||
|
||||
// Process brokers poll
|
||||
for (_, broker) in self.brokers.store.iter_mut() {
|
||||
broker.process_poll()?;
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::StaticKem;
|
||||
use rosenpass_secret_memory::file::StoreSecret;
|
||||
use rosenpass_util::file::{LoadValue, LoadValueB64};
|
||||
use rosenpass_wireguard_broker::brokers::native_unix::{
|
||||
NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::app_server::AppServer;
|
||||
use crate::app_server::{self, AppServerTest};
|
||||
use crate::app_server::AppServerTest;
|
||||
use crate::app_server::{AppServer, BrokerPeer};
|
||||
use crate::protocol::{SPk, SSk, SymKey};
|
||||
|
||||
use super::config;
|
||||
@@ -314,7 +317,28 @@ impl CliCommand {
|
||||
test_helpers,
|
||||
)?);
|
||||
|
||||
let broker_store_ptr = srv.register_broker(Box::new(NativeUnixBroker::new()))?;
|
||||
|
||||
fn cfg_err_map(e: NativeUnixBrokerConfigBaseBuilderError) -> anyhow::Error {
|
||||
anyhow::Error::msg(format!("NativeUnixBrokerConfigBaseBuilderError: {:?}", e))
|
||||
}
|
||||
|
||||
for cfg_peer in config.peers {
|
||||
let broker_peer = if let Some(wg) = &cfg_peer.wg {
|
||||
let peer_cfg = NativeUnixBrokerConfigBaseBuilder::default()
|
||||
.peer_id_b64(&wg.peer)?
|
||||
.interface(wg.device.clone())
|
||||
.extra_params_ser(&wg.extra_params)?
|
||||
.build()
|
||||
.map_err(cfg_err_map)?;
|
||||
|
||||
let broker_peer = BrokerPeer::new(broker_store_ptr.clone(), Box::new(peer_cfg));
|
||||
|
||||
Some(broker_peer)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
srv.add_peer(
|
||||
// psk, pk, outfile, outwg, tx_addr
|
||||
cfg_peer
|
||||
@@ -323,11 +347,7 @@ impl CliCommand {
|
||||
.transpose()?,
|
||||
SPk::load(&cfg_peer.public_key)?,
|
||||
cfg_peer.key_out,
|
||||
cfg_peer.wg.map(|cfg| app_server::WireguardOut {
|
||||
dev: cfg.device,
|
||||
pk: cfg.peer,
|
||||
extra_params: cfg.extra_params,
|
||||
}),
|
||||
broker_peer,
|
||||
cfg_peer.endpoint.clone(),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
// add path to "self"
|
||||
config.config_file_path = p.as_ref().to_owned();
|
||||
p.as_ref().clone_into(&mut config.config_file_path);
|
||||
|
||||
// return
|
||||
Ok(config)
|
||||
|
||||
@@ -2295,7 +2295,7 @@ mod test {
|
||||
.secret(),
|
||||
)
|
||||
.unwrap()
|
||||
.mix(&ip_addr_port_a.encode())
|
||||
.mix(ip_addr_port_a.encode())
|
||||
.unwrap()
|
||||
.into_value()[..16]
|
||||
.to_vec();
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
use std::{fs, net::UdpSocket, path::PathBuf, time::Duration};
|
||||
use std::{
|
||||
fs,
|
||||
net::UdpSocket,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use clap::{builder::Str, Parser};
|
||||
use rosenpass::{app_server::AppServerTestBuilder, cli::CliArgs};
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
use rosenpass_wireguard_broker::{WireguardBrokerMio, WG_KEY_LEN, WG_PEER_LEN};
|
||||
use serial_test::serial;
|
||||
use std::io::Write;
|
||||
|
||||
@@ -34,12 +42,7 @@ fn generate_keys() {
|
||||
}
|
||||
|
||||
fn find_udp_socket() -> Option<u16> {
|
||||
for port in 1025..=u16::MAX {
|
||||
if UdpSocket::bind(("::1", port)).is_ok() {
|
||||
return Some(port);
|
||||
}
|
||||
}
|
||||
None
|
||||
(1025..=u16::MAX).find(|&port| UdpSocket::bind(("::1", port)).is_ok())
|
||||
}
|
||||
|
||||
fn setup_logging() {
|
||||
@@ -271,3 +274,57 @@ fn check_exchange_under_dos() {
|
||||
// cleanup
|
||||
fs::remove_dir_all(&tmpdir).unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct MockBrokerInner {
|
||||
psk: Option<Secret<WG_KEY_LEN>>,
|
||||
peer_id: Option<Public<WG_PEER_LEN>>,
|
||||
interface: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct MockBroker {
|
||||
inner: Arc<Mutex<MockBrokerInner>>,
|
||||
}
|
||||
|
||||
impl WireguardBrokerMio for MockBroker {
|
||||
type MioError = anyhow::Error;
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
_registry: &mio::Registry,
|
||||
_token: mio::Token,
|
||||
) -> Result<(), Self::MioError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_poll(&mut self) -> Result<(), Self::MioError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, _registry: &mio::Registry) -> Result<(), Self::MioError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl rosenpass_wireguard_broker::WireGuardBroker for MockBroker {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
config: rosenpass_wireguard_broker::SerializedBrokerConfig<'_>,
|
||||
) -> Result<(), Self::Error> {
|
||||
loop {
|
||||
let mut lock = self.inner.try_lock();
|
||||
|
||||
if let Ok(ref mut mutex) = lock {
|
||||
**mutex = MockBrokerInner {
|
||||
psk: Some(config.psk.clone()),
|
||||
peer_id: Some(config.peer_id.clone()),
|
||||
interface: Some(std::str::from_utf8(config.interface).unwrap().to_string()),
|
||||
};
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ rosenpass-ciphers = { workspace = true }
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
rosenpass-wireguard-broker = {workspace = true}
|
||||
|
||||
tokio = {workspace = true}
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ mod tests {
|
||||
|
||||
#[inline]
|
||||
fn parse(arr: &[&str]) -> Result<Cli, String> {
|
||||
Cli::parse(arr.into_iter().map(|x| x.to_string()).peekable())
|
||||
Cli::parse(arr.iter().map(|x| x.to_string()).peekable())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -300,7 +300,7 @@ mod tests {
|
||||
assert!(cli.is_ok());
|
||||
let cli = cli.unwrap();
|
||||
|
||||
assert_eq!(cli.verbose, false);
|
||||
assert!(!cli.verbose);
|
||||
assert!(matches!(cli.command, Some(Command::GenKey { .. })));
|
||||
|
||||
match cli.command {
|
||||
@@ -324,7 +324,7 @@ mod tests {
|
||||
assert!(cli.is_ok());
|
||||
let cli = cli.unwrap();
|
||||
|
||||
assert_eq!(cli.verbose, false);
|
||||
assert!(!cli.verbose);
|
||||
assert!(matches!(cli.command, Some(Command::PubKey { .. })));
|
||||
|
||||
match cli.command {
|
||||
@@ -365,7 +365,7 @@ mod tests {
|
||||
assert!(cli.is_ok());
|
||||
let cli = cli.unwrap();
|
||||
|
||||
assert_eq!(cli.verbose, false);
|
||||
assert!(!cli.verbose);
|
||||
assert!(matches!(cli.command, Some(Command::Exchange(_))));
|
||||
|
||||
match cli.command {
|
||||
@@ -391,7 +391,7 @@ mod tests {
|
||||
assert!(cli.is_ok());
|
||||
let cli = cli.unwrap();
|
||||
|
||||
assert_eq!(cli.verbose, false);
|
||||
assert!(!cli.verbose);
|
||||
assert!(matches!(cli.command, Some(Command::Exchange(_))));
|
||||
|
||||
match cli.command {
|
||||
@@ -431,7 +431,7 @@ mod tests {
|
||||
assert!(cli.is_ok());
|
||||
let cli = cli.unwrap();
|
||||
|
||||
assert_eq!(cli.verbose, false);
|
||||
assert!(!cli.verbose);
|
||||
assert!(matches!(cli.command, Some(Command::Exchange(_))));
|
||||
|
||||
match cli.command {
|
||||
|
||||
@@ -136,12 +136,15 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> {
|
||||
use anyhow::anyhow;
|
||||
use netlink_packet_wireguard::{constants::WG_KEY_LEN, nlas::WgDeviceAttrs};
|
||||
use rosenpass::{
|
||||
app_server::{AppServer, WireguardOut},
|
||||
app_server::{AppServer, BrokerPeer},
|
||||
config::Verbosity,
|
||||
protocol::{SPk, SSk, SymKey},
|
||||
};
|
||||
use rosenpass_secret_memory::Secret;
|
||||
use rosenpass_util::file::{LoadValue as _, LoadValueB64};
|
||||
use rosenpass_wireguard_broker::brokers::native_unix::{
|
||||
NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError,
|
||||
};
|
||||
|
||||
let (connection, rtnetlink, _) = rtnetlink::new_connection()?;
|
||||
tokio::spawn(connection);
|
||||
@@ -198,6 +201,12 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> {
|
||||
None,
|
||||
)?);
|
||||
|
||||
let broker_store_ptr = srv.register_broker(Box::new(NativeUnixBroker::new()))?;
|
||||
|
||||
fn cfg_err_map(e: NativeUnixBrokerConfigBaseBuilderError) -> anyhow::Error {
|
||||
anyhow::Error::msg(format!("NativeUnixBrokerConfigBaseBuilderError: {:?}", e))
|
||||
}
|
||||
|
||||
for peer in options.peers {
|
||||
let wgpk = peer.public_keys_dir.join("wgpk");
|
||||
let pqpk = peer.public_keys_dir.join("pqpk");
|
||||
@@ -220,6 +229,18 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> {
|
||||
extra_params.push(allowed_ips.clone());
|
||||
}
|
||||
|
||||
let peer_cfg = NativeUnixBrokerConfigBaseBuilder::default()
|
||||
.peer_id_b64(&fs::read_to_string(wgpk)?)?
|
||||
.interface(link_name.clone())
|
||||
.extra_params_ser(&extra_params)?
|
||||
.build()
|
||||
.map_err(cfg_err_map)?;
|
||||
|
||||
let broker_peer = Some(BrokerPeer::new(
|
||||
broker_store_ptr.clone(),
|
||||
Box::new(peer_cfg),
|
||||
));
|
||||
|
||||
srv.add_peer(
|
||||
if psk.exists() {
|
||||
Some(SymKey::load_b64::<WG_B64_LEN, _>(psk))
|
||||
@@ -229,11 +250,7 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> {
|
||||
.transpose()?,
|
||||
SPk::load(&pqpk)?,
|
||||
None,
|
||||
Some(WireguardOut {
|
||||
dev: link_name.clone(),
|
||||
pk: fs::read_to_string(wgpk)?,
|
||||
extra_params,
|
||||
}),
|
||||
broker_peer,
|
||||
peer.endpoint.map(|x| x.to_string()),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -162,12 +162,12 @@ impl<const N: usize> StoreValueB64Writer for Public<N> {
|
||||
mut writer: W,
|
||||
) -> Result<(), Self::Error> {
|
||||
let mut f = [0u8; F];
|
||||
let encoded_str = b64_encode(&self.value, &mut f)
|
||||
.with_context(|| format!("Could not encode secret to base64"))?;
|
||||
let encoded_str =
|
||||
b64_encode(&self.value, &mut f).with_context(|| "Could not encode secret to base64")?;
|
||||
|
||||
writer
|
||||
.write_all(encoded_str.as_bytes())
|
||||
.with_context(|| format!("Could not write base64 to writer"))?;
|
||||
.with_context(|| "Could not write base64 to writer")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,11 +295,11 @@ impl<const N: usize> StoreValueB64Writer for Secret<N> {
|
||||
fn store_b64_writer<const F: usize, W: Write>(&self, mut writer: W) -> anyhow::Result<()> {
|
||||
let mut f: Secret<F> = Secret::random();
|
||||
let encoded_str = b64_encode(self.secret(), f.secret_mut())
|
||||
.with_context(|| format!("Could not encode secret to base64"))?;
|
||||
.with_context(|| "Could not encode secret to base64")?;
|
||||
|
||||
writer
|
||||
.write_all(encoded_str.as_bytes())
|
||||
.with_context(|| format!("Could not write base64 to writer"))?;
|
||||
.with_context(|| "Could not write base64 to writer")?;
|
||||
f.zeroize();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct B64DisplayHelper<'a, const F: usize>(&'a [u8]);
|
||||
impl<const F: usize> Display for B64DisplayHelper<'_, F> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut bytes = [0u8; F];
|
||||
let string = b64_encode(&self.0, &mut bytes).map_err(|_| std::fmt::Error)?;
|
||||
let string = b64_encode(self.0, &mut bytes).map_err(|_| std::fmt::Error)?;
|
||||
let result = f.write_str(string);
|
||||
bytes.zeroize();
|
||||
result
|
||||
@@ -16,17 +16,17 @@ impl<const F: usize> Display for B64DisplayHelper<'_, F> {
|
||||
}
|
||||
|
||||
pub trait B64Display {
|
||||
fn fmt_b64<'o, const F: usize>(&'o self) -> B64DisplayHelper<'o, F>;
|
||||
fn fmt_b64<const F: usize>(&self) -> B64DisplayHelper<F>;
|
||||
}
|
||||
|
||||
impl B64Display for [u8] {
|
||||
fn fmt_b64<'o, const F: usize>(&'o self) -> B64DisplayHelper<'o, F> {
|
||||
fn fmt_b64<const F: usize>(&self) -> B64DisplayHelper<F> {
|
||||
B64DisplayHelper(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> B64Display for T {
|
||||
fn fmt_b64<'o, const F: usize>(&'o self) -> B64DisplayHelper<'o, F> {
|
||||
fn fmt_b64<const F: usize>(&self) -> B64DisplayHelper<F> {
|
||||
B64DisplayHelper(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ readme = "readme.md"
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
rosenpass-secret-memory = {workspace = true}
|
||||
|
||||
# Privileged only
|
||||
wireguard-uapi = { workspace = true }
|
||||
@@ -23,6 +24,8 @@ anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
log = { workspace = true }
|
||||
derive_builder = {workspace = true}
|
||||
postcard = {workspace = true}
|
||||
|
||||
# Mio broker client
|
||||
mio = { workspace = true }
|
||||
@@ -32,18 +35,18 @@ rosenpass-util = { workspace = true }
|
||||
rand = {workspace = true}
|
||||
|
||||
[features]
|
||||
enable_broker=[]
|
||||
enable_broker_api=[]
|
||||
|
||||
[[bin]]
|
||||
name = "rosenpass-wireguard-broker-privileged"
|
||||
path = "src/bin/priviledged.rs"
|
||||
test = false
|
||||
doc = false
|
||||
required-features=["enable_broker"]
|
||||
required-features=["enable_broker_api"]
|
||||
|
||||
[[bin]]
|
||||
name = "rosenpass-wireguard-broker-socket-handler"
|
||||
test = false
|
||||
path = "src/bin/socket_handler.rs"
|
||||
doc = false
|
||||
required-features=["enable_broker"]
|
||||
required-features=["enable_broker_api"]
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use std::{borrow::BorrowMut, fmt::Debug};
|
||||
|
||||
use crate::{
|
||||
api::msgs::{self, REQUEST_MSG_BUFFER_SIZE},
|
||||
WireGuardBroker,
|
||||
api::{
|
||||
config::NetworkBrokerConfig,
|
||||
msgs::{self, REQUEST_MSG_BUFFER_SIZE},
|
||||
},
|
||||
SerializedBrokerConfig, WireGuardBroker,
|
||||
};
|
||||
|
||||
use super::msgs::{Envelope, SetPskResponse};
|
||||
use super::{
|
||||
config::NetworkBrokerConfigErr,
|
||||
msgs::{Envelope, SetPskResponse},
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BrokerClientPollResponseError<RecvError> {
|
||||
@@ -34,6 +40,8 @@ fn invalid_msg_poller<RecvError>() -> BrokerClientPollResponseError<RecvError> {
|
||||
pub enum BrokerClientSetPskError<SendError> {
|
||||
#[error("Error with encoding/decoding message")]
|
||||
MsgError,
|
||||
#[error("Network Broker Config error: {0}")]
|
||||
BrokerError(NetworkBrokerConfigErr),
|
||||
#[error(transparent)]
|
||||
IoError(SendError),
|
||||
#[error("Interface name out of bounds")]
|
||||
@@ -51,14 +59,14 @@ pub trait BrokerClientIo {
|
||||
#[derive(Debug)]
|
||||
pub struct BrokerClient<Io>
|
||||
where
|
||||
Io: BrokerClientIo,
|
||||
Io: BrokerClientIo + Debug,
|
||||
{
|
||||
io: Io,
|
||||
}
|
||||
|
||||
impl<Io> BrokerClient<Io>
|
||||
where
|
||||
Io: BrokerClientIo,
|
||||
Io: BrokerClientIo + Debug,
|
||||
{
|
||||
pub fn new(io: Io) -> Self {
|
||||
Self { io }
|
||||
@@ -99,16 +107,14 @@ where
|
||||
|
||||
impl<Io> WireGuardBroker for BrokerClient<Io>
|
||||
where
|
||||
Io: BrokerClientIo,
|
||||
Io: BrokerClientIo + Debug,
|
||||
{
|
||||
type Error = BrokerClientSetPskError<Io::SendError>;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
iface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error> {
|
||||
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
||||
let config: Result<NetworkBrokerConfig, NetworkBrokerConfigErr> = config.try_into();
|
||||
let config = config.map_err(|e| BrokerClientSetPskError::BrokerError(e))?;
|
||||
|
||||
use BrokerClientSetPskError::*;
|
||||
const BUF_SIZE: usize = REQUEST_MSG_BUFFER_SIZE;
|
||||
|
||||
@@ -126,9 +132,10 @@ where
|
||||
let req = &mut req.payload;
|
||||
|
||||
// Populate payload
|
||||
req.peer_id.copy_from_slice(&peer_id);
|
||||
req.psk.copy_from_slice(&psk);
|
||||
req.set_iface(iface).ok_or(IfaceOutOfBounds)?;
|
||||
req.peer_id.copy_from_slice(&config.peer_id.value);
|
||||
req.psk.copy_from_slice(config.psk.secret());
|
||||
req.set_iface(config.iface.as_ref())
|
||||
.ok_or(IfaceOutOfBounds)?;
|
||||
}
|
||||
|
||||
// Send message
|
||||
|
||||
43
wireguard-broker/src/api/config.rs
Normal file
43
wireguard-broker/src/api/config.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use crate::{SerializedBrokerConfig, WG_KEY_LEN, WG_PEER_LEN};
|
||||
use derive_builder::Builder;
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
|
||||
#[derive(Builder)]
|
||||
#[builder(pattern = "mutable")]
|
||||
//TODO: Use generics for iface, add additional params
|
||||
pub struct NetworkBrokerConfig<'a> {
|
||||
pub iface: &'a str,
|
||||
pub peer_id: &'a Public<WG_PEER_LEN>,
|
||||
pub psk: &'a Secret<WG_KEY_LEN>,
|
||||
}
|
||||
|
||||
impl<'a> Into<SerializedBrokerConfig<'a>> for NetworkBrokerConfig<'a> {
|
||||
fn into(self) -> SerializedBrokerConfig<'a> {
|
||||
SerializedBrokerConfig {
|
||||
interface: self.iface.as_bytes(),
|
||||
peer_id: self.peer_id,
|
||||
psk: self.psk,
|
||||
additional_params: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum NetworkBrokerConfigErr {
|
||||
#[error("Interface")]
|
||||
Interface,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<SerializedBrokerConfig<'a>> for NetworkBrokerConfig<'a> {
|
||||
type Error = NetworkBrokerConfigErr;
|
||||
|
||||
fn try_from(value: SerializedBrokerConfig<'a>) -> Result<Self, Self::Error> {
|
||||
let iface =
|
||||
std::str::from_utf8(value.interface).map_err(|_| NetworkBrokerConfigErr::Interface)?;
|
||||
Ok(Self {
|
||||
iface,
|
||||
peer_id: value.peer_id,
|
||||
psk: value.psk,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod client;
|
||||
pub mod mio_client;
|
||||
pub mod config;
|
||||
pub mod msgs;
|
||||
pub mod server;
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use std::result::Result;
|
||||
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
|
||||
use crate::api::msgs::{self, Envelope, SetPskRequest, SetPskResponse};
|
||||
use crate::WireGuardBroker;
|
||||
|
||||
use super::config::{NetworkBrokerConfigBuilder, NetworkBrokerConfigErr};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BrokerServerError {
|
||||
#[error("No such request type: {}", .0)]
|
||||
NoSuchRequestType(u8),
|
||||
#[error("Invalid message received.")]
|
||||
InvalidMessage,
|
||||
#[error("Network Broker Config error: {0}")]
|
||||
BrokerError(NetworkBrokerConfigErr),
|
||||
}
|
||||
|
||||
impl From<msgs::InvalidMessageTypeError> for BrokerServerError {
|
||||
@@ -21,16 +27,16 @@ impl From<msgs::InvalidMessageTypeError> for BrokerServerError {
|
||||
|
||||
pub struct BrokerServer<Err, Inner>
|
||||
where
|
||||
msgs::SetPskError: From<Err>,
|
||||
Inner: WireGuardBroker<Error = Err>,
|
||||
msgs::SetPskError: From<Err>,
|
||||
{
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
impl<Err, Inner> BrokerServer<Err, Inner>
|
||||
where
|
||||
msgs::SetPskError: From<Err>,
|
||||
Inner: WireGuardBroker<Error = Err>,
|
||||
msgs::SetPskError: From<Err>,
|
||||
{
|
||||
pub fn new(inner: Inner) -> Self {
|
||||
Self { inner }
|
||||
@@ -64,12 +70,20 @@ where
|
||||
) -> Result<(), BrokerServerError> {
|
||||
// Using unwrap here since lenses can not return fixed-size arrays
|
||||
// TODO: Slices should give access to fixed size arrays
|
||||
let r: Result<(), Err> = self.inner.borrow_mut().set_psk(
|
||||
req.iface()
|
||||
.map_err(|_e| BrokerServerError::InvalidMessage)?,
|
||||
req.peer_id.try_into().unwrap(),
|
||||
req.psk.try_into().unwrap(),
|
||||
);
|
||||
let peer_id = Public::from_slice(&req.peer_id);
|
||||
let psk = Secret::from_slice(&req.psk);
|
||||
|
||||
let interface = req
|
||||
.iface()
|
||||
.map_err(|_e| BrokerServerError::InvalidMessage)?;
|
||||
|
||||
let config = NetworkBrokerConfigBuilder::default()
|
||||
.peer_id(&peer_id)
|
||||
.psk(&psk)
|
||||
.iface(interface)
|
||||
.build()
|
||||
.unwrap();
|
||||
let r: Result<(), Err> = self.inner.borrow_mut().set_psk(config.into());
|
||||
let r: msgs::SetPskResult = r.map_err(|e| e.into());
|
||||
let r: msgs::SetPskResponseReturnCode = r.into();
|
||||
res.return_code = r as u8;
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::result::Result;
|
||||
|
||||
use rosenpass_wireguard_broker::api::msgs;
|
||||
use rosenpass_wireguard_broker::api::server::BrokerServer;
|
||||
use rosenpass_wireguard_broker::netlink as wg;
|
||||
use rosenpass_wireguard_broker::brokers::netlink as wg;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum BrokerAppError {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use anyhow::{bail, ensure};
|
||||
use mio::Interest;
|
||||
use std::collections::VecDeque;
|
||||
use std::io::{ErrorKind, Read, Write};
|
||||
|
||||
use anyhow::{bail, ensure};
|
||||
use crate::{SerializedBrokerConfig, WireGuardBroker, WireguardBrokerMio};
|
||||
|
||||
use crate::WireGuardBroker;
|
||||
|
||||
use super::client::{
|
||||
use crate::api::client::{
|
||||
BrokerClient, BrokerClientIo, BrokerClientPollResponseError, BrokerClientSetPskError,
|
||||
};
|
||||
use super::msgs::{self, RESPONSE_MSG_BUFFER_SIZE};
|
||||
use crate::api::msgs::{self, RESPONSE_MSG_BUFFER_SIZE};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MioBrokerClient {
|
||||
@@ -47,7 +47,7 @@ impl MioBrokerClient {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn poll(&mut self) -> anyhow::Result<Option<msgs::SetPskResult>> {
|
||||
fn poll(&mut self) -> anyhow::Result<Option<msgs::SetPskResult>> {
|
||||
self.inner.io_mut().flush()?;
|
||||
|
||||
// This sucks
|
||||
@@ -68,18 +68,46 @@ impl MioBrokerClient {
|
||||
impl WireGuardBroker for MioBrokerClient {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn set_psk(&mut self, iface: &str, peer_id: [u8; 32], psk: [u8; 32]) -> anyhow::Result<()> {
|
||||
fn set_psk<'a>(&mut self, config: SerializedBrokerConfig<'a>) -> anyhow::Result<()> {
|
||||
use BrokerClientSetPskError::*;
|
||||
let e = self.inner.set_psk(iface, peer_id, psk);
|
||||
let e = self.inner.set_psk(config);
|
||||
match e {
|
||||
Ok(()) => Ok(()),
|
||||
Err(IoError(e)) => Err(e),
|
||||
Err(IfaceOutOfBounds) => bail!("Interface name size is out of bounds."),
|
||||
Err(MsgError) => bail!("Error with encoding/decoding message."),
|
||||
Err(BrokerError(e)) => bail!("Broker error: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WireguardBrokerMio for MioBrokerClient {
|
||||
type MioError = anyhow::Error;
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
registry: &mio::Registry,
|
||||
token: mio::Token,
|
||||
) -> Result<(), Self::MioError> {
|
||||
registry.register(
|
||||
&mut self.inner.io_mut().socket,
|
||||
token,
|
||||
Interest::READABLE | Interest::WRITABLE,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_poll(&mut self) -> Result<(), Self::MioError> {
|
||||
self.poll()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, registry: &mio::Registry) -> Result<(), Self::MioError> {
|
||||
registry.deregister(&mut self.inner.io_mut().socket)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl BrokerClientIo for MioBrokerClientIo {
|
||||
type SendError = anyhow::Error;
|
||||
type RecvError = anyhow::Error;
|
||||
6
wireguard-broker/src/brokers/mod.rs
Normal file
6
wireguard-broker/src/brokers/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#[cfg(feature = "enable_broker_api")]
|
||||
pub mod mio_client;
|
||||
#[cfg(feature = "enable_broker_api")]
|
||||
pub mod netlink;
|
||||
|
||||
pub mod native_unix;
|
||||
177
wireguard-broker/src/brokers/native_unix.rs
Normal file
177
wireguard-broker/src/brokers/native_unix.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use std::fmt::Debug;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::thread;
|
||||
|
||||
use derive_builder::Builder;
|
||||
use log::{debug, error};
|
||||
use postcard::{from_bytes, to_allocvec};
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
use rosenpass_util::b64::b64_decode;
|
||||
use rosenpass_util::{b64::B64Display, file::StoreValueB64Writer};
|
||||
|
||||
use crate::{SerializedBrokerConfig, WireGuardBroker, WireguardBrokerCfg, WireguardBrokerMio};
|
||||
use crate::{WG_KEY_LEN, WG_PEER_LEN};
|
||||
|
||||
const MAX_B64_KEY_SIZE: usize = WG_KEY_LEN * 5 / 3;
|
||||
const MAX_B64_PEER_ID_SIZE: usize = WG_PEER_LEN * 5 / 3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NativeUnixBroker {}
|
||||
|
||||
impl Default for NativeUnixBroker {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeUnixBroker {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl WireGuardBroker for NativeUnixBroker {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn set_psk(&mut self, config: SerializedBrokerConfig<'_>) -> Result<(), Self::Error> {
|
||||
let config: NativeUnixBrokerConfig = config.try_into()?;
|
||||
|
||||
let peer_id = format!("{}", config.peer_id.fmt_b64::<MAX_B64_PEER_ID_SIZE>());
|
||||
|
||||
let mut child = match Command::new("wg")
|
||||
.arg("set")
|
||||
.arg(config.interface)
|
||||
.arg("peer")
|
||||
.arg(peer_id)
|
||||
.arg("preshared-key")
|
||||
.arg("/dev/stdin")
|
||||
.stdin(Stdio::piped())
|
||||
.args(config.extra_params)
|
||||
.spawn()
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
anyhow::bail!("Could not find wg command");
|
||||
} else {
|
||||
return Err(anyhow::Error::new(e));
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Err(e) = config
|
||||
.psk
|
||||
.store_b64_writer::<MAX_B64_KEY_SIZE, _>(child.stdin.take().unwrap())
|
||||
{
|
||||
error!("could not write psk to wg: {:?}", e);
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
let status = child.wait();
|
||||
|
||||
if let Ok(status) = status {
|
||||
if status.success() {
|
||||
debug!("successfully passed psk to wg")
|
||||
} else {
|
||||
error!("could not pass psk to wg {:?}", status)
|
||||
}
|
||||
} else {
|
||||
error!("wait failed: {:?}", status)
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WireguardBrokerMio for NativeUnixBroker {
|
||||
type MioError = anyhow::Error;
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
_registry: &mio::Registry,
|
||||
_token: mio::Token,
|
||||
) -> Result<(), Self::MioError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_poll(&mut self) -> Result<(), Self::MioError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, _registry: &mio::Registry) -> Result<(), Self::MioError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Builder)]
|
||||
#[builder(pattern = "mutable")]
|
||||
pub struct NativeUnixBrokerConfigBase {
|
||||
pub interface: String,
|
||||
pub peer_id: Public<WG_PEER_LEN>,
|
||||
#[builder(private)]
|
||||
pub extra_params: Vec<u8>,
|
||||
}
|
||||
|
||||
impl NativeUnixBrokerConfigBaseBuilder {
|
||||
pub fn peer_id_b64(
|
||||
&mut self,
|
||||
peer_id: &str,
|
||||
) -> Result<&mut Self, NativeUnixBrokerConfigBaseBuilderError> {
|
||||
let mut peer_id_b64 = Public::<WG_PEER_LEN>::zero();
|
||||
b64_decode(peer_id.as_bytes(), &mut peer_id_b64.value).map_err(|_e| {
|
||||
NativeUnixBrokerConfigBaseBuilderError::ValidationError(
|
||||
"Failed to parse peer id b64".to_string(),
|
||||
)
|
||||
})?;
|
||||
Ok(self.peer_id(peer_id_b64))
|
||||
}
|
||||
|
||||
pub fn extra_params_ser(
|
||||
&mut self,
|
||||
extra_params: &Vec<String>,
|
||||
) -> Result<&mut Self, NativeUnixBrokerConfigBuilderError> {
|
||||
let params = to_allocvec(extra_params).map_err(|_e| {
|
||||
NativeUnixBrokerConfigBuilderError::ValidationError(
|
||||
"Failed to parse extra params".to_string(),
|
||||
)
|
||||
})?;
|
||||
Ok(self.extra_params(params))
|
||||
}
|
||||
}
|
||||
|
||||
impl WireguardBrokerCfg for NativeUnixBrokerConfigBase {
|
||||
fn create_config<'a>(&'a self, psk: &'a Secret<WG_KEY_LEN>) -> SerializedBrokerConfig<'a> {
|
||||
SerializedBrokerConfig {
|
||||
interface: self.interface.as_bytes(),
|
||||
peer_id: &self.peer_id,
|
||||
psk,
|
||||
additional_params: &self.extra_params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Builder)]
|
||||
#[builder(pattern = "mutable")]
|
||||
pub struct NativeUnixBrokerConfig<'a> {
|
||||
pub interface: &'a str,
|
||||
pub peer_id: &'a Public<WG_PEER_LEN>,
|
||||
pub psk: &'a Secret<WG_KEY_LEN>,
|
||||
pub extra_params: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<SerializedBrokerConfig<'a>> for NativeUnixBrokerConfig<'a> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: SerializedBrokerConfig<'a>) -> Result<Self, Self::Error> {
|
||||
let iface = std::str::from_utf8(value.interface)
|
||||
.map_err(|_| anyhow::Error::msg("Interface UTF8 decoding error"))?;
|
||||
|
||||
let extra_params: Vec<String> =
|
||||
from_bytes(value.additional_params).map_err(anyhow::Error::new)?;
|
||||
Ok(Self {
|
||||
interface: iface,
|
||||
peer_id: value.peer_id,
|
||||
psk: value.psk,
|
||||
extra_params,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use wireguard_uapi::linux as wg;
|
||||
|
||||
use crate::api::config::NetworkBrokerConfig;
|
||||
use crate::api::msgs;
|
||||
use crate::WireGuardBroker;
|
||||
use crate::{SerializedBrokerConfig, WireGuardBroker};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ConnectError {
|
||||
@@ -61,39 +64,45 @@ impl NetlinkWireGuardBroker {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for NetlinkWireGuardBroker {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
//TODO: Add useful info in Debug
|
||||
f.debug_struct("NetlinkWireGuardBroker").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl WireGuardBroker for NetlinkWireGuardBroker {
|
||||
type Error = SetPskError;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
interface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error> {
|
||||
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
||||
let config: NetworkBrokerConfig = config
|
||||
.try_into()
|
||||
.map_err(|e| SetPskError::NoSuchInterface)?;
|
||||
// Ensure that the peer exists by querying the device configuration
|
||||
// TODO: Use InvalidInterfaceError
|
||||
|
||||
let state = self
|
||||
.sock
|
||||
.get_device(wg::DeviceInterface::from_name(interface.to_owned()))?;
|
||||
.get_device(wg::DeviceInterface::from_name(config.iface))?;
|
||||
|
||||
if state
|
||||
.peers
|
||||
.iter()
|
||||
.find(|p| &p.public_key == &peer_id)
|
||||
.find(|p| &p.public_key == &config.peer_id.value)
|
||||
.is_none()
|
||||
{
|
||||
return Err(SetPskError::NoSuchPeer);
|
||||
}
|
||||
|
||||
// Peer update description
|
||||
let mut set_peer = wireguard_uapi::set::Peer::from_public_key(&peer_id);
|
||||
let mut set_peer = wireguard_uapi::set::Peer::from_public_key(&config.peer_id);
|
||||
set_peer
|
||||
.flags
|
||||
.push(wireguard_uapi::linux::set::WgPeerF::UpdateOnly);
|
||||
set_peer.preshared_key = Some(&psk);
|
||||
set_peer.preshared_key = Some(&config.psk.secret());
|
||||
|
||||
// Device update description
|
||||
let mut set_dev = wireguard_uapi::set::Device::from_ifname(interface.to_owned());
|
||||
let mut set_dev = wireguard_uapi::set::Device::from_ifname(config.iface);
|
||||
set_dev.peers.push(set_peer);
|
||||
|
||||
self.sock.set_device(set_dev)?;
|
||||
@@ -1,19 +1,40 @@
|
||||
#[cfg(feature = "enable_broker")]
|
||||
use std::result::Result;
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
use std::{fmt::Debug, result::Result};
|
||||
|
||||
#[cfg(feature = "enable_broker")]
|
||||
pub trait WireGuardBroker {
|
||||
pub const WG_KEY_LEN: usize = 32;
|
||||
pub const WG_PEER_LEN: usize = 32;
|
||||
pub trait WireGuardBroker: Debug {
|
||||
type Error;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
interface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error>;
|
||||
fn set_psk(&mut self, config: SerializedBrokerConfig<'_>) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "enable_broker")]
|
||||
pub trait WireguardBrokerCfg: Debug {
|
||||
fn create_config<'a>(&'a self, psk: &'a Secret<WG_KEY_LEN>) -> SerializedBrokerConfig<'a>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SerializedBrokerConfig<'a> {
|
||||
pub interface: &'a [u8],
|
||||
pub peer_id: &'a Public<WG_PEER_LEN>,
|
||||
pub psk: &'a Secret<WG_KEY_LEN>,
|
||||
pub additional_params: &'a [u8],
|
||||
}
|
||||
|
||||
pub trait WireguardBrokerMio: WireGuardBroker {
|
||||
type MioError;
|
||||
/// Register interested events for mio::Registry
|
||||
fn register(
|
||||
&mut self,
|
||||
registry: &mio::Registry,
|
||||
token: mio::Token,
|
||||
) -> Result<(), Self::MioError>;
|
||||
/// Run after a mio::poll operation
|
||||
fn process_poll(&mut self) -> Result<(), Self::MioError>;
|
||||
|
||||
fn unregister(&mut self, registry: &mio::Registry) -> Result<(), Self::MioError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "enable_broker_api")]
|
||||
pub mod api;
|
||||
#[cfg(feature = "enable_broker")]
|
||||
pub mod netlink;
|
||||
|
||||
pub mod brokers;
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
#[cfg(feature = "enable_broker")]
|
||||
#[cfg(feature = "enable_broker_api")]
|
||||
#[cfg(test)]
|
||||
mod integration_tests {
|
||||
|
||||
use rand::Rng;
|
||||
use rosenpass_wireguard_broker::api::mio_client::MioBrokerClient;
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
use rosenpass_wireguard_broker::api::msgs::{
|
||||
SetPskError, REQUEST_MSG_BUFFER_SIZE, RESPONSE_MSG_BUFFER_SIZE,
|
||||
};
|
||||
use rosenpass_wireguard_broker::api::server::{BrokerServer, BrokerServerError};
|
||||
use rosenpass_wireguard_broker::WireGuardBroker;
|
||||
use rosenpass_wireguard_broker::brokers::mio_client::MioBrokerClient;
|
||||
use rosenpass_wireguard_broker::WG_KEY_LEN;
|
||||
use rosenpass_wireguard_broker::WG_PEER_LEN;
|
||||
use rosenpass_wireguard_broker::{SerializedBrokerConfig, WireGuardBroker};
|
||||
use std::io::Read;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
struct MockServerBrokerInner {
|
||||
psk: Option<[u8; 32]>,
|
||||
peer_id: Option<[u8; 32]>,
|
||||
psk: Option<Secret<WG_KEY_LEN>>,
|
||||
peer_id: Option<Public<WG_PEER_LEN>>,
|
||||
interface: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockServerBroker {
|
||||
inner: Arc<Mutex<MockServerBrokerInner>>,
|
||||
}
|
||||
@@ -32,20 +36,15 @@ mod integration_tests {
|
||||
impl WireGuardBroker for MockServerBroker {
|
||||
type Error = SetPskError;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
interface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error> {
|
||||
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
||||
loop {
|
||||
let mut lock = self.inner.try_lock();
|
||||
|
||||
if let Ok(ref mut mutex) = lock {
|
||||
**mutex = MockServerBrokerInner {
|
||||
psk: Some(psk),
|
||||
peer_id: Some(peer_id),
|
||||
interface: Some(interface.to_string()),
|
||||
psk: Some(config.psk.clone()),
|
||||
peer_id: Some(config.peer_id.clone()),
|
||||
interface: Some(std::str::from_utf8(config.interface).unwrap().to_string()),
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -91,25 +90,34 @@ mod integration_tests {
|
||||
|
||||
for _ in 0..TEST_RUNS {
|
||||
//Create psk of random 32 bytes
|
||||
let mut psk: [u8; 32] = [0; 32];
|
||||
rand::thread_rng().fill(&mut psk);
|
||||
let mut peer_id: [u8; 32] = [0; 32];
|
||||
rand::thread_rng().fill(&mut peer_id);
|
||||
let psk = Secret::random();
|
||||
let peer_id = Public::random();
|
||||
let interface = "test";
|
||||
client.set_psk(&interface, peer_id, psk).unwrap();
|
||||
let config = SerializedBrokerConfig {
|
||||
psk: &psk,
|
||||
peer_id: &peer_id,
|
||||
interface: interface.as_bytes(),
|
||||
additional_params: &[],
|
||||
};
|
||||
client.set_psk(config).unwrap();
|
||||
|
||||
//Sleep for a while to allow the server to process the message
|
||||
std::thread::sleep(std::time::Duration::from_millis(
|
||||
rand::thread_rng().gen_range(100..500),
|
||||
));
|
||||
|
||||
let psk = psk.secret().to_owned();
|
||||
|
||||
loop {
|
||||
let mut lock = server_broker_inner.try_lock();
|
||||
|
||||
if let Ok(ref mut inner) = lock {
|
||||
// Check if the psk is received by the server
|
||||
let received_psk = inner.psk;
|
||||
assert_eq!(received_psk, Some(psk));
|
||||
let received_psk = &inner.psk;
|
||||
assert_eq!(
|
||||
received_psk.as_ref().map(|psk| psk.secret().to_owned()),
|
||||
Some(psk)
|
||||
);
|
||||
|
||||
let recieved_peer_id = inner.peer_id;
|
||||
assert_eq!(recieved_peer_id, Some(peer_id));
|
||||
|
||||
Reference in New Issue
Block a user