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

Add create-netns-only argument for debugging + firejail usage #242

Merged
merged 1 commit into from
Nov 26, 2023
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
16 changes: 16 additions & 0 deletions USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ entire shell sessions inside vopono.
Note that the order of command-line arguments matters, as the `--dns`
argument can take a list of DNS servers for example.

### Creating only Network Namespace

You can run vopono to create only the network namespace using the
`--create-netns-only` argument, the application related arguments are
then ignored (pass anything as the application name). This can be useful
for debugging connection issues.

This can also be used to launch an application without sudo via firejail
- e.g. (where `none` is passed as the dummy application):

```bash
$ vopono -v exec --provider protonvpn --server japan --protocol openvpn --create-netns-only none
2023-11-26T11:17:52.623Z INFO vopono::exec > Created netns vo_pr_japan - will leave network namespace alive until ctrl+C received
$ firejail --netns=vo_pr_japan firefox-developer-edition
```

### Configuration file

You can save default configuration options in the config file
Expand Down
4 changes: 4 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ pub struct ExecCommand {
/// Enable port forwarding for ProtonVPN connections
#[clap(long = "protonvpn-port-forwarding")]
pub protonvpn_port_forwarding: bool,

/// Only create network namespace (does not run application)
#[clap(long = "create-netns-only")]
pub create_netns_only: bool,
}

#[derive(Parser)]
Expand Down
73 changes: 47 additions & 26 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ pub fn exec(command: ExecCommand, uiclient: &dyn UiClient) -> anyhow::Result<()>
command.protonvpn_port_forwarding
};

// Create netns only
let create_netns_only = if !command.create_netns_only {
vopono_config_settings
.get("create-netns-only")
.map_err(|_e| anyhow!("Failed to read config file"))
.ok()
.unwrap_or(false)
} else {
command.create_netns_only
};

// Assign DNS server from args or vopono config file
let base_dns = command.dns.clone().or_else(|| {
vopono_config_settings
Expand Down Expand Up @@ -551,15 +562,6 @@ pub fn exec(command: ExecCommand, uiclient: &dyn UiClient) -> anyhow::Result<()>
vopono_core::util::open_ports(&ns, &[pmpc.local_port], firewall)?;
}

let application = ApplicationWrapper::new(
&ns,
&command.application,
user,
group,
working_directory.map(PathBuf::from),
natpmpc,
)?;

// Launch TCP proxy server on other threads if forwarding ports
// TODO: Fix when running as root
let mut proxy = Vec::new();
Expand All @@ -580,27 +582,46 @@ pub fn exec(command: ExecCommand, uiclient: &dyn UiClient) -> anyhow::Result<()>
}
}

let pid = application.handle.id();
info!(
"Application {} launched in network namespace {} with pid {}",
&command.application, &ns.name, pid
);
if !create_netns_only {
let application = ApplicationWrapper::new(
&ns,
&command.application,
user,
group,
working_directory.map(PathBuf::from),
natpmpc,
)?;

if let Some(pmpc) = application.protonvpn_port_forwarding.as_ref() {
info!("ProtonVPN Port Forwarding on port {}", pmpc.local_port)
}
let output = application.wait_with_output()?;
io::stdout().write_all(output.stdout.as_slice())?;
let pid = application.handle.id();
info!(
"Application {} launched in network namespace {} with pid {}",
&command.application, &ns.name, pid
);

// Allow daemons to leave namespace open
if vopono_core::util::check_process_running(pid) {
if let Some(pmpc) = application.protonvpn_port_forwarding.as_ref() {
info!("ProtonVPN Port Forwarding on port {}", pmpc.local_port)
}
let output = application.wait_with_output()?;
io::stdout().write_all(output.stdout.as_slice())?;

// Allow daemons to leave namespace open
if vopono_core::util::check_process_running(pid) {
info!(
"Process {} still running, assumed to be daemon - will leave network namespace {} alive until ctrl+C received",
pid, &ns.name
);
stay_alive(Some(pid), signals);
} else if command.keep_alive {
info!(
"Keep-alive flag active - will leave network namespace {} alive until ctrl+C received", &ns.name
);
stay_alive(None, signals);
}
} else {
info!(
"Process {} still running, assumed to be daemon - will leave network namespace alive until ctrl+C received",
pid
"Created netns {} - will leave network namespace alive until ctrl+C received",
&ns.name
);
stay_alive(Some(pid), signals);
} else if command.keep_alive {
info!("Keep-alive flag active - will leave network namespace alive until ctrl+C received");
stay_alive(None, signals);
}

Expand Down
9 changes: 4 additions & 5 deletions vopono_core/src/network/shadowsocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use log::{debug, error};
use rand::seq::SliceRandom;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fs::read_to_string;
use std::net::{IpAddr, Ipv4Addr};
use std::path::Path;
Expand Down Expand Up @@ -95,9 +94,9 @@ pub fn uses_shadowsocks(openvpn_config: &Path) -> anyhow::Result<Option<(IpAddr,
}
debug!("socks-proxy detected, will launch Shadowsocks server");
Ok(Some((
IpAddr::try_from(Ipv4Addr::from_str(
IpAddr::from(Ipv4Addr::from_str(
cap.as_ref().unwrap().get(1).unwrap().as_str(),
)?)?,
)?),
cap.unwrap().get(2).unwrap().as_str().parse::<u16>()?,
)))
}
Expand All @@ -113,9 +112,9 @@ pub fn get_routes_from_config(path: &Path) -> anyhow::Result<Vec<IpAddr>> {
let caps = re.captures_iter(&file_string);

for cap in caps {
output_vec.push(IpAddr::try_from(Ipv4Addr::from_str(
output_vec.push(IpAddr::from(Ipv4Addr::from_str(
cap.get(1).unwrap().as_str(),
)?)?);
)?));
}

if output_vec.is_empty() {
Expand Down
3 changes: 1 addition & 2 deletions vopono_core/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,7 @@ pub fn elevate_privileges(askpass: bool) -> anyhow::Result<()> {
.status()
.context(format!("Executing sudo {} {:?}", sudo_flags, &args))?;

// Deprecated - do we need to handle flag here?
// cleanup::cleanup_signal(SIGINT)?;
// TODO: Could handle executing with non-sudo firejail here

if terminated.load(Ordering::SeqCst) {
// we received a sigint,
Expand Down
Loading