mullvadvpn-app icon indicating copy to clipboard operation
mullvadvpn-app copied to clipboard

Update Android incoming traffic behavior info

Open t-m-w opened this issue 1 year ago • 6 comments

It is true that, currently, a VPN app must be fully-routed (include routes 0.0.0.0/0 and ::/0, as described) to block incoming traffic when a VPN is connected. However, Android's option to "Block connections without VPN" will also block inbound traffic when a VPN's connection is not established, provided the device is running Android 13 or later. See Block incoming packets in VPN Lockdown mode. - Commit 966ff7f.

A sentence fragment was also fixed.


This change is Reviewable

t-m-w avatar Oct 30 '22 00:10 t-m-w

Thanks for the PR! Did you try it out on a physical device?

albin-mullvad avatar Oct 31 '22 15:10 albin-mullvad

Yes, this accurately describes the behavior on a physical device running Android 13, and I have tested it. I have not confirmed Android 12's behavior, but I assume it suffers the shortcomings that Google describes fixing in their patch for 13.

t-m-w avatar Oct 31 '22 16:10 t-m-w

Alright, thanks! Will also try this out and go through the documentation.

albin-mullvad avatar Nov 01 '22 09:11 albin-mullvad

Could you please elaborate on your testing procedure? Running netcat in Nix on Droid (or Termux), I can listen on a port and receive traffic on it both from LAN and over the internet, regardless of any combination of settings that I have enabled or disabled - Allow LAN in the app (just toggles what is routed into the tunnel) , Block connections without VPN in Android settings.

This was tested on a Pixel 5 running Graphene TP1A.221005.002.2022102800 and 2022.2-beta1.

pinkisemils avatar Nov 01 '22 09:11 pinkisemils

Sure thing... Here's what I know and how to test.

The behavior I've described is for when a VPN connection is not established, in which case, for Android 13, all non-loopback ingress traffic will be dropped for VPN-covered applications. There is still unexpected behavior for established VPNs, which I'll describe later; perhaps that's the source of confusion.

In any case, here are some steps to test the behavior for a non-established VPN:

  1. Ensure the VPN app is listed in Settings > Network & internet > VPN.
  2. Tap the settings gear icon for the VPN.
  3. Ensure the toggles for "Always-on VPN" and "Block connections without VPN" are on. This is essential.
  4. Open the VPN app and disconnect it. This should cause Settings > Network & internet > VPN to show the text "Always on" underneath the VPN's entry, but it will not show the word "Connected".
  5. Configure and run primitive ftpd, netcat in Termux, or any other app capable of listening on a port.
  6. Attempt to connect to it from the internet or another device on the network. This will fail.
  7. Additionally, adb shell dumpsys connectivity trafficcontroller shows output for mUidOwnerMap. When a VPN is configured in lockdown, and a connection is not established, all of the uids should show LOCKDOWN_VPN_MATCH with IIF_MATCH missing.
  8. Removing "Block connections without VPN" for the VPN app will allow future inbound connections to succeed again, provided the VPN remains disconnected.

For established VPNs, the behavior is not at all what you might expect. Android does not even consider whether lockdown mode is enabled for established VPNs, when determining the ingress behavior. Instead, it checks several properties of the VPN, including whether or not it is fully-routed, to decide whether inbound traffic should be allowed from interfaces other than the VPN interface (or loopback); if not, the VPN is deemed to be "isolated." Depending on its decision, ingress either will or will not be allowed from outside of the VPN interface (or loopback). Again, unfortunately, lockdown mode has no bearing on this.

You can verify if your VPN app builds its configuration such that Android considers it to be "isolated" by running adb shell dumpsys connectivity trafficcontroller and looking for lines in the mUidOwnerMap section that include IIF_MATCH and end with a tunnel interface, such as tun0 or another tunnel interface as opposed to just 0. IIF_MATCH uids with an interface of just 0 are allowed to receive packets from any interface, as a result of the determination made here and described previously.

(Note that the output of adb shell dumpsys connectivity trafficcontroller will include uids from all users/profiles on the device. The main user (owner)'s uids are only 5 digits long; work profiles or other users' uids are 7 digits.)

There is a Connectivity module patch in the works on AOSP's Gerrit to Always drop non-VPN ingress in lockdown mode regardless of whether the VPN is otherwise considered "isolated." Hopefully it will reach a point of being acceptable to merge at some point soon, if Google chooses to do so; otherwise, ideally they will implement a similar fix themselves. :-)

t-m-w avatar Nov 01 '22 18:11 t-m-w

Hiya, I apologize for the weeks of silence, I hope you can understand that we've been busy. I can confirm, that when disconnected and Block connections without VPN is enabled, incoming traffic will be dropped by Android. However, when connected, our app still allows incoming traffic. Since in this case, we'd very much like to drop incomming traffic, I'll look into why this isn't happening - all traffic should be routed into the tunnel. We'll look into fixing that and then we can update the security docs.

pinkisemils avatar Nov 16 '22 11:11 pinkisemils

Hello @t-m-w,

I stumbled upon this PR as I was working on a related issue. I've gone through your test cases and they are what I can see mostly correct. Indeed when the VPN is not established and using "Block connections without VPN" and all inbound are dropped. I tried reaching the fully-routed state by routing (0/0 and ::0/0), however I still could receive inbound traffic. This could be because I'm using Android 14 and the behavior changed again.

I'm thinking about closing this PR, since we can't really guarantee this behavior is true for current versions, and future version of android, and the current documentation is a bit more lenient so users' won't make false assumptions. I would like to clarify this in the future though and I've created a internal ticket to create a chart describing the expected behavior per version of Android so we can be more explicit to users on what they can expect. Does sound okay for you?

Also thanks for all the detailed work 🙏 , this helped me a lot when debugging.

Rawa avatar Mar 01 '24 14:03 Rawa

I'm going to close this PR, I'm open to opening it up again if needed.

As mentioned previously we've added an internal task to clarify the android behavior in a more detailed fashion. Thanks @t-m-w for all the work you put in and the detailed information provided, if you have any update or more information to share feel free to answer on this PR and we'll take it into account.

Rawa avatar Mar 04 '24 09:03 Rawa

This all sounds fine to me! I haven't looked into it in a while. AOSP hasn't even weighed in on whether the behavior is intentional or not, and they haven't appeared interested. Erring on the side of caution and potential for sudden change (or no change at all) is a good idea.

t-m-w avatar Mar 04 '24 14:03 t-m-w