summaryrefslogtreecommitdiff
path: root/src/daemon.rs
diff options
context:
space:
mode:
authorLia Lenckowski <lialenck@protonmail.com>2024-08-13 21:09:22 +0200
committerLia Lenckowski <lialenck@protonmail.com>2024-08-13 21:09:22 +0200
commitc7369246ad3b339be9607f7018f8764ee320bd20 (patch)
tree8f0254c78d7f1eb21af879272a7ed95560be9772 /src/daemon.rs
parent8aa9f19e313b985aa13a52270a9dc77c19d8ce53 (diff)
downloadmaesch-c7369246ad3b339be9607f7018f8764ee320bd20.tar
maesch-c7369246ad3b339be9607f7018f8764ee320bd20.tar.bz2
maesch-c7369246ad3b339be9607f7018f8764ee320bd20.tar.zst
sync hosts more safely; refactor: split off config
Diffstat (limited to 'src/daemon.rs')
-rw-r--r--src/daemon.rs157
1 files changed, 23 insertions, 134 deletions
diff --git a/src/daemon.rs b/src/daemon.rs
index bf6105c..57667eb 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -1,33 +1,23 @@
-use core::net::SocketAddr;
-use dbus::{channel::MatchingReceiver, message::MatchRule};
-use dbus_crossroads::{Context, Crossroads, MethodErr};
-use defguard_wireguard_rs::{
- host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
-};
-use log::{debug, error, info, warn};
-use serde::{Deserialize, Serialize};
+use dbus::channel::MatchingReceiver;
+use defguard_wireguard_rs::{net::IpAddrMask, WGApi, WireguardInterfaceApi};
+use log::{debug, error, info};
use std::{
collections::{BTreeSet, HashMap},
- fs::File,
- io::{ErrorKind, Read, Write},
- marker::PhantomData,
- net::ToSocketAddrs,
ops::DerefMut,
+ path::PathBuf,
str::FromStr,
sync::Arc,
- time::SystemTime,
};
use thiserror::Error;
use tokio::{
net::TcpListener,
runtime::Builder,
signal::unix::{signal, SignalKind},
- sync::{broadcast, RwLock},
+ sync::RwLock,
task,
};
-use xdg::BaseDirectories;
-use crate::{daemon_dbus::*, daemon_network::*};
+use crate::{daemon_config::*, daemon_dbus::*, daemon_network::*};
#[derive(Debug, Error)]
pub enum DaemonError {
@@ -51,53 +41,10 @@ pub enum DaemonError {
DbusError(#[from] dbus::Error),
}
-#[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>,
-}
-
-// TODO das überschreibt änderungen an /etc/hosts während der runtime :(
pub struct State {
pub conf: Config,
pub nw_handles: HashMap<String, (WGApi, task::JoinHandle<()>)>,
- pub hostfile: Option<(String, BTreeSet<String>)>,
+ pub hostnames: BTreeSet<(String, String)>,
}
impl Drop for State {
@@ -109,60 +56,31 @@ impl Drop for State {
}
pub fn daemon() -> Result<(), 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)?,
- },
- };
+ let (config_path, config) = load_config()?;
info!("read config");
- let 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
- }
- };
-
+ // NOTE this should not be upgraded to a multi-writer structure carelessly, as we also use the
+ // exclusivity of write locks to ensure only one thread at a time syncs the config to disk
let state = Arc::new(RwLock::new(State {
conf: config,
nw_handles: HashMap::new(),
- hostfile,
+ hostnames: BTreeSet::new(),
}));
let rt = Builder::new_current_thread().enable_all().build()?;
- rt.block_on(run_networks(state))?;
+ rt.block_on(run_networks(state, config_path))?;
Ok(())
}
-async fn run_networks(state: Arc<RwLock<State>>) -> Result<(), DaemonError> {
+async fn run_networks(state: Arc<RwLock<State>>, config_path: PathBuf) -> Result<(), DaemonError> {
let mut state_rw_guard = state.write().await;
let state_rw = state_rw_guard.deref_mut();
// load existing configurations
+ let mut hostname_pairs = BTreeSet::new();
for (name, nw) in &state_rw.conf.networks {
- let wg_api = add_network(
- &mut state_rw.hostfile,
+ let (wg_api, mut new_hostnames) = add_network(
name.clone(),
nw.privkey.clone(),
nw.address.clone(),
@@ -171,6 +89,8 @@ async fn run_networks(state: Arc<RwLock<State>>) -> Result<(), DaemonError> {
)
.await?;
+ hostname_pairs.append(&mut new_hostnames);
+
let addr = IpAddrMask::from_str(&nw.address)?.ip;
let h = task::spawn(print_error(run_network(
state.clone(),
@@ -182,46 +102,15 @@ async fn run_networks(state: Arc<RwLock<State>>) -> Result<(), DaemonError> {
debug!("loaded configuration for {0}", name);
}
+ match sync_hostnames(&hostname_pairs) {
+ Ok(_) => (),
+ Err(e) => error!("Failed to sync hostnames to disk: {e}"),
+ };
info!("loaded all existing configurations");
drop(state_rw_guard);
// set up dbus interface
- let mut cr = Crossroads::new();
- let state_ref = state.clone();
- let if_token = cr.register("de.a.maesch", move |b| {
- b.signal::<(String, String), _>("Proposal", ("network", "peer_data"));
- b.method_with_cr_async(
- "AddNetwork",
- ("name", "key", "ip", "listen_port", "maesch_port"),
- ("success",),
- move |ctx, _, args: (String, String, String, u16, u16)| {
- debug!("Received AddNetwork");
- handle_add_network(ctx, state_ref.clone(), args)
- },
- );
- });
- cr.insert("/de/a/maesch", &[if_token], ());
-
- // drive dbus interface
- let (res, c) = dbus_tokio::connection::new_system_sync()?;
- cr.set_async_support(Some((
- c.clone(),
- Box::new(|x| {
- tokio::spawn(x);
- }),
- )));
- let _ = tokio::spawn(print_error(async {
- res.await;
- Result::<!, &'static str>::Err("lost connection to dbus!")
- }));
- let receive_token = c.start_receive(
- MatchRule::new_method_call(),
- Box::new(move |msg, conn| {
- cr.handle_message(msg, conn).unwrap();
- true
- }),
- );
- c.request_name("de.a.maesch", false, true, false).await?;
+ let (c, recv_token) = start_dbus(state.clone(), config_path).await?;
// wait for SIGTERM/SIGINT
let mut sigterm_fut = signal(SignalKind::terminate())?;
@@ -235,7 +124,7 @@ async fn run_networks(state: Arc<RwLock<State>>) -> Result<(), DaemonError> {
};
// clean exit
- c.stop_receive(receive_token);
+ c.stop_receive(recv_token);
let mut state_rw_guard = state.write().await;
for (_, (wg_api, h)) in state_rw_guard.nw_handles.drain() {
let _ = wg_api.remove_interface();