Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to Clap v3 #136

Merged
merged 2 commits into from
Feb 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ keywords = ["vopono", "vpn", "wireguard", "openvpn", "netns"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
structopt = "0.3"
anyhow = "1"
directories-next = "2"
log = "0.4"
pretty_env_logger = "0.4"
clap = "2"
clap = {version = "3", features = ["derive"]}
which = "4"
users = "0.11"
nix = "0.23"
Expand Down
5 changes: 5 additions & 0 deletions USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ $ vopono exec --provider mullvad --server sweden --protocol wireguard "transmiss
The server prefix will be searched against available servers (and
country names) and a random one will be chosen (and reported in the terminal).

Note that vopono expects the `AllowedIPs` setting to allow all traffic,
since all traffic in the vopono network namespace will be forced through
this tunnel (traffic via the host is deliberately blocked to enforce the
killswitch). e.g. it should be `AllowedIPs = 0.0.0.0/0,::/0`

#### Custom Settings

The sync menu will prompt you for any custom settings (i.e. ports used,
Expand Down
76 changes: 38 additions & 38 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,159 +2,159 @@ use super::firewall::Firewall;
use super::network_interface::NetworkInterface;
use super::providers::VpnProvider;
use super::vpn::Protocol;
use clap::Parser;
use std::net::IpAddr;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(
#[derive(Parser)]
#[clap(
name = "vopono",
about = "Launch applications in a temporary VPN network namespace"
)]
pub struct App {
/// Verbose output
#[structopt(short = "v", long = "verbose")]
#[clap(short = 'v', long = "verbose")]
pub verbose: bool,

/// read sudo password from program specified in SUDO_ASKPASS environment variable
#[structopt(short = "A", long = "askpass")]
#[clap(short = 'A', long = "askpass")]
pub askpass: bool,

#[structopt(subcommand)]
#[clap(subcommand)]
pub cmd: Command,
}

#[derive(StructOpt)]
#[derive(Parser)]
pub enum Command {
#[structopt(
#[clap(
name = "exec",
about = "Execute an application with the given VPN connection"
)]
Exec(ExecCommand),
#[structopt(
#[clap(
name = "list",
about = "List running vopono namespaces and applications"
)]
List(ListCommand),
#[structopt(
#[clap(
name = "sync",
about = "Synchronise local server lists with VPN providers"
)]
Synch(SynchCommand),
#[structopt(
#[clap(
name = "servers",
about = "List possible server configs for VPN provider, beginning with prefix"
)]
Servers(ServersCommand),
}

#[derive(StructOpt)]
#[derive(Parser)]
pub struct SynchCommand {
/// VPN Provider - will launch interactive menu if not provided
#[structopt(possible_values = &VpnProvider::variants(), case_insensitive = true)]
#[clap(arg_enum, ignore_case = true)]
pub vpn_provider: Option<VpnProvider>,

/// VPN Protocol (if not given will try to sync both)
#[structopt(long = "protocol", short="c", possible_values = &Protocol::variants(), case_insensitive = true)]
#[clap(arg_enum, long = "protocol", short = 'c', ignore_case = true)]
pub protocol: Option<Protocol>,
}

