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 serde::{Deserialize, Serialize}; use std::net::{TcpListener, ToSocketAddrs}; use thiserror::Error; use xdg::BaseDirectories; use std::str::FromStr; #[derive(Debug, Error)] pub enum DaemonError { #[error("{0}")] Io(#[from] std::io::Error), #[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), #[error("{0}")] WgInterfaceError(#[from] defguard_wireguard_rs::error::WireguardInterfaceError), } #[derive(Serialize, Deserialize, Clone)] enum Endpoint { Ip(SocketAddr), Domain(String, u16), } // 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, endpoint: Option, ips: Vec, } fn default_wg_port() -> u16 { 51820 } #[derive(Serialize, Deserialize)] struct InterfaceConfig { privkey: String, // this really should be a different type, but this is what defguard takes... address: String, #[serde(default = "default_wg_port")] listen_port: u16, 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"); 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 networks = vec![("kek".to_string(), InterfaceConfig { // 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, // endpoint: Some(Endpoint::Domain("alex.69owo.de".to_string(), 12456)), // ips: vec![IpAddrMask::from_str("5.4.3.2/24").unwrap()], // }], //})]; // 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 .peers .iter() .map(|p| Peer { public_key: p.pubkey.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.clone(), }) .collect(); wg.create_interface()?; wg.configure_interface(&InterfaceConfiguration { name, prvkey: intr.privkey, address: intr.address, port: intr.listen_port as u32, peers: defguard_peers, })?; } Ok(()) }