summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/security.md7
-rw-r--r--talpid-core/src/dns/linux/network_manager.rs7
-rw-r--r--talpid-core/src/firewall/linux.rs19
-rw-r--r--talpid-core/src/firewall/mod.rs8
-rw-r--r--talpid-core/src/linux.rs3
-rw-r--r--talpid-core/src/process/openvpn.rs7
-rw-r--r--talpid-core/src/tunnel/wireguard/config.rs8
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs35
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs21
-rw-r--r--talpid-types/src/net/mod.rs19
10 files changed, 88 insertions, 46 deletions
diff --git a/docs/security.md b/docs/security.md
index 7fc5369bd3..786cf99d8b 100644
--- a/docs/security.md
+++ b/docs/security.md
@@ -124,8 +124,11 @@ VPN tunnel is allowed on all interfaces, together with responses to this outgoin
First hop means the bridge server if one is used, otherwise the VPN server directly.
This IP+port+protocol combination should only be allowed for the process establishing the
VPN tunnel, or only administrator level processes, depending on what the platform firewall
-allows restricting. On Windows the rule only allows processes from binaries in certain paths.
-On Linux and macOS the rule only allows packets from processes running as `root`.
+allows restricting. On Windows the rule only allows processes from binaries in certain paths. macOS
+the rule only allows packets from processes running as `root`. On Linux, the rule only allows
+packets that have the mark `0x6d6f6c65` set: setting a firewall mark on traffic requires elevated
+privileges when using tunnels that support marking traffic, otherwise the rule is the same as on
+macOS: the packet needs to originate from a process running as `root`.
This process/user check is important to not allow unprivileged programs
to leak packets to this IP outside the tunnel, as those packets can be fingerprinted.
diff --git a/talpid-core/src/dns/linux/network_manager.rs b/talpid-core/src/dns/linux/network_manager.rs
index 0e517e07f1..5a17c3c59e 100644
--- a/talpid-core/src/dns/linux/network_manager.rs
+++ b/talpid-core/src/dns/linux/network_manager.rs
@@ -248,6 +248,13 @@ impl NetworkManager {
Self::update_dns_config(&mut settings, "ipv6", v6_dns);
}
+ if let Some(wg_config) = settings.get_mut("wireguard") {
+ wg_config.insert(
+ "fwmark",
+ Variant(Box::new(crate::linux::TUNNEL_FW_MARK) as Box<dyn RefArg>),
+ );
+ }
+
self.reapply_settings(&device, settings, version_id)?;
self.device = Some(device);
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs
index 3a68d3734d..f4a3e07378 100644
--- a/talpid-core/src/firewall/linux.rs
+++ b/talpid-core/src/firewall/linux.rs
@@ -445,9 +445,11 @@ impl<'a> PolicyBatch<'a> {
peer_endpoint,
pingable_hosts,
allow_lan,
+ use_fwmark,
} => {
self.add_allow_icmp_pingable_hosts(&pingable_hosts);
- self.add_allow_endpoint_rules(peer_endpoint);
+ self.add_allow_endpoint_rules(peer_endpoint, *use_fwmark);
+
// Important to block DNS after allow relay rule (so the relay can operate
// over port 53) but before allow LAN (so DNS does not leak to the LAN)
self.add_drop_dns_rule();
@@ -457,8 +459,9 @@ impl<'a> PolicyBatch<'a> {
peer_endpoint,
tunnel,
allow_lan,
+ use_fwmark,
} => {
- self.add_allow_endpoint_rules(peer_endpoint);
+ self.add_allow_endpoint_rules(peer_endpoint, *use_fwmark);
self.add_allow_dns_rules(tunnel, TransportProtocol::Udp)?;
self.add_allow_dns_rules(tunnel, TransportProtocol::Tcp)?;
// Important to block DNS *before* we allow the tunnel and allow LAN. So DNS
@@ -492,7 +495,7 @@ impl<'a> PolicyBatch<'a> {
Ok(())
}
- fn add_allow_endpoint_rules(&mut self, endpoint: &Endpoint) {
+ fn add_allow_endpoint_rules(&mut self, endpoint: &Endpoint, use_fwmark: bool) {
let mut in_rule = Rule::new(&self.in_chain);
check_endpoint(&mut in_rule, End::Src, endpoint);
@@ -504,11 +507,15 @@ impl<'a> PolicyBatch<'a> {
self.batch.add(&in_rule, nftnl::MsgType::Add);
-
let mut out_rule = Rule::new(&self.out_chain);
check_endpoint(&mut out_rule, End::Dst, endpoint);
- out_rule.add_expr(&nft_expr!(meta skuid));
- out_rule.add_expr(&nft_expr!(cmp == 0u32));
+ if use_fwmark {
+ out_rule.add_expr(&nft_expr!(meta mark));
+ out_rule.add_expr(&nft_expr!(cmp == crate::linux::TUNNEL_FW_MARK));
+ } else {
+ out_rule.add_expr(&nft_expr!(meta skuid));
+ out_rule.add_expr(&nft_expr!(cmp == 0u32));
+ }
add_verdict(&mut out_rule, &Verdict::Accept);
self.batch.add(&out_rule, nftnl::MsgType::Add);
diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs
index 6b65dd5b70..4d8d3f0459 100644
--- a/talpid-core/src/firewall/mod.rs
+++ b/talpid-core/src/firewall/mod.rs
@@ -97,6 +97,10 @@ pub enum FirewallPolicy {
/// A process that is allowed to send packets to the relay.
#[cfg(windows)]
relay_client: PathBuf,
+ /// Whether rule for allowing traffic to endpoint should match a firewall mark or match on
+ /// root UID.
+ #[cfg(target_os = "linux")]
+ use_fwmark: bool,
},
/// Allow traffic only to server and over tunnel interface
@@ -110,6 +114,10 @@ pub enum FirewallPolicy {
/// A process that is allowed to send packets to the relay.
#[cfg(windows)]
relay_client: PathBuf,
+ /// Whether rule for allowing traffic to endpoint should match a firewall mark or match on
+ /// root UID.
+ #[cfg(target_os = "linux")]
+ use_fwmark: bool,
},
/// Block all network traffic in and out from the computer.
diff --git a/talpid-core/src/linux.rs b/talpid-core/src/linux.rs
index 05655bf4be..47d0714813 100644
--- a/talpid-core/src/linux.rs
+++ b/talpid-core/src/linux.rs
@@ -25,3 +25,6 @@ pub enum IfaceIndexLookupError {
#[error(display = "Failed to get index for interface {}", _0)]
InterfaceLookupError(String, #[error(source)] io::Error),
}
+
+// b"mole" is [ 0x6d, 0x6f 0x6c, 0x65 ]
+pub const TUNNEL_FW_MARK: u32 = 0x6d6f6c65;
diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs
index e07be4fa16..7922ba952c 100644
--- a/talpid-core/src/process/openvpn.rs
+++ b/talpid-core/src/process/openvpn.rs
@@ -249,6 +249,13 @@ impl OpenVpnCommand {
args.extend(Self::tls_cipher_arguments().iter().map(OsString::from));
args.extend(self.proxy_arguments().iter().map(OsString::from));
+ #[cfg(target_os = "linux")]
+ args.extend(
+ ["--mark", &crate::linux::TUNNEL_FW_MARK.to_string()]
+ .iter()
+ .map(OsString::from),
+ );
+
args
}
diff --git a/talpid-core/src/tunnel/wireguard/config.rs b/talpid-core/src/tunnel/wireguard/config.rs
index 4f0e851b1e..87c0d2c0f0 100644
--- a/talpid-core/src/tunnel/wireguard/config.rs
+++ b/talpid-core/src/tunnel/wireguard/config.rs
@@ -17,6 +17,9 @@ pub struct Config {
pub ipv6_gateway: Option<Ipv6Addr>,
/// Maximum transmission unit for the tunnel
pub mtu: u16,
+ /// Firewall mark
+ #[cfg(target_os = "linux")]
+ pub fwmark: u32,
}
const DEFAULT_MTU: u16 = 1380;
@@ -96,6 +99,8 @@ impl Config {
ipv4_gateway: connection_config.ipv4_gateway,
ipv6_gateway,
mtu,
+ #[cfg(target_os = "linux")]
+ fwmark: crate::linux::TUNNEL_FW_MARK,
})
}
@@ -108,6 +113,9 @@ impl Config {
.add("private_key", self.tunnel.private_key.to_bytes().as_ref())
.add("listen_port", "0");
+ #[cfg(target_os = "linux")]
+ wg_conf.add("fwmark", self.fwmark.to_string().as_str());
+
wg_conf.add("replace_peers", "true");
for peer in &self.peers {
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index 43595a4e79..dd259a87d9 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -11,7 +11,7 @@ use futures01::{
Async, Future, Stream,
};
use talpid_types::{
- net::{Endpoint, TunnelParameters},
+ net::TunnelParameters,
tunnel::{ErrorStateCause, FirewallPolicyError},
BoxedError, ErrorExt,
};
@@ -52,19 +52,7 @@ impl ConnectedState {
&self,
shared_values: &mut SharedTunnelStateValues,
) -> Result<(), FirewallPolicyError> {
- // If a proxy is specified we need to pass it on as the peer endpoint.
- let peer_endpoint = self.get_endpoint_from_params();
-
- let policy = FirewallPolicy::Connected {
- peer_endpoint,
- tunnel: self.metadata.clone(),
- allow_lan: shared_values.allow_lan,
- #[cfg(windows)]
- relay_client: TunnelMonitor::get_relay_client(
- &shared_values.resource_dir,
- &self.tunnel_parameters,
- ),
- };
+ let policy = self.get_firewall_policy(shared_values);
shared_values
.firewall
.apply_policy(policy)
@@ -85,13 +73,18 @@ impl ConnectedState {
})
}
- fn get_endpoint_from_params(&self) -> Endpoint {
- match self.tunnel_parameters {
- TunnelParameters::OpenVpn(ref params) => match params.proxy {
- Some(ref proxy_settings) => proxy_settings.get_endpoint().endpoint,
- None => params.config.endpoint,
- },
- TunnelParameters::Wireguard(ref params) => params.connection.get_endpoint(),
+ fn get_firewall_policy(&self, shared_values: &SharedTunnelStateValues) -> FirewallPolicy {
+ FirewallPolicy::Connected {
+ peer_endpoint: self.tunnel_parameters.get_next_hop_endpoint(),
+ tunnel: self.metadata.clone(),
+ allow_lan: shared_values.allow_lan,
+ #[cfg(windows)]
+ relay_client: TunnelMonitor::get_relay_client(
+ &shared_values.resource_dir,
+ &self.tunnel_parameters,
+ ),
+ #[cfg(target_os = "linux")]
+ use_fwmark: self.tunnel_parameters.get_proxy_endpoint().is_none(),
}
}
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index d206b34a23..859ef52eb0 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -22,7 +22,7 @@ use std::{
time::{Duration, Instant},
};
use talpid_types::{
- net::{openvpn, TunnelParameters},
+ net::TunnelParameters,
tunnel::{ErrorStateCause, FirewallPolicyError},
ErrorExt,
};
@@ -48,13 +48,7 @@ impl ConnectingState {
shared_values: &mut SharedTunnelStateValues,
params: &TunnelParameters,
) -> Result<(), FirewallPolicyError> {
- let proxy = &get_openvpn_proxy_settings(&params);
- let endpoint = params.get_tunnel_endpoint().endpoint;
-
- let peer_endpoint = match proxy {
- Some(proxy_settings) => proxy_settings.get_endpoint().endpoint,
- None => endpoint,
- };
+ let peer_endpoint = params.get_next_hop_endpoint();
let policy = FirewallPolicy::Connecting {
peer_endpoint,
@@ -62,6 +56,8 @@ impl ConnectingState {
allow_lan: shared_values.allow_lan,
#[cfg(windows)]
relay_client: TunnelMonitor::get_relay_client(&shared_values.resource_dir, &params),
+ #[cfg(target_os = "linux")]
+ use_fwmark: params.get_proxy_endpoint().is_none(),
};
shared_values
.firewall
@@ -312,15 +308,6 @@ impl ConnectingState {
}
}
-fn get_openvpn_proxy_settings(
- tunnel_parameters: &TunnelParameters,
-) -> &Option<openvpn::ProxySettings> {
- match tunnel_parameters {
- TunnelParameters::OpenVpn(ref config) => &config.proxy,
- _ => &None,
- }
-}
-
fn should_retry(error: &tunnel::Error) -> bool {
#[cfg(not(windows))]
use tunnel::wireguard::{Error, TunnelError};
diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs
index 97292fd381..15bf33a5bb 100644
--- a/talpid-types/src/net/mod.rs
+++ b/talpid-types/src/net/mod.rs
@@ -37,6 +37,25 @@ impl TunnelParameters {
}
}
+ // Returns the endpoint that will be connected to
+ pub fn get_next_hop_endpoint(&self) -> Endpoint {
+ match self {
+ TunnelParameters::OpenVpn(params) => params
+ .proxy
+ .as_ref()
+ .map(|proxy| proxy.get_endpoint().endpoint)
+ .unwrap_or(params.config.endpoint),
+ TunnelParameters::Wireguard(params) => params.connection.get_endpoint(),
+ }
+ }
+
+ pub fn get_proxy_endpoint(&self) -> Option<openvpn::ProxySettings> {
+ match self {
+ TunnelParameters::OpenVpn(params) => params.proxy.clone(),
+ _ => None,
+ }
+ }
+
pub fn get_generic_options(&self) -> &GenericTunnelOptions {
match &self {
TunnelParameters::OpenVpn(params) => &params.generic_options,