#[derive(StructOpt)]
#[derive(Parser)]
pub struct ExecCommand {
/// VPN Provider (must be given unless using custom config)
#[structopt(long = "provider", short="p", possible_values = &VpnProvider::variants(), case_insensitive = true)]
#[clap(arg_enum, long = "provider", short = 'p', ignore_case = true)]
pub vpn_provider: Option<VpnProvider>,

/// VPN Protocol (if not given will use default)
#[structopt(long = "protocol", short="c", possible_values = &Protocol::variants(), case_insensitive = true)]
#[clap(arg_enum, long = "protocol", short = 'c', ignore_case = true)]
pub protocol: Option<Protocol>,

/// Network Interface (if not given, will use first active network interface)
#[structopt(long = "interface", short = "i", case_insensitive = true)]
#[clap(long = "interface", short = 'i', ignore_case = true)]
pub interface: Option<NetworkInterface>,

/// VPN Server prefix (must be given unless using custom config)
#[structopt(long = "server", short = "s")]
#[clap(long = "server", short = 's')]
pub server: Option<String>,

/// Application to run (should be on PATH or full path to binary)
pub application: String,

/// User with which to run the application (default is current user)
#[structopt(long = "user", short = "u")]
#[clap(long = "user", short = 'u')]
pub user: Option<String>,

/// Custom VPN Provider - OpenVPN or Wireguard config file (will override other settings)
#[structopt(parse(from_os_str), long = "custom")]
#[clap(parse(from_os_str), long = "custom")]
pub custom_config: Option<PathBuf>,

/// DNS Server (will override provider's DNS server)
#[structopt(long = "dns", short = "d")]
#[clap(long = "dns", short = 'd')]
pub dns: Option<Vec<IpAddr>>,

/// Disable killswitch
#[structopt(long = "no-killswitch")]
#[clap(long = "no-killswitch")]
pub no_killswitch: bool,

/// Keep-alive - do not close network namespace when launched process terminates
#[structopt(long = "keep-alive", short = "k")]
#[clap(long = "keep-alive", short = 'k')]
pub keep_alive: bool,

/// List of ports to open on network namespace (to allow port forwarding through the tunnel,
/// e.g. for BitTorrent, etc.)
#[structopt(long = "open-ports", short = "o")]
#[clap(long = "open-ports", short = 'o')]
pub open_ports: Option<Vec<u16>>,

/// List of ports to forward from network namespace to host - useful for running servers and daemons
#[structopt(long = "forward", short = "f")]
#[clap(long = "forward", short = 'f')]
pub forward_ports: Option<Vec<u16>>,

/// Disable proxying to host machine when forwarding ports
#[structopt(long = "no-proxy")]
#[clap(long = "no-proxy")]
pub no_proxy: bool,

/// VPN Protocol (if not given will use default)
#[structopt(long = "firewall", possible_values = &Firewall::variants(), case_insensitive = true)]
#[clap(arg_enum, long = "firewall", ignore_case = true)]
pub firewall: Option<Firewall>,

/// Block all IPv6 traffic
#[structopt(long = "disable-ipv6")]
#[clap(long = "disable-ipv6")]
pub disable_ipv6: bool,

/// Path or alias to executable PostUp script or binary for commands to run on the host after
/// bringing up the namespace
#[structopt(long = "postup")]
#[clap(long = "postup")]
pub postup: Option<String>,

/// Path or alias to executable PreDown script or binary for commands to run on the host after
/// before shutting down the namespace
#[structopt(long = "predown")]
#[clap(long = "predown")]
pub predown: Option<String>,

/// Path to vopono config TOML file (will be created if it does not exist)
/// Default: ~/.config/vopono/config.toml
#[structopt(long = "vopono-config")]
#[clap(long = "vopono-config")]
pub vopono_config: Option<PathBuf>,
}

#[derive(StructOpt)]
#[derive(Parser)]
pub struct ListCommand {
/// VPN Provider
#[structopt(possible_values = &["namespaces", "applications"])]
#[clap(possible_values = &["namespaces", "applications"])]
pub list_type: Option<String>,
}

#[derive(StructOpt)]
#[derive(Parser)]
pub struct ServersCommand {
/// VPN Provider
#[structopt(possible_values = &VpnProvider::variants(), case_insensitive = true)]
#[clap(arg_enum, ignore_case = true)]
pub vpn_provider: VpnProvider,

/// VPN Protocol (if not given will list all)
#[structopt(long = "protocol", short="c", possible_values = &Protocol::variants(), case_insensitive = true)]
#[clap(arg_enum, long = "protocol", short = 'c', ignore_case = true)]
pub protocol: Option<Protocol>,

/// VPN Server prefix
#[structopt(long = "prefix", short = "s")]
#[clap(long = "prefix", short = 's')]
pub prefix: Option<String>,
}
5 changes: 2 additions & 3 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
.unwrap_or_else(|| get_config_file_protocol(path));
provider = VpnProvider::Custom;
// Encode filename with base58 so we can fit it within 16 chars for the veth pair name
let sname = bs58::encode(&path.to_str().unwrap().to_string()).into_string();
let sname = bs58::encode(&path.to_str().unwrap()).into_string();

server_name = sname[0..std::cmp::min(11, sname.len())].to_string();
} else {
Expand Down Expand Up @@ -273,8 +273,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
provider
.get_dyn_openvpn_provider()
.ok()
.map(|x| x.provider_dns())
.flatten()
.and_then(|x| x.provider_dns())
})
.unwrap_or_else(|| vec![IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))]);

Expand Down
7 changes: 3 additions & 4 deletions src/firewall.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use crate::netns::NetworkNamespace;
use clap::arg_enum;
use clap::ArgEnum;
use serde::{Deserialize, Serialize};

arg_enum! {
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy, ArgEnum)]
#[clap(rename_all = "verbatim")]
pub enum Firewall {
IpTables,
NfTables,
}
}

