Skip to content

Commit

Permalink
Merge pull request #74 from jamesmcm/runscripts
Browse files Browse the repository at this point in the history
Add ability to provide PostUp and PreDown scripts
  • Loading branch information
jamesmcm authored Apr 6, 2021
2 parents e32bb00 + b18c01d commit b20f03d
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 11 deletions.
2 changes: 1 addition & 1 deletion 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.6.10"
version = "0.7.0"
authors = ["James McMurray <[email protected]>"]
edition = "2018"
license = "GPL-3.0-or-later"
Expand Down
12 changes: 12 additions & 0 deletions USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,25 @@ firewall = "NfTables"
provider = "Mullvad"
protocol = "Wireguard"
server = "usa-us22"
postup = "/home/archie/postup.sh"
predown = "/home/archie/predown.sh"
user = "archie"
# custom_config = "/home/user/vpn/mycustomconfig.ovpn"
```

Note that the values are case-sensitive. If you use a custom config file
then you should not set the provider or server (setting the protocol is
also optional).

### Host scripts

Host scripts to run just after a network namespace is created and just before it is destroyed,
can be provided with the `postup` and `predown` arguments (or in the `config.toml`).

Note these scripts run on the host (outside the network namespace), using the current working directory,
and with the same user as the final application itself (which can be set
with the `user` argument or config file entry).

### Wireguard

Install vopono and use `vopono sync` to
Expand Down
10 changes: 10 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ pub struct ExecCommand {
/// Block all IPv6 traffic
#[structopt(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")]
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")]
pub predown: Option<String>,
}

#[derive(StructOpt)]
Expand Down
59 changes: 50 additions & 9 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
let server_name: String;
let protocol: Protocol;

// TODO: Refactor this part - DRY
// Create empty config file if does not exist
let config_path = vopono_dir()?.join("config.toml");
{
Expand Down Expand Up @@ -61,6 +62,42 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
.ok()
});

// Assign postup script from args or vopono config file
let postup = command.postup.clone().or_else(|| {
vopono_config_settings
.get("postup")
.map_err(|e| {
debug!("vopono config.toml: {:?}", e);
anyhow!("Failed to read config file")
})
.ok()
});

// Assign predown script from args or vopono config file
let predown = command.predown.clone().or_else(|| {
vopono_config_settings
.get("predown")
.map_err(|e| {
debug!("vopono config.toml: {:?}", e);
anyhow!("Failed to read config file")
})
.ok()
});

// User for application command, if None will use root
let user = if command.user.is_none() {
vopono_config_settings
.get("user")
.map_err(|e| {
debug!("vopono config.toml: {:?}", e);
anyhow!("Failed to read config file")
})
.ok()
.or_else(|| std::env::var("SUDO_USER").ok())
} else {
command.user
};

// Assign protocol and server from args or vopono config file or custom config if used
if let Some(path) = &custom_config {
protocol = command
Expand All @@ -81,7 +118,6 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
);
} else {
// Get server and provider
// TODO: Handle default case and remove expect()
provider = command
.vpn_provider
.or_else(|| {
Expand Down Expand Up @@ -128,7 +164,6 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
})
.unwrap_or_else(|| provider.get_dyn_provider().default_protocol());
}
// TODO: PostUp and PreDown scripts

if provider != VpnProvider::Custom {
// Check config files exist for provider
Expand Down Expand Up @@ -196,6 +231,8 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
provider.clone(),
protocol.clone(),
firewall,
predown,
user.clone(),
)?;
let target_subnet = get_target_subnet()?;
ns.add_loopback()?;
Expand Down Expand Up @@ -309,17 +346,21 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
)?;
}
}

// Run PostUp script (if any)
if let Some(pucmd) = postup {
if user.is_some() {
std::process::Command::new("sudo")
.args(&["-Eu", user.as_ref().unwrap(), &pucmd])
.spawn()?;
} else {
std::process::Command::new(&pucmd).spawn()?;
}
}
}

let ns = ns.write_lockfile(&command.application)?;

// User for application command, if None will use root
let user = if command.user.is_none() {
std::env::var("SUDO_USER").ok()
} else {
command.user
};

let application = ApplicationWrapper::new(&ns, &command.application, user)?;

// Launch TCP proxy server on other threads if forwarding ports
Expand Down
18 changes: 18 additions & 0 deletions src/netns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub struct NetworkNamespace {
pub provider: VpnProvider,
pub protocol: Protocol,
pub firewall: Firewall,
pub predown: Option<String>,
pub predown_user: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down Expand Up @@ -67,6 +69,8 @@ impl NetworkNamespace {
provider: VpnProvider,
protocol: Protocol,
firewall: Firewall,
predown: Option<String>,
predown_user: Option<String>,
) -> anyhow::Result<Self> {
sudo_command(&["ip", "netns", "add", name.as_str()])
.with_context(|| format!("Failed to create network namespace: {}", &name))?;
Expand All @@ -86,6 +90,8 @@ impl NetworkNamespace {
provider,
protocol,
firewall,
predown,
predown_user,
})
}

Expand Down Expand Up @@ -388,6 +394,18 @@ impl Drop for NetworkNamespace {
}
}
info!("Shutting down vopono namespace - as there are no processes left running inside");
// Run PreDown script (if any)
if let Some(pdcmd) = self.predown.as_ref() {
if self.predown_user.is_some() {
std::process::Command::new("sudo")
.args(&["-Eu", self.predown_user.as_ref().unwrap(), &pdcmd])
.spawn()
.ok();
} else {
std::process::Command::new(&pdcmd).spawn().ok();
}
}

self.openvpn = None;
self.veth_pair = None;
self.dns_config = None;
Expand Down
10 changes: 9 additions & 1 deletion src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,15 @@ pub fn get_configs_from_alias(list_path: &Path, alias: &str) -> Vec<PathBuf> {
.to_string(),
)
})
.filter(|x| x.2.starts_with(alias) || (x.1.starts_with(alias)))
.filter(|x| {
x.2.starts_with(alias)
|| (x.1.starts_with(alias))
|| x.0
.file_name()
.to_str()
.expect("No filename")
.starts_with(alias)
})
.map(|x| PathBuf::from(x.0.path()))
.collect::<Vec<PathBuf>>()
}
Expand Down

0 comments on commit b20f03d

Please sign in to comment.