From c5ae944b3d0442cf8c0015d42e32c1ceea8d1c20 Mon Sep 17 00:00:00 2001 From: Lia Lenckowski Date: Sat, 10 Aug 2024 00:46:45 +0200 Subject: impl hostname ding; remove redb (deemed inefficient overkill) we are not planning to be web-scale(tm) anyway --- Cargo.lock | 106 +++++++++++++++++++++++++++++++++++++-- Cargo.toml | 12 ++--- src/daemon.rs | 158 +++++++++++++++++++++++++++++++++++++--------------------- src/main.rs | 2 + 4 files changed, 211 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddc7d53..f3be1b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,16 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "atomic-write-file" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf54d4588732bdfc5ebc3eb9f74f20e027112fc31de412fc7ff0cd1c6896dae" +dependencies = [ + "nix 0.28.0", + "rand", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -108,6 +118,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "clap" version = "4.5.13" @@ -169,7 +185,7 @@ dependencies = [ "netlink-packet-utils", "netlink-packet-wireguard", "netlink-sys", - "nix", + "nix 0.27.1", "serde", "thiserror", ] @@ -197,6 +213,17 @@ dependencies = [ "log", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.5.0" @@ -252,11 +279,11 @@ dependencies = [ name = "mäsch" version = "0.1.0" dependencies = [ + "atomic-write-file", "clap", "defguard_wireguard_rs", "env_logger", "log", - "redb", "serde", "serde_json", "thiserror", @@ -349,12 +376,33 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -374,12 +422,33 @@ dependencies = [ ] [[package]] -name = "redb" -version = "2.1.1" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6dd20d3cdeb9c7d2366a0b16b93b35b75aec15309fbeb7ce477138c9f68c8c0" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", ] [[package]] @@ -498,6 +567,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "windows-sys" version = "0.52.0" @@ -576,3 +651,24 @@ name = "xdg" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 0114aeb..7cb0994 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] -redb = "2" -serde = { version = "1", features = ["derive"] } -serde_json = "1" -xdg = "2" +atomic-write-file = "0" +clap = { version = "4.5.13", features = ["derive"] } +defguard_wireguard_rs = "0" env_logger = "0" log = "0" +serde_json = "1" +serde = { version = "1", features = ["derive"] } thiserror = "1" -clap = { version = "4.5.13", features = ["derive"] } -defguard_wireguard_rs = "0" +xdg = "2" 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, - hostname: Option, - // if false: the hostname is kept around for sharing, but we personally do not use it - use_hostname: bool, + pubkey: Key, + psk: Option, + ips: Vec<(IpAddrMask, Option)>, + // if false: the hostnames are kept around for sharing, but we personally do not use them + use_hostnames: bool, endpoint: Option, - ips: Vec, } 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, } -// 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, +} 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::>()?, - 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 = 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(()) } diff --git a/src/main.rs b/src/main.rs index 6bc049b..d85da33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(let_chains)] + pub mod daemon; use clap::{Parser, Subcommand}; -- cgit v1.2.3-70-g09d2