diff --git a/Cargo.toml b/Cargo.toml index 8de8f7c..8a303c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "vopono" description = "Launch applications via VPN tunnels using temporary network namespaces" -version = "0.8.3" +version = "0.8.4" authors = ["James McMurray "] edition = "2018" license = "GPL-3.0-or-later" @@ -34,7 +34,7 @@ chrono = "0.4" compound_duration = "1" ipnet = {version = "2", features = ["serde"]} reqwest = {default-features = false, version = "0.11", features = ["blocking", "json", "rustls-tls"]} -sysinfo = "0.19" +sysinfo = "0.20" base64 = "0.13" x25519-dalek = "1" strum = "0.21" @@ -49,6 +49,12 @@ config = "0.11" [package.metadata.rpm] package = "vopono" +[package.metadata.deb] +maintainer = "James McMurray " +recommends = "wireguard-tools, openvpn" +# Not supported in Github Actions image with cargo-deb yet +# suggests = "shadowsocks-libev, openfortivpn" + [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/src/netns.rs b/src/netns.rs index 3f4618e..e667cd6 100644 --- a/src/netns.rs +++ b/src/netns.rs @@ -154,7 +154,7 @@ impl NetworkNamespace { // TODO: Handle if name taken? let source = format!("{}_s", &self.name[7..self.name.len().min(20)]); let dest = format!("{}_d", &self.name[7..self.name.len().min(20)]); - self.veth_pair = Some(VethPair::new(source, dest, &self)?); + self.veth_pair = Some(VethPair::new(source, dest, self)?); Ok(()) } @@ -230,7 +230,7 @@ impl NetworkNamespace { disable_ipv6: bool, ) -> anyhow::Result<()> { self.openvpn = Some(OpenVpn::run( - &self, + self, config_file, auth_file, dns, @@ -252,7 +252,7 @@ impl NetworkNamespace { server: &str, ) -> anyhow::Result<()> { self.openconnect = Some(OpenConnect::run( - &self, + self, config_file, open_ports, forward_ports, @@ -288,7 +288,7 @@ impl NetworkNamespace { encrypt_method: &str, ) -> anyhow::Result<()> { self.shadowsocks = Some(Shadowsocks::run( - &self, + self, config_file, ss_host, listen_port, @@ -425,7 +425,7 @@ impl Drop for NetworkNamespace { std::env::set_var("VOPONO_NS", &self.name); if self.predown_user.is_some() { std::process::Command::new("sudo") - .args(&["-Eu", self.predown_user.as_ref().unwrap(), &pdcmd]) + .args(&["-Eu", self.predown_user.as_ref().unwrap(), pdcmd]) .spawn() .ok(); } else { diff --git a/src/openconnect.rs b/src/openconnect.rs index 016b3a0..3126cda 100644 --- a/src/openconnect.rs +++ b/src/openconnect.rs @@ -52,12 +52,12 @@ impl OpenConnect { // Allow input to and output from open ports (for port forwarding in tunnel) if let Some(opens) = open_ports { - super::util::open_ports(&netns, opens.as_slice(), firewall)?; + super::util::open_ports(netns, opens.as_slice(), firewall)?; } // Allow input to and output from forwarded ports if let Some(forwards) = forward_ports { - super::util::open_ports(&netns, forwards.as_slice(), firewall)?; + super::util::open_ports(netns, forwards.as_slice(), firewall)?; } Ok(Self { pid: id }) diff --git a/src/openfortivpn.rs b/src/openfortivpn.rs index f9cdd97..ca73529 100644 --- a/src/openfortivpn.rs +++ b/src/openfortivpn.rs @@ -99,12 +99,12 @@ impl OpenFortiVpn { netns.dns_config(dns_ip.as_slice(), suffixes.as_slice())?; // Allow input to and output from open ports (for port forwarding in tunnel) if let Some(opens) = open_ports { - super::util::open_ports(&netns, opens.as_slice(), firewall)?; + super::util::open_ports(netns, opens.as_slice(), firewall)?; } // Allow input to and output from forwarded ports if let Some(forwards) = forward_ports { - super::util::open_ports(&netns, forwards.as_slice(), firewall)?; + super::util::open_ports(netns, forwards.as_slice(), firewall)?; } Ok(Self { pid: id }) diff --git a/src/openvpn.rs b/src/openvpn.rs index 5e7d1f3..5a7319c 100644 --- a/src/openvpn.rs +++ b/src/openvpn.rs @@ -134,12 +134,12 @@ impl OpenVpn { // Allow input to and output from open ports (for port forwarding in tunnel) if let Some(opens) = open_ports { - super::util::open_ports(&netns, opens.as_slice(), firewall)?; + super::util::open_ports(netns, opens.as_slice(), firewall)?; } // Allow input to and output from forwarded ports (will be proxied to host) if let Some(forwards) = forward_ports { - super::util::open_ports(&netns, forwards.as_slice(), firewall)?; + super::util::open_ports(netns, forwards.as_slice(), firewall)?; } if use_killswitch { diff --git a/src/providers/ivpn/wireguard.rs b/src/providers/ivpn/wireguard.rs index 924bb6a..687eedb 100644 --- a/src/providers/ivpn/wireguard.rs +++ b/src/providers/ivpn/wireguard.rs @@ -254,7 +254,7 @@ fn request_port() -> anyhow::Result { let port = Input::::new() .with_prompt("Enter port number:") .validate_with(|x: &u16| -> Result<(), &str> { - if [2049,2050,53,30587,41893,48574,58237].contains(&x) { + if [2049,2050,53,30587,41893,48574,58237].contains(x) { Ok(()) } else { Err("Port must be one of: 2049,2050,53,30587,41893,48574,58237 (see https://www.ivpn.net/setup/gnu-linux-wireguard.html for ports reference)") diff --git a/src/providers/mozilla/wireguard.rs b/src/providers/mozilla/wireguard.rs index e6abc29..4d8f6ad 100644 --- a/src/providers/mozilla/wireguard.rs +++ b/src/providers/mozilla/wireguard.rs @@ -52,7 +52,7 @@ impl MozillaVPN { .with_prompt( "The following devices exist on your account, which would you like to use (you will need the private key)", ) - .items(&devices) + .items(devices) .item("Create a new device (keypair)") .default(0) .interact()?; diff --git a/src/providers/mullvad/wireguard.rs b/src/providers/mullvad/wireguard.rs index a05b832..050d6b4 100644 --- a/src/providers/mullvad/wireguard.rs +++ b/src/providers/mullvad/wireguard.rs @@ -179,7 +179,7 @@ fn prompt_for_wg_key( return Err(anyhow!("Cannot add more Wireguard keypairs to this account. Try to delete existing keypairs.")); } let keypair = generate_keypair()?; - Mullvad::upload_wg_key(&client, auth_token, &keypair)?; + Mullvad::upload_wg_key(client, auth_token, &keypair)?; Ok(keypair) } else { let private_key = Input::::new() diff --git a/src/providers/nordvpn/openvpn.rs b/src/providers/nordvpn/openvpn.rs index 764fb4e..38c1ba4 100644 --- a/src/providers/nordvpn/openvpn.rs +++ b/src/providers/nordvpn/openvpn.rs @@ -82,7 +82,7 @@ impl OpenVpnProvider for NordVPN { let server_name = fname.to_lowercase().replace(' ', "_"); let server_name = server_name.split('.').next().unwrap(); - if let Some(cap) = server_regex.captures(&server_name) { + if let Some(cap) = server_regex.captures(server_name) { // check whether the server is a special config type // or not, and discard ones not in line with user's // selection diff --git a/src/veth_pair.rs b/src/veth_pair.rs index 34f749b..a5878e4 100644 --- a/src/veth_pair.rs +++ b/src/veth_pair.rs @@ -1,7 +1,7 @@ use super::util::sudo_command; use super::NetworkNamespace; use anyhow::Context; -use log::debug; +use log::{debug, warn}; use serde::{Deserialize, Serialize}; use std::fs::OpenOptions; use std::io::Write; @@ -26,11 +26,29 @@ impl VethPair { assert!(source.len() <= 15, "ifname must be <= 15 chars: {}", source); assert!(dest.len() <= 15, "ifname must be <= 15 chars: {}", dest); + // NetworkManager device management // If NetworkManager used, add destination veth to unmanaged devices // Avoids NM overriding our IP assignment - // TODO: Check with systemd? + // TODO: Check with systemd instead of nmcli directly? let nm_path = PathBuf::from_str("/etc/NetworkManager")?; - let nm_unmanaged = if nm_path.exists() && which::which("nmcli").is_ok() { + let nm_running = if which::which("nmcli").is_ok() { + std::process::Command::new("nmcli") + .arg("general") + .arg("status") + .status() + .map(|x| x.success()) + .unwrap_or(false) + } else { + false + }; + + if nm_running { + debug!("Detected NetworkManager running"); + } else { + debug!("NetworkManager not detected running"); + } + + let nm_unmanaged = if nm_path.exists() && nm_running { debug!( "NetworkManager detected, adding {} to unmanaged devices", &dest @@ -72,13 +90,46 @@ impl VethPair { )?; } - sudo_command(&["nmcli", "connection", "reload"]) - .context("Failed to reload NetworkManager configuration")?; + if let Err(e) = sudo_command(&["nmcli", "connection", "reload"]) + .context("Failed to reload NetworkManager configuration") + { + warn!("Tried but failed to reload NetworkManager configuration - is NetworkManager running? : {}", e); + } Some(NetworkManagerUnmanaged { backup_file }) } else { None }; + // systemd firewalld device management + let firewalld_running = std::process::Command::new("systemctl") + .arg("is-active") + .arg("firewalld") + .output() + .map(|x| String::from_utf8(x.stdout).ok().unwrap().trim() == "active") + .unwrap_or(false); + + if firewalld_running { + debug!("Detected firewalld running"); + } else { + debug!("firewalld not detected running"); + } + + if firewalld_running && which::which("firewall-cmd").is_ok() { + debug!( + "Detected firewalld running, adding {} veth device to trusted zone", + dest + ); + // Permit new interface + match std::process::Command::new("firewall-cmd") + .arg("--zone=trusted") + .arg(format!("--add-interface={}", dest).as_str()) + .status().map(|x| x.success()) { + Err(e) => warn!("Failed to add veth device {} to firewalld trusted zone, error: {}", dest, e), + Ok(false) => warn!("Possibly failed to add veth device {} to firewalld trusted zone (non-zero exit code)", dest), + _ => {} + } + } + sudo_command(&[ "ip", "link", diff --git a/src/wireguard.rs b/src/wireguard.rs index b44c40a..6f17f0b 100644 --- a/src/wireguard.rs +++ b/src/wireguard.rs @@ -123,7 +123,7 @@ impl Wireguard { let dns = dns.unwrap_or(&config.interface.dns); // TODO: DNS suffixes? - namespace.dns_config(&dns, &[])?; + namespace.dns_config(dns, &[])?; let fwmark = "51820"; namespace.exec(&["wg", "set", &if_name, "fwmark", fwmark])?; @@ -321,12 +321,12 @@ impl Wireguard { // Allow input to and output from open ports (for port forwarding in tunnel) if let Some(opens) = open_ports { - super::util::open_ports(&namespace, opens.as_slice(), firewall)?; + super::util::open_ports(namespace, opens.as_slice(), firewall)?; } // Allow input to and output from forwarded ports if let Some(forwards) = forward_ports { - super::util::open_ports(&namespace, forwards.as_slice(), firewall)?; + super::util::open_ports(namespace, forwards.as_slice(), firewall)?; } if use_killswitch { @@ -443,7 +443,7 @@ impl Drop for Wireguard { "ip", "link", "del", - &if_name, + if_name, ]) { Ok(_) => {} Err(e) => warn!("Failed to delete ip link {}: {:?}", &self.ns_name, e),