summaryrefslogtreecommitdiff
path: root/src/daemon_config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon_config.rs')
-rw-r--r--src/daemon_config.rs121
1 files changed, 121 insertions, 0 deletions
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<Key>,
+ pub ips: Vec<(IpAddrMask, Option<String>)>,
+ // if false: the hostnames are kept around for sharing, but we personally do not use them
+ pub use_hostnames: bool,
+ pub endpoint: Option<Endpoint>,
+
+ pub last_changed: SystemTime,
+ pub known_to: Vec<usize>,
+
+ 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<Key, PeerConfig>,
+
+ pub mäsch_port: u16,
+}
+
+#[derive(Serialize, Deserialize, Default)]
+pub struct Config {
+ pub networks: HashMap<String, Network>,
+}
+
+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<String> = 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(())
+}