From c7369246ad3b339be9607f7018f8764ee320bd20 Mon Sep 17 00:00:00 2001 From: Lia Lenckowski Date: Tue, 13 Aug 2024 21:09:22 +0200 Subject: sync hosts more safely; refactor: split off config --- src/daemon_config.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/daemon_config.rs (limited to 'src/daemon_config.rs') diff --git a/src/daemon_config.rs b/src/daemon_config.rs new file mode 100644 index 0000000..246a736 --- /dev/null +++ b/src/daemon_config.rs @@ -0,0 +1,121 @@ +use atomic_write_file::AtomicWriteFile; +use defguard_wireguard_rs::{key::Key, net::IpAddrMask}; +use log::info; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeSet, HashMap}, + fs::File, + io::{ErrorKind, Read, Write}, + net::SocketAddr, + path::PathBuf, + time::SystemTime, +}; +use xdg::BaseDirectories; + +use crate::daemon::DaemonError; + +#[derive(Serialize, Deserialize, Clone)] +pub enum Endpoint { + Ip(SocketAddr), + Domain(String, u16), +} + +// subset of defguard_wireguard_rs::host::Peer, with hostname added +#[derive(Serialize, Deserialize)] +pub struct PeerConfig { + pub psk: Option, + pub ips: Vec<(IpAddrMask, Option)>, + // if false: the hostnames are kept around for sharing, but we personally do not use them + pub use_hostnames: bool, + pub endpoint: Option, + + pub last_changed: SystemTime, + pub known_to: Vec, + + pub mäsch_endpoint: SocketAddr, +} + +fn default_wg_port() -> u16 { + 51820 +} + +#[derive(Serialize, Deserialize)] +pub struct Network { + pub privkey: String, + // this really should be a different type, but this is what defguard takes... + pub address: String, + #[serde(default = "default_wg_port")] + pub listen_port: u16, + pub peers: HashMap, + + pub mäsch_port: u16, +} + +#[derive(Serialize, Deserialize, Default)] +pub struct Config { + pub networks: HashMap, +} + +pub fn load_config() -> Result<(PathBuf, Config), DaemonError> { + 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"); + + Ok((config_path, config)) +} + +pub fn write_config(conf: &Config, path: &PathBuf) -> Result<(), std::io::Error> { + let mut f = AtomicWriteFile::open(path)?; + serde_json::to_writer(&mut f, conf)?; + f.commit()?; + + Ok(()) +} + +pub fn sync_hostnames(hostnames: &BTreeSet<(String, String)>) -> Result<(), std::io::Error> { + let mut hosts_str = match File::open("/etc/hosts") { + Ok(mut f) => { + let mut r = String::new(); + f.read_to_string(&mut r)?; + r + } + Err(e) => match e.kind() { + ErrorKind::NotFound => "".to_owned(), + _ => return Err(e), + }, + }; + + let seen_hostnames: BTreeSet = hosts_str + .lines() + .map(|l| { + l.split_whitespace() + .take_while(|dom| dom.chars().next().unwrap() != '#') + .skip(1) + }) + .flatten() + .map(|dom| dom.to_owned()) + .collect(); + + for (ip, dom) in hostnames { + if !seen_hostnames.contains(dom.as_str()) { + hosts_str.push_str(ip); + hosts_str.push(' '); + hosts_str.push_str(&format!("{dom}")); + hosts_str.push('\n'); + } + } + + info!("Syncing host file"); + + let mut f = AtomicWriteFile::open("/etc/hosts")?; + f.write(hosts_str.as_bytes())?; + f.commit()?; + + Ok(()) +} -- cgit v1.2.3-70-g09d2