pub fn disable_ipv6(netns: &NetworkNamespace, firewall: Firewall) -> anyhow::Result<()> {
match firewall {
Expand Down
2 changes: 1 addition & 1 deletion src/list_configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn print_configs(cmd: ServersCommand) -> anyhow::Result<()> {
}

// Use get_configs_from_alias
let prefix = cmd.prefix.unwrap_or_else(String::new);
let prefix = cmd.prefix.unwrap_or_default();
println!("provider\tprotocol\tconfig_file");
if (cmd.protocol.is_none() && provider.get_dyn_openvpn_provider().is_ok())
|| cmd.protocol == Some(Protocol::OpenVpn)
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ mod veth_pair;
mod vpn;
mod wireguard;

use clap::Parser;
use list::output_list;
use list_configs::print_configs;
use log::{debug, warn, LevelFilter};
use netns::NetworkNamespace;
use structopt::StructOpt;
use sync::{sync_menu, synch};
use util::clean_dead_locks;
use util::clean_dead_namespaces;
Expand All @@ -41,7 +41,7 @@ use which::which;

fn main() -> anyhow::Result<()> {
// Get struct of args using structopt
let app = args::App::from_args();
let app = args::App::parse();

// Set up logging
let mut builder = pretty_env_logger::formatted_timed_builder();
Expand Down
4 changes: 1 addition & 3 deletions src/openconnect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ impl OpenConnect {
));
}

let handle;

let creds = {
if let Some(config_file) = config_file {
let config_file_path = config_file.canonicalize().context("Invalid path given")?;
Expand All @@ -45,7 +43,7 @@ impl OpenConnect {
let user_arg = format!("--user={}", creds.0);
let command_vec = (&["openconnect", &user_arg, "--passwd-on-stdin", server]).to_vec();

handle = netns
let handle = netns
.exec_no_block(&command_vec, None, false, false, None)
.context("Failed to launch OpenConnect - is openconnect installed?")?;
let id = handle.id();
Expand Down
4 changes: 1 addition & 3 deletions src/openfortivpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ impl OpenFortiVpn {
));
}

let mut handle;

info!("Launching OpenFortiVPN...");
// Must run as root - https://github.com/adrienverge/openfortivpn/issues/650
let command_vec = (&[
Expand All @@ -48,7 +46,7 @@ impl OpenFortiVpn {
std::fs::remove_file(&pppd_log).ok();

// TODO - better handle forwarding output when blocking on password entry (no newline!)
handle = netns
let mut handle = netns
.exec_no_block(&command_vec, None, false, true, None)
.context("Failed to launch OpenFortiVPN - is openfortivpn installed?")?;
let stdout = handle.stdout.take().unwrap();
Expand Down
5 changes: 2 additions & 3 deletions src/openvpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ impl OpenVpn {
));
}

let handle;
let log_file_str = format!("/etc/netns/{}/openvpn.log", &netns.name);
{
File::create(&log_file_str)?;
Expand Down Expand Up @@ -95,7 +94,7 @@ impl OpenVpn {
debug!("Found remotes: {:?}", &remotes);
let working_dir = PathBuf::from(config_file_path.parent().unwrap());

handle = netns
let handle = netns
.exec_no_block(&command_vec, None, true, false, Some(working_dir))
.context("Failed to launch OpenVPN - is openvpn installed?")?;
let id = handle.id();
Expand Down Expand Up @@ -559,7 +558,7 @@ pub fn get_remotes_from_config(path: &Path) -> anyhow::Result<Vec<Remote>> {

let re2 = Regex::new(r"proto ([a-z\-]+)")?;
let mut caps2 = re2.captures_iter(&file_string);
let default_proto = caps2.next().map(|x| x.get(1)).flatten();
let default_proto = caps2.next().and_then(|x| x.get(1));

for cap in caps {
let proto = match (cap.get(3), default_proto) {
Expand Down
2 changes: 1 addition & 1 deletion src/providers/airvpn/openvpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl OpenVpnProvider for AirVPN {
.get("public_name")
.unwrap()
.to_string()
.replace("\"", "");
.replace('\"', "");
if !request_server_names.is_empty() {
// separate server names with '%2C'
request_server_names.push_str("%2C");
Expand Down
2 changes: 1 addition & 1 deletion src/providers/azirevpn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct ConnectResponse {
#[derive(Deserialize, Debug, Clone)]
struct WgResponse {
#[serde(alias = "DNS", deserialize_with = "de_vec_ipaddr")]
dns: Vec<IpAddr>,
dns: Option<Vec<IpAddr>>,
#[serde(alias = "Address", deserialize_with = "de_vec_ipnet")]
address: Vec<IpNet>,
#[serde(alias = "PublicKey")]
Expand Down
7 changes: 3 additions & 4 deletions src/providers/hma/openvpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ impl OpenVpnProvider for HMA {
== Some("ovpn")
&& path
.parent()
.map(|x| x.file_name().map(|x| x.to_str()))
.flatten()
.and_then(|x| x.file_name().map(|x| x.to_str()))
.flatten()
== Some(&config_choice.dir_name())
&& !path.starts_with("OpenVPN-2.4")
Expand All @@ -83,9 +82,9 @@ impl OpenVpnProvider for HMA {
.expect("Invalid filename");
let mut filename_iter = filename.split('.');
let country = filename_iter.next().unwrap();
let country = country.replace("'", "").replace("`", "").to_lowercase();
let country = country.replace('\'', "").replace('`', "").to_lowercase();
let city = filename_iter.next().unwrap();
let city = city.replace("'", "").replace("`", "").to_lowercase();
let city = city.replace('\'', "").replace('`', "").to_lowercase();
let filename = format!("{}-{}.ovpn", country, city);
let outpath = openvpn_dir.join(filename.to_lowercase().replace(' ', "_"));
debug!(
Expand Down
Loading