diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2020-01-07 21:50:56 +0100 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2020-01-07 21:50:56 +0100 |
| commit | a846f29fe7cb237d73295d2f33775e0749e19f60 (patch) | |
| tree | 77a60c4848b828f723ff8a1795b199c9c8da8580 | |
| parent | df1c08b5a6ecfbc51e6b06c18481d16c614e494f (diff) | |
| parent | ddce319e802f08a082754aa5f073d03c8c3060f8 (diff) | |
| download | mullvadvpn-a846f29fe7cb237d73295d2f33775e0749e19f60.tar.xz mullvadvpn-a846f29fe7cb237d73295d2f33775e0749e19f60.zip | |
Merge branch 'security-docs'
| -rw-r--r-- | README.md | 19 | ||||
| -rw-r--r-- | docs/security.md | 241 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 34 |
3 files changed, 254 insertions, 40 deletions
@@ -1,6 +1,14 @@ # Mullvad VPN desktop and mobile app -The system service/daemon, GUI and CLI for the Mullvad VPN app. +Welcome to the Mullvad VPN client app. This repository contains all the source code for the +desktop and mobile versions of the app. For desktop this includes the system service/daemon +([`mullvad-daemon`](mullvad-daemon/)), a graphical user interface ([GUI](gui/)) and a +command line interface ([CLI](mullvad-cli/)). + +The Android app uses the same backing system service for the tunnel and security but has +a dedicated frontend in [android/](android/). + +iOS consists of a completely standalone implementation that resides in [ios/](ios/) ## Releases @@ -24,6 +32,15 @@ state of latest master, not necessarily any existing release. | OpenVPN over Shadowsocks | ✓ | ✓ | ✓ | | | Optional local network access | ✓ | ✓ | ✓ | | +## Security and anonymity + +This app is a privacy preserving VPN client. As such it goes to great lengths to stop traffic +leaks. And basically all settings default to the more secure/private option. The user has to +explicitly allow more loose rules if desired. See the [dedicated security document] for details +on what the app blocks and allows and how it does it. + +[dedicated security document]: docs/security.md + ## Checking out the code This repository contains submodules needed for building the app. However, some of those submodules diff --git a/docs/security.md b/docs/security.md index af2d8f5f3d..5e913e5420 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,30 +1,257 @@ # Mullvad VPN app security This document describes the security properties of the Mullvad VPN app. It describes it for all -platforms and their differences. +platforms and their differences. Individual platforms might have slightly different properties and +allow or block network traffic a bit differently, but all such deviations are described here. -This document does not describe *how* we reach and uphold these properties, just what they are. -See the [architecture](architecture.md) document for details on how this security is implemented. +This document does not describe in detail *how* we reach and uphold these properties, just what +they are. See the [architecture](architecture.md) document for details on how the firewall +integration is implemented. +The main purpose of the app is to allow the user to make all network/internet traffic to and +from the device travel via an encrypted VPN tunnel. + +## Desktop vs mobile + +For desktop operating systems, the security is ensured via tight integration with the default +system firewall. This means WFP on Windows, PF on macOS and nftables on Linux. All changes to +the rules are applied as atomic transactions. This means that there is no time window of +inconsistent or invalid rules during changes. + +On mobile, Android and iOS, it is not possible for apps to directly access and manipulate the +firewall, routing table or DNS settings. There we employ other techniques to keep the system as +secure as possible with the limitations of the OS APIs. + +### Android + +On Android, the only way an app can filter network traffic is essentially via the VPN service API. +This API allows all traffic to and from the phone to flow though a third party app. This API is of +course what the app uses for the tunnel itself as well, but apart from that it is also what the +leak protection is built on. + +An app with permission to act as a VPN service can request to open a VPN tunnel on the device and +provide a set of IP networks it would like to have routed via itself. Doing so and specifying +the routes `0/0` and `::0/0` forces all traffic to go via the app. That is what this app does both +when it has a VPN tunnel up, but also when in a state where it would like to block all network +traffic. Such as the [connecting], [disconnecting] and [blocked] states. In these states, all +packets are simply dropped. + +### iOS + +On iOS a designated packet tunnel process handles the network packet flow. iOS implementation +delegates the traffic handling to wireguard-go, which works directly with the tun interface. +The network configuration set up by the packet tunnel extension specifies the routing rules +that all traffic should flow through the tunnel, the same way it works on Android. + +The iOS app currently does not support blocking in the app's blocked state. ## App states +At the core of the app is a state machine called the "tunnel state machine". The following +sub-sections will describe each state and what security properties hold and what network activity +will be blocked and allowed in each state. + +Except what is described as allowed in this document, all network packets should be blocked. + +The following network traffic is allowed or blocked independent of state: + +1. All traffic on loopback adapters is always allowed. + +1. DHCPv4 and DHCPv6 requests are always allowed to go out and responses to come in: + * Outgoing UDP from `*:68` to `255.255.255.255:67` (client to server) + * Incoming UDP `*:67` to `*:68` (server to client) + * Outgoing UDP from `[fe80::]/10:546` to `[ff02::1:2]:547` and `[ff05::1:3]:547` (client to + server) + * Incoming UDP from `[fe80::]/10:547` to `[fe80::]/10:546` (server to client) + +1. Router solicitation, advertisement and redirects (subset of NDP) is always allowed: + * Outgoing to `ff02::2`, but only ICMPv6 with type 133 and code 0 (Router solicitation) + * Incoming from `[fe80::]/10`, but only ICMPv6 type 134 and code 0 (Router advertisement) + * Incoming from `[fe80::]/10`, but only ICMPv6 type 137 and code 0 (Redirect) + +1. If the "Allow LAN" setting is enabled, the following is also allowed: + * Outgoing to, and incoming from, any IP in an unroutable network, that means: + * `10.0.0.0/8` + * `172.16.0.0/12` + * `192.168.0.0/16` + * `169.254.0.0/16` + * `fe80::/10` + * Outgoing to any IP in a local, unroutable, multicast network, meaning these: + * `224.0.0.0/24` (local subnet IPv4 multicast) + * `239.255.255.250/32` (SSDP) + * `239.255.255.251/32` (mDNS) + * `ff02::/16` (Link-local IPv6 multicast. IPv6 equivalent of `224.0.0.0/24`) + * `ff05::/16` (Site-local IPv6 multicast. Is routable, but should never leave the "site") + * Incoming DHCPv4 requests and outgoing responses (be a DHCPv4 server): + * Incoming UDP from `*:68` to `255.255.255.255:67` + * Outgoing UDP from `*:67` to `*:68` + +#### macOS deviations + +* The app does not look at ICMPv6 type and code headers. So all ICMPv6 is allowed between the + specified IP networks. + ### Disconnected +This is the default state that the `mullvad-daemon` starts in when the device boots, unless +"Launch app on start-up" and "Auto-connect" are **both** active. Then the app will proceed to the +[connecting] state immediately. + +The disconnected state behaves very differently depending on the value of the +"block when disconnected" setting. If this setting is enabled, the disconnected state behaves +like and has the same security properties as, the [blocked] state. If the setting is +disabled (the default), then it is the only state where the app does not enforce any firewall +rules. It then behaves the same as if the `mullvad-daemon` was not even running. It lets +network traffic flow in and out of the computer freely. + +The disconnected state is not active while the app changes server or if the VPN tunnel goes down +unexpectedly. See the [connecting] state and [kill switch](#kill-switch) documentation for these +unexpected network issues. The only time this state is active is initially when the daemon +starts and later when the user explicitly clicks the disconnect/cancel button to intentionally +disable the VPN. + ### Connecting +This state is active from when the app decides to create a VPN tunnel, until said tunnel has +been established and verified to work. Then it transitions to the [connected] state. + +In this state, network traffic to the IP+port+protocol combination used for the first hop of the +VPN tunnel is allowed on all interfaces, together with responses to this outgoing traffic. +First hop means the bridge server if one is used, otherwise the VPN server directly. +Examples: +1. No bridge is used and the tunnel protocol is OpenVPN trying to connect with UDP to a VPN + server at IP `a.b.c.d` port `1301` - Allow traffic to `a.b.c.d:1301/UDP` and incoming matching + traffic. +1. Connecting to the same VPN server, but via a bridge. The bridge is at IP `e.f.g.h` and the + proxy service listens on TCP port `443` - Allow traffic to `e.f.g.h:443/TCP` and incoming matching + traffic. Do not allow any direct communication with the VPN server. + +If connecting via WireGuard, this state allows ICMP packets to and from the in-tunnel IPs +(both v4 and v6) of the relay server the app is currently connecting to. That means the private +network IPs where the relay will respond inside the tunnel. It allows this on all interfaces, +since with the current architecture we don't know which network interface is the tunnel interface +at this point. + ### Connected +This state becomes active when [connecting] has fully established a VPN tunnel. It +stays active until the user requests a disconnect, quit, server change, change of other setting +that affects the tunnel or until the tunnel goes down unexpectedly. + +In this state, all traffic in both directions over the tunnel interface is allowed. Minus DNS +requests (TCP and UDP destination port 53) not to a gateway IP on the tunnel interface. +Meaning we can *only* request DNS inside the tunnel and *only* from the relay server itself. + +This state allows traffic on all interfaces to and from the IP+port+protocol combination that +the tunnel runs over. See the [connecting] state for details on this rule. + ### Disconnecting +This state becomes active if there is a VPN tunnel active but the app decides to close said +tunnel. This state is active until the tunnel has been properly closed. + +This state does not apply its own security policy on the firewall. It just keeps what was already +active. All states transitioning into this state, and all states this state later +transitions to, have their own security policies. This state is just a short transition between +those, while the app waits for a running tunnel to come down and clean up after itself. + ### Blocked +This state is only active when there is a problem/error. As described in other sections, the app +will never unlock the firewall and allow network traffic outside the tunnel unless a +disconnect/quit is explicitly requested by the user. At the same time there might be situations +when the app can't establish a tunnel for the device. This includes, but is not limited to: +* Account runs out of time +* The computer is offline +* the TAP adapter driver has an error or the adapter can't be found (Windows) +* Some internal error parsing or modifying system routing table, DNS settings etc. + +In the above cases the app gives up trying to create a tunnel, but it can't go to the +[disconnected] state, since it should not unlock the firewall. Then it enters this state. +This state locks the firewall so no traffic can flow (except the always active exceptions) and +informs the user what the problem is. The user must then explicitly click disconnect in order +to unlock the firewall and get access to the internet again. + +## Kill switch + +The app has an always on "kill switch" that can't be disabled. There is no setting for it. +This means that whenever the app changes server or temporarily loses tunnel connectivity it will +ensure no network traffic leaks out unencrypted. + +The app avoids the term "kill switch". Because it sounds like a red button +that has to be *engaged when a problem arises*. This app is much more proactive and applies +[strict firewall rules](#app-states) directly when it leaves the [disconnected] +state and keeps those rules active and enforced until the app comes back to the [disconnected] +state via an explicit user request again. Said strict firewall rules unsure that packets can only +leave or enter the computer in a few predefined ways, most notably to the selected VPN server of +course. Changes to the firewall are done in atomic transactions. Meaning there is no time window +where no or invalid rules are active on the device. + +If the tunnel were to come down and your operating system tries to route packets out via the +normal network rather than through the VPN, these rules would block them from leaving. +So rather than failing open, meaning if the tunnel fails your traffic leaves in other ways, +we fail closed, meaning if the packets don't leave encrypted in the way the app intends, +then they can't leave at all. + +Essentially, one can say that the app's "kill switch" is the fact that the [connecting], +[disconnecting] and [blocked] states prevent leaks via firewall rules. -## Firewall +### Block when disconnected -The states above should probably explain what can and can't be reached in the different states. -But we might need/want this section in case there is something that does not fit above. +The "block when disconnected" setting in the app is regularly misunderstood as the kill switch. +This is not the case. The "block when disconnected" setting only changes whether or not the +[disconnected] state should allow traffic to flow freely or to block it. The +disconnected state is not active during intermittent network issues or server changes, when +a kill switch would normally be operating. + +The intended use case for this setting is when the user want to only switch between no internet +connectivity at all and using VPN. With this setting active, the device can never communicate +with the internet outside of a VPN tunnel. ## DNS -Where are DNS requests sent? +DNS is treated a bit differently from other protocols. Since a user's DNS history can give a +detailed view of what they are doing, it is important to not leak it. +Since an invalid or missing DNS response prevents the user from going where they want to go, +it is important that it works and gives correct replies, from an anti-censorship point of view. +Poisoned DNS replies is a very common way of censoring the network in many places. + +With the above as background, the app makes sure that every DNS request from the device goes +inside the VPN tunnel and to exactly one place, the VPN relay server the device is currently +connected to. That ensures the request reaches the Mullvad infrastructure and does so safely +(encrypted). From there the Mullvad servers are responsible for delivering a correct and +uncensored reply. + +The above holds during the [connected] state. In the [disconnected] +state the app does nothing with DNS, meaning the default one is used, probably from the ISP. +In the other states DNS is simply blocked. + +## Desktop system service + +On all desktop platforms the VPN tunnel and the device security is handled by a system +service called `mullvad-daemon`. This service is installed as the administrator/root user +during app install and is then always running in the background, even when the user +quits the GUI and when no tunnels are running. + +This system service can be controlled via a management interface, exposed locally +via unix domain sockets (UDS) on Linux and macOS and via named pipes on Windows. +This management interface can be reached by any process running on the device. +Locally running malicious programs are outside of the app's threat model. + +## Desktop Electron GUI + +The graphical frontend for the app on desktop is an Electron app. This app only ever loads +local resources in the form of html, CSS and Javascript directly from the installation +directory of the app, and never from remote sources. + +The GUI only communicates with the system service (`mullvad-daemon`), it makes no other +network connections. Except when it sends a problem report, then it spawn the +`mullvad-problem-report` tool, which in turn communicate over TLS with our API. + + +[disconnected]: #disconnected +[connecting]: #connecting +[connected]: #connected +[disconnecting]: #disconnecting +[blocked]: #blocked +[GUI]: #desktop-electron-gui diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs index 0ed21fd6be..93279437a1 100644 --- a/talpid-core/src/firewall/mod.rs +++ b/talpid-core/src/firewall/mod.rs @@ -73,38 +73,8 @@ const DHCPV6_CLIENT_PORT: u16 = 546; /// /// # Firewall block/allow specification. /// -/// Except what's described as allowed below, all network packets should be blocked. -/// -/// ## In all policies the firewall should always allow the following traffic -/// -/// 1. All traffic on loopback adapters -/// 2. DHCPv4 and DHCPv6 requests to go out and responses to come in: -/// * Outgoing from *:DHCPV4_CLIENT_PORT to 255.255.255.255:DHCPV4_SERVER_PORT -/// * Incoming *:DHCPV4_SERVER_PORT to *:DHCPV4_CLIENT_PORT -/// * Outgoing from IPV6_LINK_LOCAL:DHCPV6_CLIENT_PORT to DHCPV6_SERVER_ADDRS:DHCPV6_SERVER_PORT -/// * Incoming from IPV6_LINK_LOCAL:DHCPV6_SERVER_PORT to IPV6_LINK_LOCAL:DHCPV6_CLIENT_PORT -/// 3. Router solicitation, advertisement and redirects (subset of NDP): -/// * Outgoing to ROUTER_SOLICITATION_OUT_DST_ADDR, but only ICMPv6 with type 133 and code 0. -/// * Incoming from IPV6_LINK_LOCAL, but only ICMPv6 type 134 or 137 and code 0. -/// 4. If `allow_lan` is enabled, all policies should allow the following traffic: -/// * Outgoing to, and incoming from, any IP in the networks listed in ALLOWED_LAN_NETS -/// * Outgoing to any IP in the networks listed in ALLOWED_LAN_MULTICAST_NETS -/// * Incoming DHCPv4 requests and outgoing responses (be a DHCPv4 server): -/// * Incoming from *:DHCPV4_CLIENT_PORT to 255.255.255.255:DHCPV4_SERVER_PORT -/// * Outgoing from *:DHCPV4_SERVER_PORT to *:DHCPV4_CLIENT_PORT -/// -/// ## Policy specific rules -/// -/// 1. In the `Connecting` and `Connected` policies traffic should be allowed to and from the IP and -/// port in `peer_endpoint` -/// 2. In the `Connecting` policy, ICMP packets should be allowed to and from all IPs in -/// `pingable_hosts`. -/// 3. In the `Connected` policy, DNS requests (destination port 53 on both UDP and TCP) should be -/// allowed over the tunnel interface in `tunnel.interface` and to the IPs `tunnel.ipv4_gateway` -/// and `tunnel.ipv6_gateway`. But blocked to all other destinations and over all other -/// interfaces. -/// 4. In the `Connected` policy, all traffic should be allowed over the tunnel interface in -/// `tunnel.interface`, minus the DNS packets described above. +/// See the [security](../../../docs/security.md) document for the specification on how to +/// implement these policies and what should and should not be allowed to flow. #[derive(Debug, Clone, Eq, PartialEq)] pub enum FirewallPolicy { /// Allow traffic only to server |
