diff options
author | Lia Lenckowski <lialenck@protonmail.com> | 2024-08-13 16:51:40 +0200 |
---|---|---|
committer | Lia Lenckowski <lialenck@protonmail.com> | 2024-08-13 16:51:40 +0200 |
commit | 8aa9f19e313b985aa13a52270a9dc77c19d8ce53 (patch) | |
tree | fe72c10161ce708833de1b07ab240812ad4ea21d | |
parent | 5073dc6ee88a02eb60f46127cbdb598692a42f39 (diff) | |
download | maesch-8aa9f19e313b985aa13a52270a9dc77c19d8ce53.tar maesch-8aa9f19e313b985aa13a52270a9dc77c19d8ce53.tar.bz2 maesch-8aa9f19e313b985aa13a52270a9dc77c19d8ce53.tar.zst |
refactor: split dbus/network off of daemon.rs
-rw-r--r-- | src/daemon.rs | 214 | ||||
-rw-r--r-- | src/daemon_dbus.rs | 98 | ||||
-rw-r--r-- | src/daemon_network.rs | 102 | ||||
-rw-r--r-- | src/main.rs | 2 |
4 files changed, 226 insertions, 190 deletions
diff --git a/src/daemon.rs b/src/daemon.rs index b5d1fc4..bf6105c 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,5 +1,3 @@ -use atomic_write_file::AtomicWriteFile; -use base64::prelude::*; use core::net::SocketAddr; use dbus::{channel::MatchingReceiver, message::MatchRule}; use dbus_crossroads::{Context, Crossroads, MethodErr}; @@ -7,7 +5,6 @@ use defguard_wireguard_rs::{ host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, }; use log::{debug, error, info, warn}; -use rand::Rng; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeSet, HashMap}, @@ -16,6 +13,7 @@ use std::{ marker::PhantomData, net::ToSocketAddrs, ops::DerefMut, + str::FromStr, sync::Arc, time::SystemTime, }; @@ -29,7 +27,7 @@ use tokio::{ }; use xdg::BaseDirectories; -use std::str::FromStr; +use crate::{daemon_dbus::*, daemon_network::*}; #[derive(Debug, Error)] pub enum DaemonError { @@ -54,24 +52,24 @@ pub enum DaemonError { } #[derive(Serialize, Deserialize, Clone)] -enum Endpoint { +pub enum Endpoint { Ip(SocketAddr), Domain(String, u16), } // subset of defguard_wireguard_rs::host::Peer, with hostname added #[derive(Serialize, Deserialize)] -struct PeerConfig { - psk: Option<Key>, - ips: Vec<(IpAddrMask, Option<String>)>, +pub struct PeerConfig { + pub psk: Option<Key>, + pub ips: Vec<(IpAddrMask, Option<String>)>, // if false: the hostnames are kept around for sharing, but we personally do not use them - use_hostnames: bool, - endpoint: Option<Endpoint>, + pub use_hostnames: bool, + pub endpoint: Option<Endpoint>, - last_changed: SystemTime, - known_to: Vec<usize>, + pub last_changed: SystemTime, + pub known_to: Vec<usize>, - mäsch_endpoint: SocketAddr, + pub mäsch_endpoint: SocketAddr, } fn default_wg_port() -> u16 { @@ -79,30 +77,27 @@ fn default_wg_port() -> u16 { } #[derive(Serialize, Deserialize)] -struct Network { - privkey: String, - +pub struct Network { + pub privkey: String, // this really should be a different type, but this is what defguard takes... - address: String, - + pub address: String, #[serde(default = "default_wg_port")] - listen_port: u16, - - peers: HashMap<Key, PeerConfig>, + pub listen_port: u16, + pub peers: HashMap<Key, PeerConfig>, - mäsch_port: u16, + pub mäsch_port: u16, } #[derive(Serialize, Deserialize, Default)] -struct Config { - networks: HashMap<String, Network>, +pub struct Config { + pub networks: HashMap<String, Network>, } // TODO das überschreibt änderungen an /etc/hosts während der runtime :( -struct State { - conf: Config, - nw_handles: HashMap<String, (WGApi, task::JoinHandle<()>)>, - hostfile: Option<(String, BTreeSet<String>)>, +pub struct State { + pub conf: Config, + pub nw_handles: HashMap<String, (WGApi, task::JoinHandle<()>)>, + pub hostfile: Option<(String, BTreeSet<String>)>, } impl Drop for State { @@ -250,160 +245,7 @@ async fn run_networks(state: Arc<RwLock<State>>) -> Result<(), DaemonError> { Ok(()) } -// TODO also take peers -async fn handle_add_network( - mut ctx: Context, - state: Arc<RwLock<State>>, - (name, may_key, may_ip, may_lp, may_mp): (String, String, String, u16, u16), -) -> PhantomData<(bool,)> { - // NOTE: this is kinda stupid: we convert to a string later anyways, as thats what - // defguard_wg takes... - let key = Key::new(match may_key.as_str() { - "" => rand::thread_rng().gen(), - _ => match BASE64_STANDARD.decode(may_key) { - Ok(v) if v.len() == 32 => v.try_into().unwrap(), - _ => { - warn!("AddNetwork with bad key"); - return ctx.reply(Err(MethodErr::invalid_arg("bad key"))); - } - }, - }); - - // we store the ip as the original string, but should validate it regardless - let (ip, ip_string) = match may_ip.as_str() { - "" => todo!(), - _ => match IpAddrMask::from_str(&may_ip) { - Ok(ip_mask) => (ip_mask.ip, may_ip), - Err(_) => { - warn!("AddNetwork with bad ip"); - return ctx.reply(Err(MethodErr::invalid_arg("invalid ip"))); - } - }, - }; - - let lp = if may_lp == 0 { 25565 } else { may_lp }; - let mp = if may_mp == 0 { 51820 } else { may_mp }; - - let mut state_rw_guard = state.write().await; - let state_rw = state_rw_guard.deref_mut(); - - let wg_api = match add_network( - &mut state_rw.hostfile, - name.clone(), - key.to_string(), - ip_string, - lp, - &HashMap::new(), - ) - .await - { - Ok(wg_api) => wg_api, - Err(e) => { - warn!("AddNetwork couldn't add network: {e}"); - return ctx.reply(Err(MethodErr::failed(&e))); - } - }; - - // TODO ins wg_api - let listener = match TcpListener::bind((ip, mp)).await { - Ok(l) => l, - Err(e) => { - let _ = wg_api.remove_interface(); - warn!("AddNetwork couldn't start listener: {e}"); - return ctx.reply(Err(MethodErr::failed(&e))); - } - }; - let h = task::spawn(print_error(run_network( - state.clone(), - listener, - name.clone(), - ))); - - state_rw.nw_handles.insert(name.clone(), (wg_api, h)); - - // TODO save new config - - ctx.reply(Ok((true,))) -} - -async fn add_network( - hostfile: &mut Option<(String, BTreeSet<String>)>, - name: String, - privkey: String, - address: String, - port: u16, - peers: &HashMap<Key, PeerConfig>, -) -> Result<WGApi, DaemonError> { - let wg = WGApi::new(name.clone(), false)?; - let defguard_peers = peers - .iter() - .map(|(peer_key, p)| Peer { - public_key: peer_key.clone(), - preshared_key: p.psk.clone(), - protocol_version: None, - endpoint: p - .endpoint - .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(), - }) - .flatten(), - last_handshake: None, - tx_bytes: 0, - rx_bytes: 0, - persistent_keepalive_interval: None, - allowed_ips: p.ips.iter().map(|(ip_mask, _)| ip_mask.clone()).collect(), - }) - .collect(); - wg.create_interface()?; - wg.configure_interface(&InterfaceConfiguration { - name: name.clone(), - prvkey: privkey, - address: address, - port: port as u32, - peers: defguard_peers, - })?; - - if let Some((hosts_str, hosts)) = hostfile { - peers - .values() - .map(|peer| { - if peer.use_hostnames { - peer.ips - .iter() - .map(|(mask, may_dom)| { - if let Some(dom) = may_dom - && hosts.insert(dom.clone()) - { - hosts_str.push_str(&format!("{}", mask.ip)); - hosts_str.push('\t'); - hosts_str.push_str(&dom); - hosts_str.push('\n'); - } - }) - .count(); - } - }) - .count(); - } - - if let Some((hosts_str, _)) = hostfile { - debug!("writing hosts file: {hosts_str}"); - - let mut f = AtomicWriteFile::open("/etc/hosts")?; - f.write(hosts_str.as_bytes())?; - f.commit()?; - } - - Ok(wg) -} - -async fn print_error<E: std::fmt::Display, O, F: std::future::Future<Output = Result<O, E>>>( +pub async fn print_error<E: std::fmt::Display, O, F: std::future::Future<Output = Result<O, E>>>( f: F, ) -> () { match f.await { @@ -411,11 +253,3 @@ async fn print_error<E: std::fmt::Display, O, F: std::future::Future<Output = Re _ => (), }; } - -async fn run_network( - state: Arc<RwLock<State>>, - sock: TcpListener, - nw_name: String, -) -> Result<(), DaemonError> { - Ok(()) -} diff --git a/src/daemon_dbus.rs b/src/daemon_dbus.rs new file mode 100644 index 0000000..7065eec --- /dev/null +++ b/src/daemon_dbus.rs @@ -0,0 +1,98 @@ +use base64::prelude::*; +use dbus::{channel::MatchingReceiver, message::MatchRule}; +use dbus_crossroads::{Context, Crossroads, MethodErr}; +use defguard_wireguard_rs::{ + host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, +}; +use log::{debug, error, info, warn}; +use rand::Rng; +use std::{ + collections::{BTreeSet, HashMap}, + marker::PhantomData, + ops::DerefMut, + str::FromStr, + sync::Arc, +}; +use tokio::{ + net::TcpListener, + sync::{broadcast, RwLock}, + task, +}; + +use crate::{daemon::*, daemon_network::*}; + +// TODO also take peers +pub async fn handle_add_network( + mut ctx: Context, + state: Arc<RwLock<State>>, + (name, may_key, may_ip, may_lp, may_mp): (String, String, String, u16, u16), +) -> PhantomData<(bool,)> { + // NOTE: this is kinda stupid: we convert to a string later anyways, as thats what + // defguard_wg takes... + let key = Key::new(match may_key.as_str() { + "" => rand::thread_rng().gen(), + _ => match BASE64_STANDARD.decode(may_key) { + Ok(v) if v.len() == 32 => v.try_into().unwrap(), + _ => { + warn!("AddNetwork with bad key"); + return ctx.reply(Err(MethodErr::invalid_arg("bad key"))); + } + }, + }); + + // we store the ip as the original string, but should validate it regardless + let (ip, ip_string) = match may_ip.as_str() { + "" => todo!(), + _ => match IpAddrMask::from_str(&may_ip) { + Ok(ip_mask) => (ip_mask.ip, may_ip), + Err(_) => { + warn!("AddNetwork with bad ip"); + return ctx.reply(Err(MethodErr::invalid_arg("invalid ip"))); + } + }, + }; + + let lp = if may_lp == 0 { 25565 } else { may_lp }; + let mp = if may_mp == 0 { 51820 } else { may_mp }; + + let mut state_rw_guard = state.write().await; + let state_rw = state_rw_guard.deref_mut(); + + let wg_api = match add_network( + &mut state_rw.hostfile, + name.clone(), + key.to_string(), + ip_string, + lp, + &HashMap::new(), + ) + .await + { + Ok(wg_api) => wg_api, + Err(e) => { + warn!("AddNetwork couldn't add network: {e}"); + return ctx.reply(Err(MethodErr::failed(&e))); + } + }; + + // TODO ins wg_api + let listener = match TcpListener::bind((ip, mp)).await { + Ok(l) => l, + Err(e) => { + let _ = wg_api.remove_interface(); + warn!("AddNetwork couldn't start listener: {e}"); + return ctx.reply(Err(MethodErr::failed(&e))); + } + }; + let h = task::spawn(print_error(run_network( + state.clone(), + listener, + name.clone(), + ))); + + state_rw.nw_handles.insert(name.clone(), (wg_api, h)); + + // TODO save new config + + ctx.reply(Ok((true,))) +} diff --git a/src/daemon_network.rs b/src/daemon_network.rs new file mode 100644 index 0000000..895e409 --- /dev/null +++ b/src/daemon_network.rs @@ -0,0 +1,102 @@ +use atomic_write_file::AtomicWriteFile; +use defguard_wireguard_rs::{ + host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, +}; +use log::{debug, error, info, warn}; +use std::{ + collections::{BTreeSet, HashMap}, + io::Write, + net::ToSocketAddrs, + sync::Arc, +}; +use tokio::{ + net::TcpListener, + sync::{broadcast, RwLock}, +}; + +use crate::daemon::*; + +pub async fn add_network( + hostfile: &mut Option<(String, BTreeSet<String>)>, + name: String, + privkey: String, + address: String, + port: u16, + peers: &HashMap<Key, PeerConfig>, +) -> Result<WGApi, DaemonError> { + let wg = WGApi::new(name.clone(), false)?; + let defguard_peers = peers + .iter() + .map(|(peer_key, p)| Peer { + public_key: peer_key.clone(), + preshared_key: p.psk.clone(), + protocol_version: None, + endpoint: p + .endpoint + .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(), + }) + .flatten(), + last_handshake: None, + tx_bytes: 0, + rx_bytes: 0, + persistent_keepalive_interval: None, + allowed_ips: p.ips.iter().map(|(ip_mask, _)| ip_mask.clone()).collect(), + }) + .collect(); + wg.create_interface()?; + wg.configure_interface(&InterfaceConfiguration { + name: name.clone(), + prvkey: privkey, + address: address, + port: port as u32, + peers: defguard_peers, + })?; + + if let Some((hosts_str, hosts)) = hostfile { + peers + .values() + .map(|peer| { + if peer.use_hostnames { + peer.ips + .iter() + .map(|(mask, may_dom)| { + if let Some(dom) = may_dom + && hosts.insert(dom.clone()) + { + hosts_str.push_str(&format!("{}", mask.ip)); + hosts_str.push('\t'); + hosts_str.push_str(&dom); + hosts_str.push('\n'); + } + }) + .count(); + } + }) + .count(); + } + + if let Some((hosts_str, _)) = hostfile { + debug!("writing hosts file: {hosts_str}"); + + let mut f = AtomicWriteFile::open("/etc/hosts")?; + f.write(hosts_str.as_bytes())?; + f.commit()?; + } + + Ok(wg) +} + +pub async fn run_network( + state: Arc<RwLock<State>>, + sock: TcpListener, + nw_name: String, +) -> Result<(), DaemonError> { + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index b7300a7..705b39d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ #![feature(if_let_guard)] pub mod daemon; +pub mod daemon_dbus; +pub mod daemon_network; use clap::{Parser, Subcommand}; use daemon::daemon; |