diff options
| author | Emīls <emils@mullvad.net> | 2022-05-31 15:14:36 +0100 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2022-05-31 15:14:36 +0100 |
| commit | c5ee2bc87d0bb42808fa0542ed00fe6257932ec1 (patch) | |
| tree | efd68ff5ac573c4185c0abcf8b3b4c1f325881e7 | |
| parent | 083d294309978a8cb96a4afc8abc0b4854289213 (diff) | |
| parent | 3c1d2fcbcdbbf6414ffb8dee1283b9e883aef8ce (diff) | |
| download | mullvadvpn-c5ee2bc87d0bb42808fa0542ed00fe6257932ec1.tar.xz mullvadvpn-c5ee2bc87d0bb42808fa0542ed00fe6257932ec1.zip | |
Merge branch 'add-docs'
| -rw-r--r-- | docs/architecture.md | 58 | ||||
| -rw-r--r-- | docs/diagrams/daemon-components.png | bin | 0 -> 443114 bytes | |||
| -rw-r--r-- | docs/diagrams/daemon-components.puml | 66 | ||||
| -rw-r--r-- | docs/diagrams/readme.md | 6 | ||||
| -rw-r--r-- | docs/diagrams/set-allow-lan.png | bin | 0 -> 119487 bytes | |||
| -rw-r--r-- | docs/diagrams/set-allow-lan.puml | 21 | ||||
| -rw-r--r-- | docs/diagrams/tsm-connecting-enter.png | bin | 0 -> 233576 bytes | |||
| -rw-r--r-- | docs/diagrams/tsm-connecting-enter.puml | 26 | ||||
| -rw-r--r-- | docs/diagrams/update-relay-constraints.png | bin | 0 -> 240478 bytes | |||
| -rw-r--r-- | docs/diagrams/update-relay-constraints.puml | 28 |
10 files changed, 198 insertions, 7 deletions
diff --git a/docs/architecture.md b/docs/architecture.md index c0186f76c9..3c61dcfba1 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,3 +1,6 @@ + + + # Mullvad VPN app architecture This document describes the code architecture and how everything fits together. @@ -15,12 +18,48 @@ My thought was that after this section every aspect of the app is explained under either the Mullvad or the Talpid header. So it's clear which part they belong to. I yet don't know if this makes sense though. +## The daemon +The client consists of two main parts - the daemon and the GUI, but there's also a CLI. The daemon +is the process that's responsible for upholding the security guarantees of the client, it consists +of an actor system so that it can drive many processes asynchronously, allowing for a multiplicity +of clients. + +<img src="diagrams/daemon-components.png"> + +The daemon receives commands from the [front-ends](#frontends) (GUI and CLI) via the management +interface and these are serviced asynchronously via the daemon, often interacting via various other +components during and after the RPC has been finished. There are various dependencies between the +different components and it's important to ensure no deadlocks occur, and some executions flows that +provide essential behavior can be hard to trace. + +<img src="diagrams/update-relay-constraints.png"> + ## Mullvad part of daemon ### Frontend <-> system service communication +This is done via GRPC ([here's](../mullvad-management-interface/proto/management_interface.proto) +the relevant proto file) over a Unix domain socket on desktop platforms and via a JNI on Android. In +both cases, the frontends end up sending a message to the daemon that it then services. The +servicing of any message must never block any other message. Frontends can also subscribe to +messages from the daemon, to receive updates about the tunnel state, new settings, new relay lists, +version information and device events. + -### Talking to api.mullvad.net +### Talking to api.mullvad.net. +Reaching the API is done via a direct TLS connection to the API host or via a shadowsocks bridge, to +increase censorship resistance. There are multiple components that interact with the API in an +asynchronous fashion, so to manage them all, there's an actor that specifically deals with sending +REST requests to the API. + +The API must be reachable in secured states even if a tunnel is not up, so the API runtime interacts +with the [tunnel state machine](#tunnel-state-machine) to send over the currently used API endpoint. +This can be a source of deadlocks, so no actor should ever be blocked by an API request if the TSM +relies on it to change states. + +All API requests can be dropped in flight to allow for resetting the connection whenever a tunnel is +established. The API requests can also be blocked when in the offline state or when it's assumed +that the user hasn't used our client for a period of time. ### Selecting relay and bridge servers See [this document](relay-selector.md). @@ -92,7 +131,14 @@ A high-level overview of the tunnel state machine can be seen in the diagram bel #### State machine inputs There are two types of inputs that the tunnel state machine react to. The first one is commands sent -to the state machine, and the second is external events that the state machine listens to. +to the state machine, and the second is external events that the state machine listens to. Reacting +to any event can result in the state machine transitioning away to a different state. + +<img src="diagrams/tsm-connecting-enter.png"> + + + + ##### Tunnel commands @@ -176,11 +222,9 @@ Linux. #### macOS -On macOS, the offline monitor uses [`SCNetworkReachability`] callbacks to detect changes in -connectivity and then enumerates network interfaces via [`SCDynamicStore`] and assumes connectivity -if an active physical interface exists. The interfaces are enumerated because sometimes -`SCNetworkReachability` can trigger a callback signalling full connectivity even when there exists -no default route. +On macOS, the offline monitor uses `route -n monitor -` to listen for changes in the routing table, +reasserting that a default route exists any time a change is detected. It's only assumed that the +host is offline if a default route doesn't exist. ##### Issues diff --git a/docs/diagrams/daemon-components.png b/docs/diagrams/daemon-components.png Binary files differnew file mode 100644 index 0000000000..cfa7c9b2ba --- /dev/null +++ b/docs/diagrams/daemon-components.png diff --git a/docs/diagrams/daemon-components.puml b/docs/diagrams/daemon-components.puml new file mode 100644 index 0000000000..42eed5aa19 --- /dev/null +++ b/docs/diagrams/daemon-components.puml @@ -0,0 +1,66 @@ +@startuml +hide empty description +scale 10000 + +left to right direction +!pragma layout elk + + +component "Mullvad daemon" as mullvad_daemon { + () "Management interface" as management_interface + () "Mullvad API" as mullvad_api + + node "Daemon" as daemon { + [Settings] as settings #yellow + [Account history] as account_history #yellow + [Various state around tunnels and relays] #yellow + } + + node "Tunnel state machine" as tsm { + [Firewall] as firewall #yellow + [DNS] as dns #yellow + [Routing] as routing + [Tunnel] as tunnel + [Offline monitor] as offline_monitor + } + + [Account manager] as account_manager + [Mullvad API client] as rest_service + [Relay list updater] as relay_list_updater + [Version updater] as version_updater + [Relay selector] as relay_selector #yellow +} + +legend + Yellow components are shared synchronously (via a mutex). +end legend + + +management_interface -> daemon : Incoming RPCs + +daemon -> tsm : Sets target tunnel state +tsm -> daemon : "Notifies of changes to tunnel state, gets tunnel connection config" +tsm -> daemon : Get tunnel connection config +daemon -> relay_selector : Select a relay for a connection +daemon -> rest_service : Change API availability +daemon -> relay_list_updater : Force relay list update +daemon -> version_updater : Fetch current version +daemon -> account_manager : Fetch account data +daemon -> account_manager : Fetch device data and WG key + +relay_list_updater -> relay_selector : Update relay list +account_manager -> daemon : Account/device events + + +rest_service -> relay_selector : Select bridge relay for API connection +rest_service -> tsm : Unblock API endpoint +rest_service -> mullvad_api : Send requests to our web service + +relay_list_updater -> rest_service : Fetch relay list from API +version_updater -> rest_service : Fetch latest app versions +account_manager -> rest_service : Fetch account data +account_manager -> rest_service : Fetch device data, rotate keys + + +@enduml + diff --git a/docs/diagrams/readme.md b/docs/diagrams/readme.md new file mode 100644 index 0000000000..50441c46d0 --- /dev/null +++ b/docs/diagrams/readme.md @@ -0,0 +1,6 @@ +# Diagrams +The diagrams are written for [PlantUML](https://plantuml.com/). The SVG files can be produced via `plantuml -tsvg *.puml`. + +The more complex diagrams require a version of PlantUML that uses [eclipses diagramming library](https://plantuml.com/elk), +this is indicated by the line `!pragma layout elk`. + diff --git a/docs/diagrams/set-allow-lan.png b/docs/diagrams/set-allow-lan.png Binary files differnew file mode 100644 index 0000000000..b34fc78452 --- /dev/null +++ b/docs/diagrams/set-allow-lan.png diff --git a/docs/diagrams/set-allow-lan.puml b/docs/diagrams/set-allow-lan.puml new file mode 100644 index 0000000000..c4a6ad96bc --- /dev/null +++ b/docs/diagrams/set-allow-lan.puml @@ -0,0 +1,21 @@ +@startuml +hide empty description +scale 800 + +title Execution of ""set_allow_lan"" RPC + +participant "Management interface" as management_interface +participant "Daemon" as daemon +participant "Settings" as settings +participant "Tunnel state machine" as tsm +participant "Daemon event subscribers" as subscribers + +management_interface -> daemon : Incoming RPC +daemon -> settings : Save settings +daemon -> management_interface : Return save result to RPC +daemon -> subscribers : Publish new settings to all subscribers +daemon -> tsm : Send new __allow lan__ setting + + +@enduml + diff --git a/docs/diagrams/tsm-connecting-enter.png b/docs/diagrams/tsm-connecting-enter.png Binary files differnew file mode 100644 index 0000000000..19101cf69c --- /dev/null +++ b/docs/diagrams/tsm-connecting-enter.png diff --git a/docs/diagrams/tsm-connecting-enter.puml b/docs/diagrams/tsm-connecting-enter.puml new file mode 100644 index 0000000000..e0964229f6 --- /dev/null +++ b/docs/diagrams/tsm-connecting-enter.puml @@ -0,0 +1,26 @@ +@startuml +hide empty description +scale 800 + +state "Entering connecting state" as enter_connecting +state "Check offline state" as offline +state "Generate tunnel parameters" as ask_gen_parameters +state "Set firewall policy" as set_firewall +state "Spawn tunnel" as spawn_tunnel +state "Connecting state" as connecting_state +state "Enter error state" as enter_error + +[*] --> enter_connecting +enter_connecting --> offline : Check offline state +offline --> ask_gen_parameters : Host is not offline +offline --> enter_error : Host is offline +ask_gen_parameters --> set_firewall : Successfully generated parameters +ask_gen_parameters --> enter_error : Failed to generate tunnel parameters +set_firewall --> spawn_tunnel : Successfully set firewall policy +set_firewall --> enter_error : Failed to set firewall policy +spawn_tunnel -> connecting_state : Start a tunnel monitor and enter connecting state + + +@enduml + +state "Starting a tunnel" diff --git a/docs/diagrams/update-relay-constraints.png b/docs/diagrams/update-relay-constraints.png Binary files differnew file mode 100644 index 0000000000..25f02ca77d --- /dev/null +++ b/docs/diagrams/update-relay-constraints.png diff --git a/docs/diagrams/update-relay-constraints.puml b/docs/diagrams/update-relay-constraints.puml new file mode 100644 index 0000000000..fa4087fe41 --- /dev/null +++ b/docs/diagrams/update-relay-constraints.puml @@ -0,0 +1,28 @@ +@startuml +hide empty description +scale 800 + +title Execution of ""update_relay_settings"" RPC when relay settings have changed, but the key is invalid. + +participant "Management interface" as management_interface +participant "Daemon" as daemon +participant "Settings" as settings +participant "Relay selector" as relay_selector +participant "Tunnel state machine" as tsm +participant "Account manager" as account_manager + +management_interface -> daemon : Incoming RPC +daemon -> settings : Update relay settings +daemon -> management_interface : Return save result to RPC +daemon -> management_interface : Publish new settings to all subscribers +daemon -> relay_selector : Update relay constraints +daemon -> tsm : Reconnect tunnel +tsm -> daemon : Request new tunnel parameters +daemon -> tsm : Send tunnel parameters +tsm -> daemon : Publish connecting state +daemon -> management_interface : Publish connecting state +daemon -> account_manager : Asynchronously verify account state +account_manager -> daemon : Publish new device state +daemon -> management_interface : Publish new device state + +@enduml |
