Skip to content
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
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "vopono"
description = "Launch applications via VPN tunnels using temporary network namespaces"
version = "0.10.14"
version = "0.10.15"
authors = ["James McMurray <jamesmcm03@gmail.com>"]
edition = "2024"
license = "GPL-3.0-or-later"
Expand All @@ -20,7 +20,7 @@ log = "0.4"
pretty_env_logger = "0.5"
clap = { version = "4", features = ["derive"] }
which = "8"
dialoguer = "0.11"
dialoguer = "0.12"
compound_duration = "1"
signal-hook = "0.3"
walkdir = "2"
Expand Down
87 changes: 45 additions & 42 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn exec(
uiclient: &dyn UiClient,
verbose: bool,
silent: bool,
) -> anyhow::Result<()> {
) -> anyhow::Result<i32> {
// this captures all sigint signals
// ignore for now, they are automatically passed on to the child
let signals = Signals::new([SIGINT])?;
Expand Down Expand Up @@ -255,49 +255,49 @@ pub fn exec(

// Launch TCP proxy server on other threads if forwarding ports
let mut proxy = Vec::new();
if let Some(f) = parsed_command.forward.clone() {
if !(parsed_command.no_proxy || f.is_empty()) {
for p in f {
debug!(
"Forwarding port: {}, {:?}",
p,
if let Some(f) = parsed_command.forward.clone()
&& !(parsed_command.no_proxy || f.is_empty())
{
for p in f {
debug!(
"Forwarding port: {}, {:?}",
p,
ns.veth_pair_ips
.as_ref()
.unwrap()
.ipv4
.as_ref()
.unwrap()
.namespace_ip
);

// TODO: Do we want IPv6 forwarding?
proxy.push(basic_tcp_proxy::TcpProxy::new(
p,
std::net::SocketAddr::new(
ns.veth_pair_ips
.as_ref()
.unwrap()
.ipv4
.as_ref()
.unwrap()
.namespace_ip
);

// TODO: Do we want IPv6 forwarding?
proxy.push(basic_tcp_proxy::TcpProxy::new(
.namespace_ip,
p,
std::net::SocketAddr::new(
ns.veth_pair_ips
.as_ref()
.unwrap()
.ipv4
.as_ref()
.unwrap()
.namespace_ip,
p,
),
false,
));
}
),
false,
));
}
}

if !parsed_command.create_netns_only {
run_application(
return run_application(
&parsed_command,
forwarder,
&ns,
signals,
silent,
&host_env_vars,
)?;
);
} else {
info!(
"Created netns {} - will leave network namespace alive until ctrl+C received",
Expand All @@ -306,7 +306,7 @@ pub fn exec(
stay_alive(None, signals);
}

Ok(())
Ok(0)
}

// Block waiting for SIGINT
Expand Down Expand Up @@ -484,18 +484,18 @@ fn run_protocol_in_netns(
}

// Set DNS with OpenVPN server response if present
if let Some(newdns) = ns.openvpn.as_ref().unwrap().openvpn_dns {
if parsed_command.dns.is_none() {
let old_dns = ns.dns_config.take();
std::mem::forget(old_dns);
// TODO: DNS suffixes?
ns.dns_config(
&[newdns],
&[],
parsed_command.hosts.as_ref(),
parsed_command.allow_host_access,
)?;
}
if let Some(newdns) = ns.openvpn.as_ref().unwrap().openvpn_dns
&& parsed_command.dns.is_none()
{
let old_dns = ns.dns_config.take();
std::mem::forget(old_dns);
// TODO: DNS suffixes?
ns.dns_config(
&[newdns],
&[],
parsed_command.hosts.as_ref(),
parsed_command.allow_host_access,
)?;
}
}
Protocol::Wireguard => {
Expand Down Expand Up @@ -672,7 +672,7 @@ fn run_application(
signals: SignalsInfo,
silent: bool,
host_env_vars: &std::collections::HashMap<String, String>,
) -> anyhow::Result<()> {
) -> anyhow::Result<i32> {
let application = ApplicationWrapper::new(
ns,
&parsed_command.application,
Expand Down Expand Up @@ -703,12 +703,15 @@ fn run_application(
pid, &ns.name
);
stay_alive(Some(pid), signals);
Ok(0)
} else if parsed_command.keep_alive {
info!(
"Keep-alive flag active - will leave network namespace {} alive until ctrl+C received",
&ns.name
);
stay_alive(None, signals);
Ok(0)
} else {
Ok(output.status.code().unwrap_or(1))
}
Ok(())
}
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ fn main() -> anyhow::Result<()> {
let verbose = app.verbose && !app.silent;
elevate_privileges(app.askpass)?;
clean_dead_namespaces()?;
exec::exec(cmd, &uiclient, verbose, app.silent)?
let exit_code = exec::exec(cmd, &uiclient, verbose, app.silent)?;
std::process::exit(exit_code);
}
args::Command::List(listcmd) => {
clean_dead_locks()?;
Expand Down
10 changes: 5 additions & 5 deletions vopono_core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "vopono_core"
description = "Library code for running VPN connections in network namespaces"
version = "0.1.14"
version = "0.1.15"
edition = "2024"
authors = ["James McMurray <jamesmcm03@gmail.com>"]
license = "GPL-3.0-or-later"
Expand All @@ -20,7 +20,7 @@ nix = { version = "0.30", features = ["user", "signal", "fs", "process"] }
serde = { version = "1", features = ["derive", "std"] }
csv = "1"
regex = "1"
ron = "0.10"
ron = "0.11"
walkdir = "2"
# Stuck on rand 0.6 due to x25519-dalek:
# https://github.com/dalek-cryptography/curve25519-dalek/issues/731
Expand All @@ -34,7 +34,7 @@ reqwest = { default-features = false, version = "0.12", features = [
"json",
"rustls-tls",
] } # TODO: Can we remove Tokio dependency?
sysinfo = "0.36"
sysinfo = "0.37"
base64 = "0.22"
x25519-dalek = { version = "3.0.0-pre.0", features = ["static_secrets"] }
strum = "0.27"
Expand All @@ -49,9 +49,9 @@ tiny_http = "0.12"
chrono = "0.4"
json = "0.12"
shell-words = "1"
dns-lookup = "2"
dns-lookup = "3"
libc = "0.2"
pem = "3"
rustls = { version = "0.23", default-features = false, features = ["ring"] }
rustls-connector = { version = "0.21", default-features = false }
rustls-connector = { version = "0.22", default-features = false }
nom = "8"
5 changes: 2 additions & 3 deletions vopono_core/src/config/providers/ivpn/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,10 @@ impl WireguardProvider for IVPN {
if let Err(err) = ip_parse {
return Err(format!("Input: {} is not valid IPv4 address: {}", ipstr.trim(), err));
};
if let Ok(ip) = ip_parse {
if ip.octets()[0] != 172 {
if let Ok(ip) = ip_parse
&& ip.octets()[0] != 172 {
return Err(format!("IP address: {} did not start with expected octet 172", ipstr.trim()));
}
}
Ok(())
}))})?;

Expand Down
28 changes: 14 additions & 14 deletions vopono_core/src/config/providers/mozilla/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,20 @@ impl MozillaVPN {
let code;
let code_url_regex = regex::Regex::new(r"\A/\?code=([0-9a-f]{80})\z").unwrap();
for request in server.incoming_requests() {
if let Some(caps) = code_url_regex.captures(request.url()) {
if *request.method() == Method::Get {
code = caps.get(1).unwrap();
let response = client
.post(format!("{}/vpn/login/verify", Self::V2_URL))
.header("User-Agent", "Why do you need a user agent???")
.json(&AccessTokenRequest {
code: code.as_str(),
code_verifier: std::str::from_utf8(&code_verifier).unwrap(),
})
.send()
.unwrap();
return Ok(response.json::<Login>().unwrap());
}
if let Some(caps) = code_url_regex.captures(request.url())
&& *request.method() == Method::Get
{
code = caps.get(1).unwrap();
let response = client
.post(format!("{}/vpn/login/verify", Self::V2_URL))
.header("User-Agent", "Why do you need a user agent???")
.json(&AccessTokenRequest {
code: code.as_str(),
code_verifier: std::str::from_utf8(&code_verifier).unwrap(),
})
.send()
.unwrap();
return Ok(response.json::<Login>().unwrap());
}
}
unreachable!("Server closed without receiving code")
Expand Down
19 changes: 9 additions & 10 deletions vopono_core/src/network/openvpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,15 @@ impl OpenVpn {

pos += x;

if let Some(cap) = dns_regex.captures(&buffer) {
if openvpn_dns.is_none() {
if let Some(ipstr) = cap.get(1) {
debug!("Found OpenVPN DNS response: {}", ipstr.as_str());
let ipaddr = IpAddr::from_str(ipstr.as_str());
if let Ok(ip) = ipaddr {
openvpn_dns = Some(ip);
debug!("Set OpenVPN DNS to: {ip:?}");
}
}
if let Some(cap) = dns_regex.captures(&buffer)
&& openvpn_dns.is_none()
&& let Some(ipstr) = cap.get(1)
{
debug!("Found OpenVPN DNS response: {}", ipstr.as_str());
let ipaddr = IpAddr::from_str(ipstr.as_str());
if let Ok(ip) = ipaddr {
openvpn_dns = Some(ip);
debug!("Set OpenVPN DNS to: {ip:?}");
}
}

Expand Down
22 changes: 11 additions & 11 deletions vopono_core/src/network/port_forwarding/azirevpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,17 @@ impl AzireVpnPortForwarding {
.with_context(|| "Failed to parse JSON response from listing AzireVPN Port Forwarding");

// If so, return that port
if let Ok(output_data) = output_data_result {
if !output_data.data.ports.is_empty() {
let port = output_data.data.ports[0].port;
log::info!("Port forwarding already enabled on port {port}");
return Ok(Self {
port,
local_ip,
access_token: access_token.to_string(),
netns_name: netns.name.clone(),
});
}
if let Ok(output_data) = output_data_result
&& !output_data.data.ports.is_empty()
{
let port = output_data.data.ports[0].port;
log::info!("Port forwarding already enabled on port {port}");
return Ok(Self {
port,
local_ip,
access_token: access_token.to_string(),
netns_name: netns.name.clone(),
});
}

// If not, create a new port forwarding
Expand Down
4 changes: 2 additions & 2 deletions vopono_core/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ pub fn elevate_privileges(askpass: bool) -> anyhow::Result<()> {

debug!("Args: {:?}", &args);
// status blocks until the process has ended
let _status = Command::new("sudo")
let status = Command::new("sudo")
.arg(sudo_flags)
.args(args.clone())
.status()
Expand All @@ -369,7 +369,7 @@ pub fn elevate_privileges(askpass: bool) -> anyhow::Result<()> {
.expect("failed to send SIGINT");
}

std::process::exit(0);
std::process::exit(status.code().unwrap_or(1));
} else if std::env::var("SUDO_USER").is_err() {
warn!("Running vopono as root user directly!");
}
Expand Down
Loading