From 7f844a2f5d67f788c3b2084fb3ab0c25b10928cc Mon Sep 17 00:00:00 2001 From: Lyn Date: Sat, 18 Jan 2025 03:08:30 +0100 Subject: [PATCH] add allowed_ips config option for peers, formatting --- README.md | 2 ++ src/main.rs | 65 +++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ee45db0..8a03226 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ address = "10.14.1.2" endpoint = "77.207.15.215" # (Optional) endpoint port port = 33722 +# (Optional) Subnets allowed to be routed through this peer. If no value is given, this defaults to only the peers address itself. +allowed_ips = ["10.14.1.2/32","192.168.0.0/16"] [[peers]] interface = "wg0" diff --git a/src/main.rs b/src/main.rs index e47deaf..ed335e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use std::thread; use std::time::Duration; use anyhow::{anyhow, bail, Result}; +use ipnet::{IpNet, Ipv4Net, Ipv6Net}; use log::*; use serde::{Deserialize, Serialize}; @@ -58,7 +59,7 @@ struct Config { interfaces: Vec, #[serde(default)] - forbidden_nets: Vec, + forbidden_nets: Vec, } #[derive(Deserialize)] @@ -73,6 +74,9 @@ struct Peer { port: Option, /// An optional Wireguard endpoint used to initialize a connection to this peer endpoint: Option, + /// Subnets allowed to be routed through this peer. Defaults to only the IP of the peer itself if no value is given. + #[serde(default)] + allowed_ips: Vec, } /// Settings for Wireguard interfaces #[derive(Deserialize)] @@ -101,6 +105,15 @@ fn main() -> Result<()> { let config_str = std::fs::read_to_string(config_path)?; toml::from_str(&config_str)? }; + for peer in &mut config.peers { + if peer.allowed_ips.is_empty() { + let ip_net = match peer.address { + IpAddr::V4(ipv4) => IpNet::V4(Ipv4Net::new(ipv4, 32)?), + IpAddr::V6(ipv6) => IpNet::V6(Ipv6Net::new(ipv6, 128)?), + }; + peer.allowed_ips.push(ip_net); + } + } if let Some(f) = &config.gossip_secret_file { if config.gossip_secret.is_some() { @@ -471,15 +484,21 @@ impl Daemon { }); } } - fn generate_portmaps(&self) -> (HashMap, Vec) { + fn generate_portmaps(&self) -> (HashMap, Vec) { let mut portmap_v4: HashMap = HashMap::new(); //collect interfaces that have peers with IPv4 addresses (-> IPv4 port mappings need to exist) let binding = self.state.lock().unwrap(); let interfaces = binding.interfaces.iter(); - let v4ifs: Vec<_> = binding.interfaces.iter() + let v4ifs: Vec<_> = binding + .interfaces + .iter() .filter_map(|(ifname, ifinfo)| { if ifinfo.listen_port != 0 - && self.config.peers.iter().any(|peer| peer.interface == **ifname && peer.address.is_ipv4()) + && self + .config + .peers + .iter() + .any(|peer| peer.interface == **ifname && peer.address.is_ipv4()) { Some((ifname, ifinfo)) } else { @@ -490,9 +509,8 @@ impl Daemon { //create portmap for ifsetting in &self.config.interfaces { if let Some(external_port) = ifsetting.upnp_ext_port_v4 { - if let Some((_ifname, ifinfo)) = v4ifs - .iter() - .find(|(ifname, _)| **ifname == ifsetting.name) + if let Some((_ifname, ifinfo)) = + v4ifs.iter().find(|(ifname, _)| **ifname == ifsetting.name) { portmap_v4.insert(ifinfo.listen_port, external_port); } @@ -501,16 +519,16 @@ impl Daemon { //collect ports of interfaces that have peers with IPv6 addresses (-> pinholes for the IPv6 listen ports should be created) let v6ports: Vec = interfaces .filter(|(ifname, ifinfo)| { - ifinfo.listen_port!=0 - && self.config - .peers - .iter() - .any(|peer| peer.interface == **ifname && peer.address.is_ipv6()) + ifinfo.listen_port != 0 + && self + .config + .peers + .iter() + .any(|peer| peer.interface == **ifname && peer.address.is_ipv6()) }) - .map(|(_, ifinfo)|ifinfo.listen_port) + .map(|(_, ifinfo)| ifinfo.listen_port) .collect(); (portmap_v4, v6ports) - } fn make_packet(&self, gossip: &Gossip) -> Result> { @@ -674,7 +692,10 @@ impl State { // if peer is connected and endpoint is the correct one, // set higher keepalive and then skip reconfiguring it - if !bad_endpoint && peer.last_seen != u64::MAX && !forbidden_endpoint && now < peer.last_seen + TIMEOUT.as_secs() + if !bad_endpoint + && peer.last_seen != u64::MAX + && !forbidden_endpoint + && now < peer.last_seen + TIMEOUT.as_secs() { Command::new("wg") .args([ @@ -754,7 +775,12 @@ impl State { "persistent-keepalive", "10", "allowed-ips", - "::/0,0.0.0.0/0", + &peer_cfg + .allowed_ips + .iter() + .map(|ip| ip.to_string()) + .collect::>() + .join(","), ]) .output()?; let packet = daemon.make_packet(&Gossip::Ping)?; @@ -772,7 +798,12 @@ impl State { "peer", &peer_cfg.pubkey, "allowed-ips", - "::/0,0.0.0.0/0", + &peer_cfg + .allowed_ips + .iter() + .map(|ip| ip.to_string()) + .collect::>() + .join(","), ]) .output()?; }