From 8e6318f29eb022126e82e0c084262c77d9ee3300 Mon Sep 17 00:00:00 2001 From: Hristo Venev Date: Sat, 28 Sep 2019 16:21:28 +0000 Subject: Implement peer overrides. --- src/config.rs | 12 ++++- src/main.rs | 4 +- src/manager/builder.rs | 119 ++++++++++++++++++++++++++++++++----------------- src/manager/mod.rs | 46 +++++++++---------- src/model.rs | 11 +---- src/wg.rs | 4 +- 6 files changed, 119 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/config.rs b/src/config.rs index ccdb042..08db0eb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,6 +18,13 @@ pub struct Source { pub required: bool, } +#[serde(deny_unknown_fields)] +#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Eq, Debug)] +pub struct Peer { + pub source: Option, + pub psk: Option, +} + #[serde(deny_unknown_fields)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Eq, Debug)] pub struct GlobalConfig { @@ -25,6 +32,8 @@ pub struct GlobalConfig { pub min_keepalive: u32, #[serde(default = "default_max_keepalive")] pub max_keepalive: u32, + #[serde(rename = "peer")] + pub peers: HashMap, } impl Default for GlobalConfig { @@ -33,6 +42,7 @@ impl Default for GlobalConfig { Self { min_keepalive: default_min_keepalive(), max_keepalive: default_max_keepalive(), + peers: HashMap::new(), } } } @@ -72,7 +82,7 @@ impl Default for UpdaterConfig { #[serde(deny_unknown_fields)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Default, Clone, Debug)] pub struct Config { - pub runtime_directory: Option, + pub state_directory: Option, #[serde(flatten)] pub global: GlobalConfig, diff --git a/src/main.rs b/src/main.rs index 438cb3c..b6b5ce1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -188,12 +188,12 @@ fn run_with_cmdline(argv0: &str, args: &mut impl Iterator) -> i fn run_daemon(ifname: OsString, mut config: config::Config) -> i32 { maybe_get_var(&mut config.updater.cache_directory, "CACHE_DIRECTORY"); - maybe_get_var(&mut config.runtime_directory, "RUNTIME_DIRECTORY"); + maybe_get_var(&mut config.state_directory, "RUNTIME_DIRECTORY"); let mut m = match manager::Manager::new(ifname, config) { Ok(m) => m, Err(e) => { - eprintln!("<1>Failed to open device: {}", e); + eprintln!("<1>Failed to start: {}", e); return 1; } }; diff --git a/src/manager/builder.rs b/src/manager/builder.rs index 3f3e227..e5fe2da 100644 --- a/src/manager/builder.rs +++ b/src/manager/builder.rs @@ -2,50 +2,56 @@ // // See COPYING. +use super::Source; use crate::{config, model, proto}; use std::collections::hash_map; use std::{error, fmt}; #[derive(Debug)] -pub struct ConfigError { - pub url: String, +pub struct Error { + pub src: String, pub peer: model::Key, - pub important: bool, + important: bool, err: &'static str, } -impl ConfigError { - fn new(err: &'static str, s: &config::Source, p: &proto::Peer, important: bool) -> Self { +impl Error { + fn new(err: &'static str, src: &Source, p: &proto::Peer, important: bool) -> Self { Self { - url: s.url.clone(), - peer: p.public_key.clone(), + src: src.name.clone(), + peer: p.public_key, important, err, } } + + #[inline] + pub fn important(&self) -> bool { + self.important + } } -impl error::Error for ConfigError {} -impl fmt::Display for ConfigError { +impl error::Error for Error {} +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "{} [{}] from [{}]: {}", + "{} [{}]/[{}]: {}", if self.important { "Invalid peer" } else { "Misconfigured peer" }, + self.src, self.peer, - self.url, self.err ) } } -pub struct ConfigBuilder<'a> { +pub(super) struct ConfigBuilder<'a> { c: model::Config, - err: Vec, + err: Vec, public_key: model::Key, gc: &'a config::GlobalConfig, } @@ -54,7 +60,7 @@ impl<'a> ConfigBuilder<'a> { #[inline] pub fn new(public_key: model::Key, gc: &'a config::GlobalConfig) -> Self { Self { - c: model::Config::default(), + c: model::Config::empty(), err: vec![], public_key, gc, @@ -62,32 +68,48 @@ impl<'a> ConfigBuilder<'a> { } #[inline] - pub fn build(self) -> (model::Config, Vec) { + pub fn build(self) -> (model::Config, Vec) { (self.c, self.err) } #[inline] - pub fn add_server(&mut self, s: &config::Source, p: &proto::Server) { + pub fn add_server(&mut self, src: &Source, p: &proto::Server) { + let gc = self.gc; + + let psk = match find_psk(gc, src, &p.peer) { + Ok(v) => v, + Err(e) => { + self.err.push(e); + return; + } + }; + if p.peer.public_key == self.public_key { return; } - let gc = self.gc; - let ent = insert_peer(&mut self.c, &mut self.err, s, &p.peer, |ent| { - ent.psk = s.psk.clone(); - ent.endpoint = Some(p.endpoint.clone()); + let ent = insert_peer(&mut self.c, &mut self.err, src, &p.peer, psk, |ent| { + ent.endpoint = Some(p.endpoint); ent.keepalive = gc.fix_keepalive(p.keepalive); }); - add_peer(&mut self.err, ent, s, &p.peer) + add_peer(&mut self.err, ent, src, &p.peer) } #[inline] - pub fn add_road_warrior(&mut self, s: &config::Source, p: &proto::RoadWarrior) { + pub fn add_road_warrior(&mut self, src: &Source, p: &proto::RoadWarrior) { + let psk = match find_psk(&self.gc, src, &p.peer) { + Ok(v) => v, + Err(e) => { + self.err.push(e); + return; + } + }; + if p.peer.public_key == self.public_key { - self.err.push(ConfigError::new( + self.err.push(Error::new( "The local peer cannot be a road warrior", - s, + src, &p.peer, true, )); @@ -95,35 +117,36 @@ impl<'a> ConfigBuilder<'a> { } let ent = if p.base == self.public_key { - insert_peer(&mut self.c, &mut self.err, s, &p.peer, |_| {}) + insert_peer(&mut self.c, &mut self.err, src, &p.peer, psk, |_| {}) } else if let Some(ent) = self.c.peers.get_mut(&p.base) { ent } else { self.err - .push(ConfigError::new("Unknown base peer", s, &p.peer, true)); + .push(Error::new("Unknown base peer", src, &p.peer, true)); return; }; - add_peer(&mut self.err, ent, s, &p.peer) + add_peer(&mut self.err, ent, src, &p.peer) } } #[inline] fn insert_peer<'b>( c: &'b mut model::Config, - err: &mut Vec, - s: &config::Source, + err: &mut Vec, + src: &Source, p: &proto::Peer, + psk: Option<&model::Key>, update: impl for<'c> FnOnce(&'c mut model::Peer) -> (), ) -> &'b mut model::Peer { - match c.peers.entry(p.public_key.clone()) { + match c.peers.entry(p.public_key) { hash_map::Entry::Occupied(ent) => { - err.push(ConfigError::new("Duplicate public key", s, p, true)); + err.push(Error::new("Duplicate public key", src, p, true)); ent.into_mut() } hash_map::Entry::Vacant(ent) => { let ent = ent.insert(model::Peer { endpoint: None, - psk: None, + psk: psk.cloned(), keepalive: 0, ipv4: vec![], ipv6: vec![], @@ -134,17 +157,33 @@ fn insert_peer<'b>( } } -fn add_peer( - err: &mut Vec, - ent: &mut model::Peer, - s: &config::Source, +fn find_psk<'a>( + gc: &'a config::GlobalConfig, + src: &'a Source, p: &proto::Peer, -) { +) -> Result, Error> { + let want = gc.peers.get(&p.public_key); + let want = if let Some(v) = want { + v + } else { + return Ok(None); + }; + + if let Some(ref want_src) = &want.source { + if *want_src != src.name { + return Err(Error::new("Peer source not allowed", src, p, true)); + } + } + + Ok(want.psk.as_ref().or_else(|| src.config.psk.as_ref())) +} + +fn add_peer(err: &mut Vec, ent: &mut model::Peer, src: &Source, p: &proto::Peer) { let mut added = false; let mut removed = false; for i in &p.ipv4 { - if s.ipv4.contains(i) { + if src.config.ipv4.contains(i) { ent.ipv4.push(*i); added = true; } else { @@ -152,7 +191,7 @@ fn add_peer( } } for i in &p.ipv6 { - if s.ipv6.contains(i) { + if src.config.ipv6.contains(i) { ent.ipv6.push(*i); added = true; } else { @@ -166,6 +205,6 @@ fn add_peer( } else { "All IPs removed" }; - err.push(ConfigError::new(msg, s, p, !added)); + err.push(Error::new(msg, src, p, !added)); } } diff --git a/src/manager/mod.rs b/src/manager/mod.rs index 680cbad..78d579f 100644 --- a/src/manager/mod.rs +++ b/src/manager/mod.rs @@ -3,26 +3,13 @@ // See COPYING. use crate::{config, model, proto, wg}; -use std::ffi::{OsString}; +use std::ffi::OsString; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; use std::path::{Path, PathBuf}; use std::time::{Duration, Instant, SystemTime}; use std::{fs, io}; -struct Source { - name: String, - config: config::Source, - data: proto::Source, - next_update: Instant, - backoff: Option, -} - -mod builder; - -mod updater; -pub use updater::load_source; - fn update_file(path: &Path, data: &[u8]) -> io::Result<()> { let mut tmp_path = OsString::from(path); tmp_path.push(".tmp"); @@ -65,23 +52,36 @@ fn load_file(path: &Path) -> io::Result>> { Ok(Some(data)) } +struct Source { + name: String, + config: config::Source, + data: proto::Source, + next_update: Instant, + backoff: Option, +} + +mod updater; +pub use updater::load_source; + +mod builder; + pub struct Manager { dev: wg::Device, global_config: config::GlobalConfig, sources: Vec, current: model::Config, - runtime_directory: Option, + state_directory: Option, updater: updater::Updater, } impl Manager { pub fn new(ifname: OsString, c: config::Config) -> io::Result { let mut m = Self { - dev: wg::Device::new(ifname)?, + dev: wg::Device::open(ifname)?, global_config: c.global, sources: vec![], - current: model::Config::default(), - runtime_directory: c.runtime_directory, + current: model::Config::empty(), + state_directory: c.state_directory, updater: updater::Updater::new(c.updater), }; @@ -95,7 +95,7 @@ impl Manager { } fn state_path(&self) -> Option { - let mut path = if let Some(ref path) = self.runtime_directory { + let mut path = if let Some(ref path) = self.state_directory { path.clone() } else { return None; @@ -191,7 +191,7 @@ impl Manager { &self, public_key: model::Key, ts: SystemTime, - ) -> (model::Config, Vec, SystemTime) { + ) -> (model::Config, Vec, SystemTime) { let mut t_cfg = ts + Duration::from_secs(1 << 20); let mut sources: Vec<(&Source, &proto::SourceConfig)> = vec![]; for src in &self.sources { @@ -215,13 +215,13 @@ impl Manager { for (src, sc) in &sources { for peer in &sc.servers { - cfg.add_server(&src.config, peer); + cfg.add_server(src, peer); } } for (src, sc) in &sources { for peer in &sc.road_warriors { - cfg.add_road_warrior(&src.config, peer); + cfg.add_road_warrior(src, peer); } } @@ -259,7 +259,7 @@ impl Manager { if config != self.current { eprintln!("<5>Applying configuration update"); for err in &errors { - eprintln!("<{}>{}", if err.important { '4' } else { '5' }, err); + eprintln!("<{}>{}", if err.important() { '4' } else { '5' }, err); } self.dev.apply_diff(&self.current, &config)?; self.current_update(&config); diff --git a/src/model.rs b/src/model.rs index 51386a5..90f9b44 100644 --- a/src/model.rs +++ b/src/model.rs @@ -15,7 +15,7 @@ pub use ip::*; pub type KeyParseError = base64::DecodeError; -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Key([u8; 32]); impl Key { @@ -82,7 +82,7 @@ impl<'de> serde::Deserialize<'de> for Key { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Endpoint { address: Ipv6Addr, port: u16, @@ -195,13 +195,6 @@ pub struct Config { pub peers: HashMap, } -impl Default for Config { - #[inline] - fn default() -> Self { - Self::empty() - } -} - impl Config { #[inline] pub fn empty() -> Self { diff --git a/src/wg.rs b/src/wg.rs index c3eeb57..7565a02 100644 --- a/src/wg.rs +++ b/src/wg.rs @@ -13,7 +13,7 @@ pub struct Device { impl Device { #[inline] - pub fn new(ifname: OsString) -> io::Result { + pub fn open(ifname: OsString) -> io::Result { let dev = Self { ifname }; let _ = dev.get_public_key()?; Ok(dev) @@ -67,7 +67,7 @@ impl Device { if *old_peer == *conf { continue; } - old_endpoint = old_peer.endpoint.clone(); + old_endpoint = old_peer.endpoint; } else { old_endpoint = None; } -- cgit