summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2018-11-13 13:58:28 +0100
committerLinus Färnstrand <linus@mullvad.net>2018-11-13 13:58:28 +0100
commitd6d0b170290c6f8aff4fcf39e7b68872313eafad (patch)
treec6adacf567d5c9f1641440acf6c6bab95444df7d
parente59ac7a68a5cdda11d82b4a69ad33fddd3b3d294 (diff)
parent08f2a5b3c6b4455cd9feb9863da96896b93f9195 (diff)
downloadmullvadvpn-d6d0b170290c6f8aff4fcf39e7b68872313eafad.tar.xz
mullvadvpn-d6d0b170290c6f8aff4fcf39e7b68872313eafad.zip
Merge branch 'next-level-kill-switch'
-rw-r--r--CHANGELOG.md3
-rw-r--r--mullvad-cli/src/cmds/block_when_disconnected.rs63
-rw-r--r--mullvad-cli/src/cmds/mod.rs8
-rw-r--r--mullvad-daemon/src/lib.rs27
-rw-r--r--mullvad-daemon/src/management_interface.rs22
-rw-r--r--mullvad-ipc-client/src/lib.rs4
-rw-r--r--mullvad-types/src/settings.rs17
-rw-r--r--talpid-core/src/tunnel_state_machine/blocked_state.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnected_state.rs41
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnecting_state.rs12
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs10
13 files changed, 208 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4cad4a8f37..46a22cdc87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,9 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
- CLI command `relay update` that triggers an update of the relay list in the daemon.
+- Add extra level of kill-switch called "block when disconnected". Blocks all network traffic even
+ in the disconnected state. Not activated by default and can be changed via the CLI subcommand
+ `block-when-disconnected`.
#### macOS
- Detect if the computer is offline. If so, don't sit in a reconnect loop, instead block and show
diff --git a/mullvad-cli/src/cmds/block_when_disconnected.rs b/mullvad-cli/src/cmds/block_when_disconnected.rs
new file mode 100644
index 0000000000..23ba31ebb1
--- /dev/null
+++ b/mullvad-cli/src/cmds/block_when_disconnected.rs
@@ -0,0 +1,63 @@
+use clap::{self, value_t_or_exit};
+use crate::{new_rpc_client, Command, Result};
+
+pub struct BlockWhenDisconnected;
+
+impl Command for BlockWhenDisconnected {
+ fn name(&self) -> &'static str {
+ "block-when-disconnected"
+ }
+
+ fn clap_subcommand(&self) -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name(self.name())
+ .about("Control if the system service should block network access when disconnected from VPN")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .subcommand(
+ clap::SubCommand::with_name("set")
+ .about("Change the block when disconnected setting")
+ .arg(
+ clap::Arg::with_name("policy")
+ .required(true)
+ .possible_values(&["on", "off"]),
+ ),
+ )
+ .subcommand(
+ clap::SubCommand::with_name("get")
+ .about("Display the current block when disconnected setting"),
+ )
+ }
+
+ fn run(&self, matches: &clap::ArgMatches) -> Result<()> {
+ if let Some(set_matches) = matches.subcommand_matches("set") {
+ let block_when_disconnected = value_t_or_exit!(set_matches.value_of("policy"), String);
+ self.set(block_when_disconnected == "on")
+ } else if let Some(_matches) = matches.subcommand_matches("get") {
+ self.get()
+ } else {
+ unreachable!("No block-when-disconnected command given");
+ }
+ }
+}
+
+impl BlockWhenDisconnected {
+ fn set(&self, block_when_disconnected: bool) -> Result<()> {
+ let mut rpc = new_rpc_client()?;
+ rpc.set_block_when_disconnected(block_when_disconnected)?;
+ println!("Changed block when disconnected setting");
+ Ok(())
+ }
+
+ fn get(&self) -> Result<()> {
+ let mut rpc = new_rpc_client()?;
+ let block_when_disconnected = rpc.get_settings()?.get_block_when_disconnected();
+ println!(
+ "Network traffic will be {} when the VPN is disconnected",
+ if block_when_disconnected {
+ "blocked"
+ } else {
+ "allowed"
+ }
+ );
+ Ok(())
+ }
+}
diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs
index e80ef875a5..0a16b952f4 100644
--- a/mullvad-cli/src/cmds/mod.rs
+++ b/mullvad-cli/src/cmds/mod.rs
@@ -16,6 +16,9 @@ pub use self::connect::Connect;
mod disconnect;
pub use self::disconnect::Disconnect;
+mod block_when_disconnected;
+pub use self::block_when_disconnected::BlockWhenDisconnected;
+
mod relay;
pub use self::relay::Relay;
@@ -33,11 +36,12 @@ pub fn get_commands() -> HashMap<&'static str, Box<Command>> {
let commands: Vec<Box<Command>> = vec![
Box::new(Account),
Box::new(AutoConnect),
- Box::new(Status),
+ Box::new(BlockWhenDisconnected),
Box::new(Connect),
Box::new(Disconnect),
- Box::new(Relay),
Box::new(Lan),
+ Box::new(Relay),
+ Box::new(Status),
Box::new(Tunnel),
Box::new(Version),
];
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index f420afdff2..b5ef9dd21e 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -220,6 +220,7 @@ impl Daemon {
let tunnel_parameters_generator = MullvadTunnelParametersGenerator { tx: tx.clone() };
let tunnel_command_tx = tunnel_state_machine::spawn(
settings.get_allow_lan(),
+ settings.get_block_when_disconnected(),
tunnel_parameters_generator,
log_dir,
resource_dir,
@@ -423,6 +424,9 @@ impl Daemon {
SetAccount(tx, account_token) => self.on_set_account(tx, account_token),
UpdateRelaySettings(tx, update) => self.on_update_relay_settings(tx, update),
SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan),
+ SetBlockWhenDisconnected(tx, block_when_disconnected) => {
+ self.on_set_block_when_disconnected(tx, block_when_disconnected)
+ }
SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect),
SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg),
SetOpenVpnProxy(tx, proxy) => self.on_set_openvpn_proxy(tx, proxy),
@@ -601,6 +605,29 @@ impl Daemon {
}
}
+ fn on_set_block_when_disconnected(
+ &mut self,
+ tx: oneshot::Sender<()>,
+ block_when_disconnected: bool,
+ ) {
+ let save_result = self
+ .settings
+ .set_block_when_disconnected(block_when_disconnected);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "set_block_when_disconnected response");
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ self.send_tunnel_command(TunnelCommand::BlockWhenDisconnected(
+ block_when_disconnected,
+ ));
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
fn on_set_auto_connect(&mut self, tx: oneshot::Sender<()>, auto_connect: bool) {
let save_result = self.settings.set_auto_connect(auto_connect);
match save_result.chain_err(|| "Unable to save settings") {
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index d306e4c33f..f9eb8ffa63 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -65,6 +65,10 @@ build_rpc_trait! {
#[rpc(meta, name = "set_allow_lan")]
fn set_allow_lan(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
+ /// Set if the client should allow network communication when in the disconnected state.
+ #[rpc(meta, name = "set_block_when_disconnected")]
+ fn set_block_when_disconnected(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
+
/// Set if the daemon should automatically establish a tunnel on start or not.
#[rpc(meta, name = "set_auto_connect")]
fn set_auto_connect(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
@@ -176,6 +180,8 @@ pub enum ManagementCommand {
UpdateRelaySettings(OneshotSender<()>, RelaySettingsUpdate),
/// Set the allow LAN setting.
SetAllowLan(OneshotSender<()>, bool),
+ /// Set the block_when_disconnected setting.
+ SetBlockWhenDisconnected(OneshotSender<()>, bool),
/// Set the auto-connect setting.
SetAutoConnect(OneshotSender<()>, bool),
/// Set the mssfix argument for OpenVPN
@@ -455,6 +461,22 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
+ fn set_block_when_disconnected(
+ &self,
+ _: Self::Metadata,
+ block_when_disconnected: bool,
+ ) -> BoxFuture<(), Error> {
+ log::debug!("set_block_when_disconnected({})", block_when_disconnected);
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::SetBlockWhenDisconnected(
+ tx,
+ block_when_disconnected,
+ ))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
+ Box::new(future)
+ }
+
fn set_auto_connect(&self, _: Self::Metadata, auto_connect: bool) -> BoxFuture<(), Error> {
log::debug!("set_auto_connect({})", auto_connect);
let (tx, rx) = sync::oneshot::channel();
diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs
index c38f6d4fc1..7f2c6a17c1 100644
--- a/mullvad-ipc-client/src/lib.rs
+++ b/mullvad-ipc-client/src/lib.rs
@@ -143,6 +143,10 @@ impl DaemonRpcClient {
self.call("set_allow_lan", &[allow_lan])
}
+ pub fn set_block_when_disconnected(&mut self, block_when_disconnected: bool) -> Result<()> {
+ self.call("set_block_when_disconnected", &[block_when_disconnected])
+ }
+
pub fn get_allow_lan(&mut self) -> Result<bool> {
self.call("get_allow_lan", &NO_ARGS)
}
diff --git a/mullvad-types/src/settings.rs b/mullvad-types/src/settings.rs
index 15e336dae4..a6a1b95e2c 100644
--- a/mullvad-types/src/settings.rs
+++ b/mullvad-types/src/settings.rs
@@ -44,6 +44,9 @@ pub struct Settings {
relay_settings: RelaySettings,
/// If the daemon should allow communication with private (LAN) networks.
allow_lan: bool,
+ /// Extra level of kill switch. When this setting is on, the disconnected state will block
+ /// the firewall to not allow any traffic in or out.
+ block_when_disconnected: bool,
/// If the daemon should connect the VPN tunnel directly on start or not.
auto_connect: bool,
/// Options that should be applied to tunnels of a specific type regardless of where the relays
@@ -60,6 +63,7 @@ impl Default for Settings {
tunnel: Constraint::Any,
}),
allow_lan: false,
+ block_when_disconnected: false,
auto_connect: false,
tunnel_options: TunnelOptions::default(),
}
@@ -165,6 +169,19 @@ impl Settings {
}
}
+ pub fn get_block_when_disconnected(&self) -> bool {
+ self.block_when_disconnected
+ }
+
+ pub fn set_block_when_disconnected(&mut self, block_when_disconnected: bool) -> Result<bool> {
+ if block_when_disconnected != self.block_when_disconnected {
+ self.block_when_disconnected = block_when_disconnected;
+ self.save().map(|_| true)
+ } else {
+ Ok(false)
+ }
+ }
+
pub fn get_auto_connect(&self) -> bool {
self.auto_connect
}
diff --git a/talpid-core/src/tunnel_state_machine/blocked_state.rs b/talpid-core/src/tunnel_state_machine/blocked_state.rs
index 3e63551110..14b72a20f8 100644
--- a/talpid-core/src/tunnel_state_machine/blocked_state.rs
+++ b/talpid-core/src/tunnel_state_machine/blocked_state.rs
@@ -58,6 +58,10 @@ impl TunnelState for BlockedState {
Self::set_security_policy(shared_values);
SameState(self)
}
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ SameState(self)
+ }
Ok(TunnelCommand::IsOffline(is_offline)) => {
shared_values.is_offline = is_offline;
if !is_offline && self.block_reason == BlockReason::IsOffline {
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index 577f435260..fa6735f145 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -94,6 +94,10 @@ impl ConnectedState {
}
}
}
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ SameState(self)
+ }
Ok(TunnelCommand::IsOffline(is_offline)) => {
shared_values.is_offline = is_offline;
if is_offline {
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index df0a00744e..e4d81dd51e 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -211,6 +211,10 @@ impl ConnectingState {
}
}
}
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ SameState(self)
+ }
Ok(TunnelCommand::IsOffline(is_offline)) => {
shared_values.is_offline = is_offline;
if is_offline {
diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
index 60444e9cc7..6c57e287a1 100644
--- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
@@ -1,7 +1,8 @@
use super::{
- BlockedState, ConnectingState, Error, EventConsequence, SharedTunnelStateValues, TunnelCommand,
- TunnelState, TunnelStateTransition, TunnelStateWrapper,
+ BlockedState, ConnectingState, EventConsequence, ResultExt, SharedTunnelStateValues,
+ TunnelCommand, TunnelState, TunnelStateTransition, TunnelStateWrapper,
};
+use crate::security::SecurityPolicy;
use error_chain::ChainedError;
use futures::sync::mpsc;
use futures::Stream;
@@ -10,10 +11,23 @@ use futures::Stream;
pub struct DisconnectedState;
impl DisconnectedState {
- fn reset_security_policy(shared_values: &mut SharedTunnelStateValues) {
- if let Err(error) = shared_values.security.reset_policy() {
- let chained_error = Error::with_chain(error, "Failed to reset security policy");
- log::error!("{}", chained_error.display_chain());
+ fn set_security_policy(shared_values: &mut SharedTunnelStateValues) {
+ let result = if shared_values.block_when_disconnected {
+ let policy = SecurityPolicy::Blocked {
+ allow_lan: shared_values.allow_lan,
+ };
+ shared_values
+ .security
+ .apply_policy(policy)
+ .chain_err(|| "Failed to apply blocking security policy for disconnected state")
+ } else {
+ shared_values
+ .security
+ .reset_policy()
+ .chain_err(|| "Failed to reset security policy")
+ };
+ if let Err(error) = result {
+ log::error!("{}", error.display_chain());
}
}
}
@@ -25,8 +39,7 @@ impl TunnelState for DisconnectedState {
shared_values: &mut SharedTunnelStateValues,
_: Self::Bootstrap,
) -> (TunnelStateWrapper, TunnelStateTransition) {
- Self::reset_security_policy(shared_values);
-
+ Self::set_security_policy(shared_values);
(
TunnelStateWrapper::from(DisconnectedState),
TunnelStateTransition::Disconnected,
@@ -42,7 +55,17 @@ impl TunnelState for DisconnectedState {
match try_handle_event!(self, commands.poll()) {
Ok(TunnelCommand::AllowLan(allow_lan)) => {
- shared_values.allow_lan = allow_lan;
+ if shared_values.allow_lan != allow_lan {
+ shared_values.allow_lan = allow_lan;
+ Self::set_security_policy(shared_values);
+ }
+ SameState(self)
+ }
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ if shared_values.block_when_disconnected != block_when_disconnected {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ Self::set_security_policy(shared_values);
+ }
SameState(self)
}
Ok(TunnelCommand::IsOffline(is_offline)) => {
diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
index 7ebb637d2d..928880f274 100644
--- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
@@ -33,6 +33,10 @@ impl DisconnectingState {
shared_values.allow_lan = allow_lan;
AfterDisconnect::Nothing
}
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ AfterDisconnect::Nothing
+ }
Ok(TunnelCommand::IsOffline(is_offline)) => {
shared_values.is_offline = is_offline;
AfterDisconnect::Nothing
@@ -46,6 +50,10 @@ impl DisconnectingState {
shared_values.allow_lan = allow_lan;
AfterDisconnect::Block(reason)
}
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ AfterDisconnect::Block(reason)
+ }
Ok(TunnelCommand::IsOffline(is_offline)) => {
shared_values.is_offline = is_offline;
if !is_offline && reason == BlockReason::IsOffline {
@@ -64,6 +72,10 @@ impl DisconnectingState {
shared_values.allow_lan = allow_lan;
AfterDisconnect::Reconnect(retry_attempt)
}
+ Ok(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
+ shared_values.block_when_disconnected = block_when_disconnected;
+ AfterDisconnect::Reconnect(retry_attempt)
+ }
Ok(TunnelCommand::IsOffline(is_offline)) => {
shared_values.is_offline = is_offline;
if is_offline {
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index e44ec91234..d952e458f2 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -43,6 +43,7 @@ error_chain! {
/// Spawn the tunnel state machine thread, returning a channel for sending tunnel commands.
pub fn spawn<P, T>(
allow_lan: bool,
+ block_when_disconnected: bool,
tunnel_parameters_generator: impl TunnelParametersGenerator,
log_dir: Option<PathBuf>,
resource_dir: PathBuf,
@@ -62,6 +63,7 @@ where
thread::spawn(move || {
match create_event_loop(
allow_lan,
+ block_when_disconnected,
is_offline,
tunnel_parameters_generator,
log_dir,
@@ -97,6 +99,7 @@ where
fn create_event_loop<T>(
allow_lan: bool,
+ block_when_disconnected: bool,
is_offline: bool,
tunnel_parameters_generator: impl TunnelParametersGenerator,
log_dir: Option<PathBuf>,
@@ -111,6 +114,7 @@ where
let reactor = Core::new().chain_err(|| ErrorKind::ReactorError)?;
let state_machine = TunnelStateMachine::new(
allow_lan,
+ block_when_disconnected,
is_offline,
tunnel_parameters_generator,
log_dir,
@@ -132,6 +136,8 @@ where
pub enum TunnelCommand {
/// Enable or disable LAN access in the firewall.
AllowLan(bool),
+ /// Enable or disable the block_when_disconnected feature.
+ BlockWhenDisconnected(bool),
/// Notify the state machine of the connectivity of the device.
IsOffline(bool),
/// Open tunnel connection.
@@ -168,6 +174,7 @@ struct TunnelStateMachine {
impl TunnelStateMachine {
fn new(
allow_lan: bool,
+ block_when_disconnected: bool,
is_offline: bool,
tunnel_parameters_generator: impl TunnelParametersGenerator,
log_dir: Option<PathBuf>,
@@ -180,6 +187,7 @@ impl TunnelStateMachine {
let mut shared_values = SharedTunnelStateValues {
security,
allow_lan,
+ block_when_disconnected,
is_offline,
tunnel_parameters_generator: Box::new(tunnel_parameters_generator),
log_dir,
@@ -258,6 +266,8 @@ struct SharedTunnelStateValues {
security: NetworkSecurity,
/// Should LAN access be allowed outside the tunnel.
allow_lan: bool,
+ /// Should network access be allowed when in the disconnected state.
+ block_when_disconnected: bool,
/// True when the computer is known to be offline.
is_offline: bool,
/// The generator of new `TunnelParameter`s