diff options
author | Lia Lenckowski <lialenck@protonmail.com> | 2024-08-10 00:46:45 +0200 |
---|---|---|
committer | Lia Lenckowski <lialenck@protonmail.com> | 2024-08-10 00:46:45 +0200 |
commit | c5ae944b3d0442cf8c0015d42e32c1ceea8d1c20 (patch) | |
tree | 30f2b29dbbb4f3d1b7829b5d2236ce09caa84305 /src/daemon.rs | |
parent | 1932a9c95174d7d211e19203df96a396107373f1 (diff) | |
download | maesch-c5ae944b3d0442cf8c0015d42e32c1ceea8d1c20.tar maesch-c5ae944b3d0442cf8c0015d42e32c1ceea8d1c20.tar.bz2 maesch-c5ae944b3d0442cf8c0015d42e32c1ceea8d1c20.tar.zst |
impl hostname ding; remove redb (deemed inefficient overkill)
we are not planning to be web-scale(tm) anyway
Diffstat (limited to 'src/daemon.rs')
-rw-r--r-- | src/daemon.rs | 158 |
1 files changed, 102 insertions, 56 deletions
diff --git a/src/daemon.rs b/src/daemon.rs index 2c73d6c..1c746fd 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,9 +1,12 @@ +use atomic_write_file::AtomicWriteFile; use core::net::SocketAddr; use defguard_wireguard_rs::{host::Peer, InterfaceConfiguration, WGApi, WireguardInterfaceApi}; -use defguard_wireguard_rs::{key::Key as WgKey, net::IpAddrMask}; -use log::{info, trace, warn}; -use redb::{Database, Error, Key, Range, ReadableTable, TableDefinition, TypeName, Value}; +use defguard_wireguard_rs::{key::Key, net::IpAddrMask}; +use log::{info, warn}; use serde::{Deserialize, Serialize}; +use std::collections::BTreeSet; +use std::fs::File; +use std::io::{ErrorKind, Read, Write}; use std::net::{TcpListener, ToSocketAddrs}; use thiserror::Error; use xdg::BaseDirectories; @@ -18,15 +21,6 @@ pub enum DaemonError { #[error("{0}")] XdgBase(#[from] xdg::BaseDirectoriesError), - #[error("{0}")] - DbOpen(#[from] redb::DatabaseError), - #[error("{0}")] - DbTrans(#[from] redb::TransactionError), - #[error("{0}")] - DbTable(#[from] redb::TableError), - #[error("{0}")] - DbStorage(#[from] redb::StorageError), - // TODO hier wärs nett zu unterscheiden was decoded wurde #[error("{0}")] Decoding(#[from] serde_json::Error), @@ -44,13 +38,12 @@ enum Endpoint { // subset of defguard_wireguard_rs::host::Peer, with hostname added #[derive(Serialize, Deserialize)] struct PeerConfig { - pubkey: WgKey, - psk: Option<WgKey>, - hostname: Option<String>, - // if false: the hostname is kept around for sharing, but we personally do not use it - use_hostname: bool, + pubkey: Key, + psk: Option<Key>, + 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>, - ips: Vec<IpAddrMask>, } fn default_wg_port() -> u16 { @@ -58,7 +51,8 @@ fn default_wg_port() -> u16 { } #[derive(Serialize, Deserialize)] -struct InterfaceConfig { +struct Network { + name: String, privkey: String, // this really should be a different type, but this is what defguard takes... @@ -70,50 +64,66 @@ struct InterfaceConfig { peers: Vec<PeerConfig>, } -// NOTE das ist ne dumme idee gewesen... nen peer hinzufügen braucht jz nen kompletten reserialize -const NETWORKS: TableDefinition<&str, /* InterfaceConfig */ &[u8]> = - TableDefinition::new("networks"); +#[derive(Serialize, Deserialize, Default)] +struct Config { + networks: Vec<Network>, +} pub fn daemon() -> Result<(), DaemonError> { - let db_path = BaseDirectories::with_prefix("mäsch")?.place_state_file("daemon-db")?; - let db = Database::create(db_path)?; - info!("opened db"); - - let networks: Vec<(String, InterfaceConfig)> = match db.begin_read()?.open_table(NETWORKS) { - Err(redb::TableError::TableDoesNotExist(_)) => vec![], - Ok(tbl) => tbl - .iter()? - .map(|kv_result| { - let (access_key, access_val) = kv_result?; - Ok(( - access_key.value().to_owned(), - serde_json::from_slice(access_val.value())?, - )) - }) - .collect::<Result<_, DaemonError>>()?, - Err(e) => Err(e)?, + let config_path = BaseDirectories::with_prefix("mäsch")?.place_state_file("daemon.json")?; + let config: Config = match File::open(config_path) { + Ok(f) => serde_json::from_reader(f)?, + Err(e) => match e.kind() { + ErrorKind::NotFound => Config::default(), + _ => Err(e)?, + }, }; + info!("read config"); - //let networks = vec![("kek".to_string(), InterfaceConfig { + //let networks = vec![Network { + // name: "kek".to_string(), // privkey: "OK9WQudPVO5rXxcdxdtTzRmJzVu+KuqLMstYsZd8mWE=".to_string(), // address: "1.2.3.4".to_string(), // listen_port: 5221, // peers: vec![PeerConfig { - // pubkey: WgKey::from_str("Osrxi/bRVK+FQit7YMbIgSaOWmRDOZQoh/7ddV4eEE8=").unwrap(), - // psk: Some(WgKey::from_str("wFiG3II9ivYBn+xjLGChC0PjNlbOibZ1K6pmspPD0Hg=").unwrap()), - // hostname: Some("blah.blob".to_string()), - // use_hostname: false, + // pubkey: Key::from_str("Osrxi/bRVK+FQit7YMbIgSaOWmRDOZQoh/7ddV4eEE8=").unwrap(), + // psk: Some(Key::from_str("wFiG3II9ivYBn+xjLGChC0PjNlbOibZ1K6pmspPD0Hg=").unwrap()), + // use_hostnames: true, // endpoint: Some(Endpoint::Domain("alex.69owo.de".to_string(), 12456)), - // ips: vec![IpAddrMask::from_str("5.4.3.2/24").unwrap()], + // ips: vec![(IpAddrMask::from_str("5.4.3.2/24").unwrap(), Some("blah.blub".to_owned()))], // }], - //})]; + //}]; // TODO call wg.remove_interface on program exit using a drop impl on an 'Interface' struct - // containing the InterfaceConfig and WGApi - // TODO possibly add hostnames to /etc/hosts - for (name, intr) in networks { - let wg = WGApi::new(name.clone(), false)?; - let defguard_peers = intr + // containing the Network and WGApi + + let mut hostfile = match File::open("/etc/hosts") { + Ok(mut f) => { + let mut r = String::new(); + f.read_to_string(&mut r)?; + + let seen_hostnames: BTreeSet<String> = r + .lines() + .map(|l| { + l.split_whitespace() + .take_while(|dom| dom.chars().next().unwrap() != '#') + .skip(1) + }) + .flatten() + .map(|dom| dom.to_owned()) + .collect(); + + Some((r, seen_hostnames)) + } + Err(e) => { + warn!("failed to read /etc/hosts: {e}"); + None + } + }; + + for nw in config.networks { + let wg = WGApi::new(nw.name.clone(), false)?; + let defguard_peers = nw .peers .iter() .map(|p| Peer { @@ -136,18 +146,54 @@ pub fn daemon() -> Result<(), DaemonError> { tx_bytes: 0, rx_bytes: 0, persistent_keepalive_interval: None, - allowed_ips: p.ips.clone(), + allowed_ips: p.ips.iter().map(|(ip_mask, _)| ip_mask.clone()).collect(), }) .collect(); wg.create_interface()?; wg.configure_interface(&InterfaceConfiguration { - name, - prvkey: intr.privkey, - address: intr.address, - port: intr.listen_port as u32, + name: nw.name.clone(), + prvkey: nw.privkey, + address: nw.address, + port: nw.listen_port as u32, peers: defguard_peers, })?; + + if let Some((hosts_str, hosts)) = &mut hostfile { + nw.peers + .iter() + .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(); + } + + info!("loaded configuration for {0}", nw.name); } + info!("loaded all existing configurations"); + + if let Some((hosts_str, _)) = &hostfile { + info!("proposed next hosts file: {hosts_str}"); + + let mut f = AtomicWriteFile::open("/etc/hosts")?; + f.write(hosts_str.as_bytes())?; + f.commit(); + } + + // TODO open dbus & network interfaces Ok(()) } |