celzero / rethink-app

DNS over HTTPS / DNS over Tor / DNSCrypt client, WireGuard proxifier, firewall, and connection tracker for Android.
https://rethinkfirewall.com/
Apache License 2.0
2.87k stars 146 forks source link

Complex use case discussion #1698

Closed calebj closed 1 week ago

calebj commented 2 weeks ago

I am trying to move away from privacy measures that require root, and this app is very close to meeting my needs. Here is my own use case; it's similar to Dyras's here. I'm just looking for dialogue and suggestions at this point.

My network at home has routes to other sites and several internal subnets, along with DNS and IP based privacy filters. When away, I use WG tunnels on my laptop and phone for all traffic so it behaves just as if I was still home. My phone only has exceptions for LAN scanning and speed test apps, and the wifi calling service. When it's connected to my home WLAN, Tasker deactivates the WG tunnel.

I also use two root apps on the phone for network privacy: AFWall+ to limit which apps can reach the internet and/or the WLAN, and AdAway in hosts file mode for DNS blocking.

There are also some apps which need to be proxied over a different WG tunnel. At home it works fine, but I have to deactivate the home tunnel to use the other one while away.

A root-free VPN app that can do all of this in always-on+killswitch mode would be phenomenal. I know Rethink can do most of the things I mentioned already (and do them better), and I am hoping the rest is coming soon.

Here are my findings; let me know if I missed anything:


Here is my troubleshooting for KDE Connect, er, connectivity:

Like in #1016, #1507 and #1618, I couldn't get KDE Connect to reach LAN IPs except when "do not route private IPs" was enabled, or the local IP range was added as a bypass universal firewall rule. In the logs, the outgoing identity packet (UDP) shows as approved, but from an unknown app. It works via WG Tunnel in lockdown mode, so it is definitely possible using a tunnel.

As it turns out, the identity packet is sent by the phone and received by the laptop fine. The code responsible for this is here, by the way. The problem occurs when the laptop tries to connect back via TCP. When the phone is using a WG proxy, the connection is rejected outright (RST reply), and with no proxy, the laptop sends SYN, hears no reply, and sends retransmissions until it gives up. Making incoming connections work should clear up the issues with KDE Connect, I think. It should help Syncthing as well.

For what it's worth, I've confirmed that KDE Connect works on two different Wireguard apps on two different phones, all of them in always-on+lockdown mode. The handshake proceeds normally each time: the phone sends a UDP identity packet to the laptop, and the laptop connects to the advertised TCP port.


Finally, I have two QoL suggestions that would streamline my and many others' use cases if and when the missing features are implemented:

First, having a place to define IP aliases/lists and allow/deny those in firewall rules would make managing them much easier than copying the addresses into each app's rules list. As an example, the WG app's option of "exclude private IPs" creates a list of 30 allowed IP ranges to carve out the IANA special use blocks. I have different subnets and remote sites that only some apps need access to. I can manually fill in the rules, but IP/subnet lists would be much easier to work with.

Secondly, if the app itself is going to check the SSID, a user-friendly "allow [app] to access local network?" prompt similar to the new app block prompt would be very nice. This would essentially be a bypass rule, but per-app and/or SSID(s), and with the option for a special alias that always refers to the currently connected LAN's subnet. Most apps don't need LAN access and the ones which do are fairly obvious, so it makes sense to alert on any unexpected activity. And as long as the rules could be edited (as they are now), routed subnets like I and other power users have can easily be added.

ignoramous commented 2 weeks ago

Thanks.

LAN-only: already there (isolate rule)

I think you misunderstand what Isolate does? It basically block ALL connections except to domains/IPs explicitly trusted (allowed) for the Isolated app.

Tasker: Seeing that folks who use Tasker are usually programmers, we are waiting for one among them to send a PR. We may yet implement this ourselves, but it isn't priority given other 300 other open issues & feature requests.

SSID: Easier but requires location permission, which we are reluctant to add.

Per-app block backgrounded apps: #720

KDE Connect: We don't use it, so it is unlikely we ever fix it ourselves. From what you describe (port knocking with UDP, connect with TCP), this is not likely a straight-forward fix given the way we setup WireGuard.

Per-app LAN routes: These can't be excluded per-app (only per-Tunnel). This is an Android limitation.

calebj commented 1 week ago

