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