Rafficer / linux-cli-community

Linux command-line client for ProtonVPN. Written in Python.
https://protonvpn.com
GNU General Public License v3.0
1.3k stars 195 forks source link

[BUG] IPv6 leak protection doesn't work as intended with pre-existing iptables rules #118

Open tycho opened 4 years ago

tycho commented 4 years ago

I have a bunch of iptables rules already set up so that I can route networking for virtual machines running on my local machine. The commands that protonvpn-cli-ng uses to drop IPv6 traffic end up doing nothing, as they're appended to the end of the relevant chains, after my existing rules selectively allowing traffic.

For example, my OUTPUT chain contains something like this (simplified for readability):

-A OUTPUT -o br0 -j ACCEPT

protonvpn-cli-ng adds this rule after it, which is never hit:

-A OUTPUT -o br0 -j DROP

As a result, IPv6 traffic ends up leaking.

One option to solve this would be to disable IPv6 entirely while connected: e.g. save off the value of net.ipv6.conf.all.disable_ipv6 before connecting, set it to 1 while connected and restore it to the previous value when disconnected. This would disable IPv6 on all interfaces, which would probably be better than a DROP rule anyway because applications wouldn't even attempt to make any IPv6 connections when that's set.

tycho commented 4 years ago

One downside to the net.ipv6.conf.all.disable_ipv6 approach is that setting it back to 0 will not necessarily cause IPv6 address assignments to come back in a timely fashion.

SLAAC addresses obtained via router advertisements would come back quickly, depending on the router advertisement interval (typically only a few seconds in most configurations).

DHCPv6 addresses are another story, because whatever is managing the interface (NetworkManager or systemd-networkd, for example) would have to explicitly request a new address assignment. This will eventually happen automatically, but it does take longer (in my local testing it took about 2-3 minutes before it noticed the missing DHCPv6 lease and refreshed it).

That said, I still think the net.ipv6.conf.all.disable_ipv6 approach is probably the better one.

Rafficer commented 4 years ago

It was like that in the past actually, but the problem was that it didn't do anything after a while, as you've mentioned. So it's a very problematic approach.

tycho commented 4 years ago

Well, even if the iptables rules were working as intended, the input/output drop rules would eventually cause the IPv6 leases to expire anyway, because the system would be prohibited from communicating to maintain the leases.

Rafficer commented 4 years ago

Yeah, but they won't come back this way. I've already thought it's not working as intended and think about making the ipv6 protection just like the kill switch... Blocking everything.

tycho commented 4 years ago

They do come back. Once disable_ipv6 is restored to 0, SLAAC addresses come back almost instantly, and DHCPv6 addresses come back after a few minutes. Using the sysctl also has the advantage of clearly communicating to other things that the interfaces have had a state change. Silently dropping IPv6 via iptables until the leases die off just makes things think the system has poor/broken IPv6 connectivity instead of no IPv6 connectivity.

Rafficer commented 4 years ago

I don't get your point. If connectivity comes back at some point but the users expects it not to come back, it's really bad and shouldn't be done this way. So sysctl is out.

tycho commented 4 years ago

We must be talking past each other. I'm saying it comes back once the VPN is disconnected. It should not come back on its own while disable_ipv6=1.

Rafficer commented 4 years ago

Oh, yeah, I meant it comes back while the VPN is on, making it possible to connect via IPv6 to services and not go over the VPN.

tycho commented 4 years ago

Okay. Under what conditions has it been observed to come back while the VPN is on and disable_ipv6=1? The only case I can think of is that something else independently explicitly enables IPv6 again.

I mean, even if the iptables approach was working (e.g. if it was using -I instead of -A), there's nothing to prevent the table from being automatically stomped on by whatever's managing the rules there and negating the IPv6 leak prevention there, either.

Rafficer commented 4 years ago

Check out #59 and also this reddit thread.

I'm all for going back and not playing with the IPv6 firewall rules, but it leaked...

tycho commented 4 years ago

Could do both. Insert (instead of append) the rules first, and then do the disable_ipv6=1 sysctl. So even if the sysctl gets reverted somehow, the firewall rules would be in place. It's still not perfect, but less likely to result in leakage.

Rafficer commented 4 years ago

I was more thinking about doing

ip6tables -F
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -P FORWARD DROP

And upon disconnecting restoring again. Basically how the Kill Switch works.

tycho commented 4 years ago

That should work. It's a bit heavy-handed, but I think it'd functionally do the right thing. I think the sysctl is still necessary on top of it, though (i.e. for link state change notifications via netlink).