I think you misunderstand what Isolate does? It basically block ALL connections except to domains/IPs explicitly _trust_ed (allowed) for the _Isolate_d app.

That's what I want it to do. Trusting the standard local network blocks (10../8, 192.168../16, 172.16../12) acts as a general LAN only filter, but I can also restrict it further. For example, I can make it so IOT apps may only reach the IOT subnet, or the address of their specific device.

Tasker: Seeing that folks who use Tasker are usually programmers, we are waiting for one among them to send a PR. We may yet implement this ourselves, but it isn't priority given other 300 other open issues & feature requests. SSID: Easier but requires location permission, which we are reluctant to add.

I understand. Adding support for profiles to be activated by name like the official Wireguard app might be simpler than writing a runner+helper+UI. I don't know that for sure, since I haven't looked much into writing Tasker plugins.

Per-app block backgrounded apps: #720

Subscribed, thanks.

KDE Connect: We don't use it, so it is unlikely we ever fix it ourselves. From what you describe (port knocking with UDP, connect with TCP), this is not likely a straight-forward fix given the way we setup WireGuard.

I think the hurdle isn't port knocking or any specific app, but handling incoming packets. The other tunnel apps don't have special handling, they Just Work™ because packets addressed to their tunnel IP show up on the tun interface.

Per-app LAN routes: These can't be excluded per-app (only per-Tunnel). This is an Android limitation.

If you mean the LAN access blocking, this would just be a per-app block rule. But I'm not sure which part of my post you're referring to with "LAN routes".

ignoramous commented 1 week ago

Thanks.

I think the hurdle isn't port knocking or any specific app, but handling incoming packets. The other tunnel apps don't have special handling, they Just Work™ because packets addressed to their tunnel IP show up on the tun interface.

Rethink doesn't run a "packet filter" (we should), but in fact runs a "stateful" firewall. Rethink also doesn't give active WireGuard configs access to the TUN device but rather "emulates" it (fakes a TUN device). This is how Rethink can run multiple WireGuards at once on Android which does not let VPN apps create multiple TUN devices (just one).

In v055o, we've recently implemented BEHAVE-UDP / rfc4787 (https://github.com/celzero/firestack/issues/77 / https://github.com/celzero/rethink-app/issues/1633) which may help with P2P apps, but I am not sure (as BEHAVE-TCP / rfc5382 isn't implemented, nor do I know how to implement it without adding a new "packet filter" mode for the firewall, which in itself is a multi-month task).

If you mean the LAN access blocking...

I meant, "excluding" LAN routes from Rethink's tunnel (which causes it to bypass Rethink and either end up in Android's blackhole when "VPN Lockdown" aka Block connections without VPN is turned ON or end up traversing the LAN just fine when "VPN Lockdown" is turned OFF). This "exclusion" (to allow / block depending on "VPN Lockdown" setting) cannot be done per-app given the way Android's VPN APIs are structured (can only be per-Tunnel).

Adding support for profiles to be activated by name like the official Wireguard app might be simpler than writing a runner+helper+UI

Unsure what the official WireGuard app for Android does, as I haven't used it. We could implement what they do, though; especially if it avoids us having to add the "Location" permission just for per-SSID WireGuards (if you know, please consider leaving a comment at #1294).

