diff options
author | Lia Lenckowski <lialenck@protonmail.com> | 2024-08-16 23:29:59 +0200 |
---|---|---|
committer | Lia Lenckowski <lialenck@protonmail.com> | 2024-08-16 23:29:59 +0200 |
commit | d14c230f63617bb5fe84414723c10842fc029a3c (patch) | |
tree | 611d286d27f6605bb7b63667f221ccc7f7b19495 | |
parent | 9c9039bdcd3f829ff0cfee5153b0435e0d209114 (diff) | |
download | maesch-d14c230f63617bb5fe84414723c10842fc029a3c.tar maesch-d14c230f63617bb5fe84414723c10842fc029a3c.tar.bz2 maesch-d14c230f63617bb5fe84414723c10842fc029a3c.tar.zst |
add AddPeer
-rw-r--r-- | src/daemon_config.rs | 2 | ||||
-rw-r--r-- | src/daemon_dbus.rs | 204 | ||||
-rw-r--r-- | src/daemon_network.rs | 47 |
3 files changed, 239 insertions, 14 deletions
diff --git a/src/daemon_config.rs b/src/daemon_config.rs index 246a736..559c9d0 100644 --- a/src/daemon_config.rs +++ b/src/daemon_config.rs @@ -30,7 +30,7 @@ pub struct PeerConfig { pub endpoint: Option<Endpoint>, pub last_changed: SystemTime, - pub known_to: Vec<usize>, + pub known_to: Vec<Key>, pub mäsch_endpoint: SocketAddr, } diff --git a/src/daemon_dbus.rs b/src/daemon_dbus.rs index f0873f0..5615bab 100644 --- a/src/daemon_dbus.rs +++ b/src/daemon_dbus.rs @@ -8,8 +8,14 @@ use defguard_wireguard_rs::{key::Key, net::IpAddrMask, WireguardInterfaceApi}; use log::{debug, error, info, warn}; use rand::Rng; use std::{ - collections::HashMap, marker::PhantomData, ops::DerefMut, path::PathBuf, str::FromStr, + collections::{hash_map::Entry, HashMap}, + marker::PhantomData, + net::SocketAddr, + ops::DerefMut, + path::PathBuf, + str::FromStr, sync::Arc, + time::SystemTime, }; use tokio::{net::TcpListener, sync::RwLock, task}; @@ -26,6 +32,9 @@ pub async fn start_dbus( let state_rem_network = state.clone(); let config_path_rem_network = config_path.clone(); + let state_add_peer = state.clone(); + let config_path_add_peer = config_path.clone(); + b.method_with_cr_async( "AddNetwork", ("name", "key", "ip", "listen_port", "maesch_port"), @@ -49,6 +58,38 @@ pub async fn start_dbus( ) }, ); + b.method_with_cr_async( + "AddPeer", + ( + "network", + "key", + "psk", + "use_hostnames", + "endpoint", + "maesch_endpoint", + "ips", + ), + ("success",), + move |ctx, + _, + args: ( + String, + String, + String, + bool, + String, + String, + Vec<(String, String)>, + )| { + debug!("Received AddPeer"); + handle_add_peer( + ctx, + state_add_peer.clone(), + config_path_add_peer.clone(), + args, + ) + }, + ); }); cr.insert("/de/a/maesch", &[if_token], ()); @@ -76,7 +117,155 @@ pub async fn start_dbus( Ok((c, receive_token)) } -pub async fn handle_remove_network( +async fn handle_add_peer( + mut ctx: Context, + state: Arc<RwLock<State>>, + config_path: PathBuf, + (nw_name, key, may_psk, use_hostnames, may_endpoint, may_mäsch_endpoint, allowed_ips): ( + String, + String, + String, + bool, + String, + String, + Vec<(String, String)>, + ), +) -> PhantomData<(bool,)> { + let key = match Key::from_str(&key) { + Ok(k) => k, + Err(e) => { + warn!("AddPeer with bad key: {e}"); + return ctx.reply(Err(MethodErr::invalid_arg(&e))); + } + }; + + let psk = match may_psk.as_str() { + "" => None, + _ => match Key::from_str(&may_psk) { + Ok(k) => Some(k), + Err(e) => { + warn!("AddPeer with bad pre-shared key: {e}"); + return ctx.reply(Err(MethodErr::invalid_arg(&e))); + } + }, + }; + + // use_hostnames is already a boolean, so i guess we require it to be set? + + let endpoint = match may_endpoint.as_str() { + "" => None, + _ => match SocketAddr::from_str(&may_endpoint) { + Ok(addr) => Some(Endpoint::Ip(addr)), + Err(_) => { + let mut parts_it = may_endpoint.split(':'); + match (parts_it.next(), parts_it.next(), parts_it.next()) { + (Some(domain), Some(port), None) if let Ok(port) = port.parse() => { + Some(Endpoint::Domain(domain.to_owned(), port)) + } + _ => { + warn!("AddPeer with bad endpoint: {may_endpoint}"); + return ctx.reply(Err(MethodErr::invalid_arg("Could not parse endpoint"))); + } + } + } + }, + }; + + let mäsch_endpoint = match SocketAddr::from_str(&may_mäsch_endpoint) { + Ok(addr) => addr, + Err(e) => { + warn!("AddPeer with bad mäsch endpoint: {e}"); + return ctx.reply(Err(MethodErr::invalid_arg(&e))); + } + }; + + // let may_allowed_ips = mapM (\(addr, hostname) -> (, if hostname == "" then Nothing else Just hostname) <$> readMaybe addr) allowed_ips + let may_allowed_ips: Result<Vec<(IpAddrMask, Option<String>)>, _> = allowed_ips + .into_iter() + .map(|(addr, hostname)| { + addr.parse() + .map(|ip_mask| (ip_mask, if hostname == "" { None } else { Some(hostname) })) + }) + .collect(); + let allowed_ips = match may_allowed_ips { + Ok(v) => v, + Err(e) => { + warn!("AddPeer with bad allowed ips: {e}"); + return ctx.reply(Err(MethodErr::invalid_arg(&e))); + } + }; + + let mut state_rw_guard = state.write().await; + if !state_rw_guard.conf.networks.contains_key(&nw_name) { + warn!("AddPeer for non-existent network"); + return ctx.reply(Err(MethodErr::invalid_arg("bad network"))); + }; + + let allowed_ips_without_domains = allowed_ips.iter().map(|(ip, _)| ip.clone()).collect(); + + let wg_api = &state_rw_guard + .nw_handles + .get(&nw_name) + .expect("state.conf.networks and state.nw_handles desynced") + .0; + match add_peer( + wg_api, + key.clone(), + psk.clone(), + endpoint.clone(), + allowed_ips_without_domains, + ) + .await + { + Ok(_) => info!("Added peer"), + Err(e) => { + warn!("AddPeer failed: {e}"); + return ctx.reply(Err(MethodErr::invalid_arg(&e))); + } + }; + + match state_rw_guard + .conf + .networks + .get_mut(&nw_name) + .expect("state.conf.networks changed while lock was held") + .peers + .entry(key) + { + Entry::Vacant(e) => { + e.insert(PeerConfig { + psk, + ips: allowed_ips, + use_hostnames, + endpoint, + last_changed: SystemTime::now(), + known_to: vec![], + mäsch_endpoint, + }); + } + Entry::Occupied(e) => { + let r = e.into_mut(); + r.psk = psk; + r.ips = allowed_ips; + r.use_hostnames = use_hostnames; + r.endpoint = endpoint; + r.last_changed = SystemTime::now(); + r.mäsch_endpoint = mäsch_endpoint; + } + }; + + match write_config(&state_rw_guard.conf, &config_path) { + Ok(_) => info!("Synced config"), + Err(e) => { + error!("Couldn't sync config: {e}"); + return ctx.reply(Err(MethodErr::failed(&e))); + } + }; + + return ctx.reply(Ok((true,))); +} + +async fn handle_remove_network( mut ctx: Context, state: Arc<RwLock<State>>, config_path: PathBuf, @@ -118,8 +307,7 @@ pub async fn handle_remove_network( ctx.reply(Ok((true,))) } -// TODO make this merge correctly... -pub async fn handle_add_network( +async fn handle_add_network( mut ctx: Context, state: Arc<RwLock<State>>, config_path: PathBuf, @@ -128,7 +316,7 @@ pub async fn handle_add_network( let mut state_rw_guard = state.write().await; let state_rw = state_rw_guard.deref_mut(); - // TODO do more with the entry... + // Scary! let prev_entry = state_rw.conf.networks.remove(&name); let key = if may_key.as_str() == "" { @@ -151,6 +339,7 @@ pub async fn handle_add_network( ), None => { warn!("AddNetwork with no ip"); + prev_entry.map(|pe| state_rw.conf.networks.insert(name, pe)); return ctx.reply(Err(MethodErr::invalid_arg("ip required"))); } }, @@ -158,6 +347,7 @@ pub async fn handle_add_network( Ok(ip_mask) => (ip_mask.ip, may_ip), Err(_) => { warn!("AddNetwork with bad ip"); + prev_entry.map(|pe| state_rw.conf.networks.insert(name, pe)); return ctx.reply(Err(MethodErr::invalid_arg("invalid ip"))); } }, @@ -189,6 +379,7 @@ pub async fn handle_add_network( Ok(v) => v, Err(e) => { warn!("AddNetwork couldn't add network: {e}"); + prev_entry.map(|pe| state_rw.conf.networks.insert(name, pe)); return ctx.reply(Err(MethodErr::failed(&e))); } }; @@ -198,6 +389,7 @@ pub async fn handle_add_network( Err(e) => { let _ = wg_api.remove_interface(); warn!("AddNetwork couldn't start listener: {e}"); + prev_entry.map(|pe| state_rw.conf.networks.insert(name, pe)); return ctx.reply(Err(MethodErr::failed(&e))); } }; @@ -214,7 +406,7 @@ pub async fn handle_add_network( privkey: key, address: ip_string, listen_port: lp, - peers: prev_entry.map_or_else(|| HashMap::new(), |nw| nw.peers), + peers: prev_entry.map_or_else(|| HashMap::new(), |nw: Network| nw.peers), mäsch_port: mp, }, ); diff --git a/src/daemon_network.rs b/src/daemon_network.rs index 97349c8..5dd003a 100644 --- a/src/daemon_network.rs +++ b/src/daemon_network.rs @@ -1,10 +1,10 @@ use defguard_wireguard_rs::{ - host::Peer, key::Key, InterfaceConfiguration, WGApi, WireguardInterfaceApi, + host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, }; //use log::{debug, error, info, warn}; use std::{ collections::{BTreeSet, HashMap}, - net::ToSocketAddrs, + net::{SocketAddr, ToSocketAddrs}, sync::Arc, }; use tokio::{net::TcpListener, sync::RwLock}; @@ -12,6 +12,43 @@ use tokio::{net::TcpListener, sync::RwLock}; use crate::daemon::*; use crate::daemon_config::*; +fn get_one_address(pair: &(String, u16)) -> Option<SocketAddr> { + pair.to_socket_addrs() + .ok() + .map(|mut it| it.next()) + .flatten() +} + +pub async fn add_peer( + api: &WGApi, + key: Key, + psk: Option<Key>, + endpoint: Option<Endpoint>, + ips: Vec<IpAddrMask>, +) -> Result<(), DaemonError> { + let endpoint = endpoint + .map(|ep| match ep { + Endpoint::Ip(addr) => Some(addr), + Endpoint::Domain(s, p) => get_one_address(&(s, p)), + }) + .flatten(); + let peer = Peer { + public_key: key, + preshared_key: psk, + protocol_version: None, + endpoint: endpoint, + last_handshake: None, + tx_bytes: 0, + rx_bytes: 0, + persistent_keepalive_interval: None, + allowed_ips: ips, + }; + + api.configure_peer(&peer)?; + + Ok(()) +} + pub async fn add_network( name: String, privkey: String, @@ -33,11 +70,7 @@ pub async fn add_network( .clone() .map(|e| match e { Endpoint::Ip(ep) => Some(ep), - Endpoint::Domain(s, p) => (s, p) - .to_socket_addrs() - .ok() - .map(|mut it| it.next()) - .flatten(), + Endpoint::Domain(s, p) => get_one_address(&(s, p)), }) .flatten(), last_handshake: None, |