Skip to content

Commit

Permalink
Merge branch 'macos-add-split-tunnel'
Browse files Browse the repository at this point in the history
  • Loading branch information
dlon committed Apr 30, 2024
2 parents b871bdf + 818ca7a commit 0eb0832
Show file tree
Hide file tree
Showing 42 changed files with 3,687 additions and 277 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Line wrap the file at 100 chars. Th
### Added
- Add custom bridge settings in GUI.

#### macOS
- Add support for split tunneling (beta).

### Fixed
#### Linux
- Fix GUI not working on Ubuntu 24.04 by adding an AppArmor profile.
Expand Down
104 changes: 100 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions docs/split-tunneling.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ Some definitions of terms used later to describe behavior:
*: On platforms where we have custom firewall integration. This is currently on desktop operating
systems, and not mobile.

### Windows and Linux
### Desktop platforms (Windows, Linux, and macOS)

| In-app DNS setting | Normal & Excluded app |
|-|-|
| **Default DNS** | In tunnel (to relay) |
| **Private custom DNS** (e.g. 10.0.1.1) | LAN (to 10.0.1.1) |
| **Public custom DNS** (e.g. 8.8.8.8) | In tunnel (to 8.8.8.8) |
| In-app DNS setting | Normal & Excluded app |
|-|------------------------------------------------|
| **Default DNS** | In tunnel (to relay) |
| **Private custom DNS** (e.g. 10.0.1.1) | LAN (to 10.0.1.1)<br/>**macOS**: Not supported |
| **Public custom DNS** (e.g. 8.8.8.8) | In tunnel (to 8.8.8.8) |

In other words: Normal and excluded processes always behave the same. This is because DNS is
typically handled by a service, e.g. DNS cache on Windows or systemd-resolved's resolver on Linux,
which is not an excluded process.
In other words: Normal and excluded processes behave the same. This is because DNS is typically
handled by a service, e.g. DNS cache on Windows or systemd-resolved's resolver on Linux, which is
not an excluded process.

For the sake of simplicity and consistency, requests to public custom DNS resolvers are also sent
inside the tunnel when using a plain old static `resolv.conf`, even though it is technically
Expand Down
86 changes: 86 additions & 0 deletions mullvad-cli/src/cmds/split_tunnel/macos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use anyhow::Result;
use std::path::PathBuf;

use clap::Subcommand;
use mullvad_management_interface::MullvadProxyClient;

use super::super::BooleanOption;

/// Set options for applications to exclude from the tunnel.
#[derive(Subcommand, Debug)]
pub enum SplitTunnel {
/// Display the split tunnel status and apps
Get,

/// Enable or disable split tunnel
Set { policy: BooleanOption },

/// Manage applications to exclude from the tunnel
#[clap(subcommand)]
App(App),
}

#[derive(Subcommand, Debug)]
pub enum App {
Add { path: PathBuf },
Remove { path: PathBuf },
Clear,
}

impl SplitTunnel {
pub async fn handle(self) -> Result<()> {
match self {
SplitTunnel::Get => {
let mut rpc = MullvadProxyClient::new().await?;
let settings = rpc.get_settings().await?.split_tunnel;

let enable_exclusions = BooleanOption::from(settings.enable_exclusions);

println!("Split tunneling state: {enable_exclusions}");

println!("Excluded applications:");
for path in &settings.apps {
println!("{}", path.display());
}

Ok(())
}
SplitTunnel::Set { policy } => {
let mut rpc = MullvadProxyClient::new().await?;
rpc.set_split_tunnel_state(*policy).await?;
println!("Split tunnel policy: {policy}");
Ok(())
}
SplitTunnel::App(subcmd) => Self::app(subcmd).await,
}
}

async fn app(subcmd: App) -> Result<()> {
match subcmd {
App::Add { path } => {
MullvadProxyClient::new()
.await?
.add_split_tunnel_app(path)
.await?;
println!("Added path to excluded apps list");
Ok(())
}
App::Remove { path } => {
MullvadProxyClient::new()
.await?
.remove_split_tunnel_app(path)
.await?;
println!("Stopped excluding app from tunnel");
Ok(())
}
App::Clear => {
MullvadProxyClient::new()
.await?
.clear_split_tunnel_apps()
.await?;
println!("Stopped excluding all apps");
Ok(())
}
}
}
}
5 changes: 4 additions & 1 deletion mullvad-cli/src/cmds/split_tunnel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ mod imp;
#[path = "windows.rs"]
mod imp;

#[cfg(any(target_os = "linux", windows))]
#[cfg(target_os = "macos")]
#[path = "macos.rs"]
mod imp;

pub use imp::*;
2 changes: 0 additions & 2 deletions mullvad-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ enum Cli {
#[clap(subcommand)]
Obfuscation(obfuscation::Obfuscation),

#[cfg(any(target_os = "windows", target_os = "linux"))]
#[clap(subcommand)]
SplitTunnel(split_tunnel::SplitTunnel),

Expand Down Expand Up @@ -171,7 +170,6 @@ async fn main() -> Result<()> {
Cli::FactoryReset => reset::handle().await,
Cli::Relay(cmd) => cmd.handle().await,
Cli::Tunnel(cmd) => cmd.handle().await,
#[cfg(any(target_os = "windows", target_os = "linux"))]
Cli::SplitTunnel(cmd) => cmd.handle().await,
Cli::Status { cmd, args } => status::handle(cmd, args).await,
Cli::CustomList(cmd) => cmd.handle().await,
Expand Down
Loading

0 comments on commit 0eb0832

Please sign in to comment.