(I am closing this issue as it mostly a discussion and I don't see any new requests here. In case I am mistaken please feel free to re-open / reply / create a new issue. Appreciate your thorough feedback. Thanks once again).

calebj commented 1 week ago

Actually, could you convert the issue to a discussion? The new request would be IP address lists/aliases, but that probably belongs in a discussion as well.

I get that the TUN device is emulated so all packet handling can happen in userspace. That's a great way to make it do more, but not the point. What I meant by that was that as far as other applications are concerned, bind() on a UDP socket is agnostic to the type of device, as long as the packets show up. So from Rethink's perspective, it will need to handle incoming packets from the WG client. For incoming connections from the underlying network, I would have to experiment but if protect() works on listening sockets, then it could work.

I get the route exclusion part now. You're right, that can't be moved to a per-app setting, so the closest thing is to use the block feature on local address ranges for apps that shouldn't be allowed. Doing so is better since you can explicitly allow only specific networks.

The official Wireguard app uses these intents which creates a custom permission. Then in this file, the intents are handled. Where needed, the tunnel name is specified as a parameter. I couldn't find anywhere that allows Tasker to detect the function named WireGuardSetTunnel that it uses to control WG, but as far as I can tell, that function is simply a wrapper for an intent broadcast. Tasker can directly perform such broadcasts as well.

ignoramous commented 1 week ago

The official WireGuard app uses these intents ...

Thanks. Noted: https://github.com/celzero/rethink-app/issues/1294#issuecomment-2359468242

(as before, Tasker integration will have to come from whoever uses it and can test it; we personally don't)

So from Rethink's perspective, it will need to handle incoming packets from the WG client

In v055o Rethink will try to handle incoming packets from active WireGuard tunnels, but whether it will work as we expect it to in all scenarios... I'm not so sure. In fact, I doubt if it ever will (since the limitation is inherent to the way Rethink's network engine is setup).

For incoming connections from the underlying network...

Android blocks all ingress by default, unless there's an equivalent egress. That is, Rethink will need to support port forwarding, and expect users to know when to enable it and how to configure to use it.

The new request would be IP address lists/aliases...

Sorry, I don't get? You want a feature to import IP blocklists? If so, see: #237 This is something we want to ship soon.

calebj commented 1 week ago

Android blocks all ingress by default, unless there's an equivalent egress.

When VPN is on, or only in lockdown? I use several apps that listen for connections, so it can't be true all the time. But you are right, user control over which ingress ports are allowed would be best since allowing everything would be insecure.

You want a feature to import IP blocklists?

If it's just importing, no. I want to be able to create (or import) a list, and then refer to it by name in firewall rules.

ignoramous commented 1 week ago

When VPN is on, or only in lockdown?

Regardless of whether a VPN is running. Though, in VPN Lockdown mode, ingress does behave funny.

use several apps that listen for connections

listening sockets are opening up those ports for ingress. On Android, the VPN app isn't notified of these sockets and so, users will have to manually setup port forwarding (which isn't yet supported by Rethink).

and then refer to it by name in firewall rules

Hopefully, we ship this by end of this year.

calebj commented 1 week ago

Interesting... for port forwarding, the only problem I see is having to pick unique external port numbers since linux won't allow them to be reused. Apps that use discovery protocols or assume fixed port numbers won't work when forwarded if they rely on incoming connections.

KDE Connect selects its TCP port automatically, and announces it over its own discovery broadcasts as well as on mdns. Syncthing's docs state, "external forwarded ports and the internal destination ports have to be the same". Both apps can make outgoing connections as normal. If they can still receive broadcast UDP, their discovery protocols might still work. As a fallback, KDE Connect also uses mdns, and Syncthing has global discovery servers which allow nodes to know each others' IP addresses.

Also, maybe firestack can automatically rewrite the registered port in mdns announcements and query responses?

ignoramous commented 1 week ago

Apps that use discovery protocols or assume fixed port numbers won't work when forwarded if they rely on incoming connections.

True, but expect apps to bind to VPN's interface (say, 10.111.222.1:port) instead of the wildcard (say, 0.0.0.0:port), which leaves the VPN (in this case Rethink) free to bind to the same :port but on the actual underlying interfaces (wifi / mobile).

Also, maybe firestack can automatically rewrite the registered port in mdns announcements and query responses?

Unsure if these announcements can be trapped and handled at all (by gvisor/netstack).

If they can still receive broadcast UDP, their discovery protocols might still work.

Unsure if broadcast UDP works at all with our current impl (which uses gvisor/netstack).

May be @Lanius-collaris knows if we can make gvisor/netstack behave wrt multicast & broadcast.


Similar:

Lanius-collaris commented 1 week ago

Unsure if broadcast UDP works at all with our current impl (which uses gvisor/netstack).

May be @Lanius-collaris knows if we can make gvisor/netstack behave wrt multicast & broadcast.

Both broadcast and multicast (may require specifying interface in ip_mreq , ip_mreqn or ipv6_mreq , and setting the IP_MULTICAST_IF option or IPV6_MULTICAST_IF option) can bypass VPN on Android, so there is nothing to do with gvisor.

Lanius-collaris commented 5 days ago

Both broadcast and multicast (may require specifying interface in ip_mreq , ip_mreqn or ipv6_mreq , and setting the IP_MULTICAST_IF option or IPV6_MULTICAST_IF option) can bypass VPN on Android, so there is nothing to do with gvisor.

I mean these egress packets don't pass through the TUN device.