Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exit Node Support #13

Open
ImranR98 opened this issue Dec 15, 2022 · 7 comments
Open

Exit Node Support #13

ImranR98 opened this issue Dec 15, 2022 · 7 comments

Comments

@ImranR98
Copy link

Hi, I saw your comment on tailscale/tailscale#2880. Does this script support using the node as an exit node (routing all traffic from the Tailscale network through Mullvad)?

@edgar-vincent
Copy link
Contributor

Not the developer, but don't think it does. Actually, it does the opposite. This should be possible with some routing magic, but I wouldn't know how to do that.

@ajgrf
Copy link

ajgrf commented Jan 15, 2023

I don't use this script, because I prefer configuring things with nix instead. However, I just figured out how to get exit nodes working and this seems like a good place to share. (Disclaimer: I am not very knowledgeable about networking, so figuring this out was an extremely slow process of educated guesswork and trial and error.)

If you run ip rule list on a system running both Mullvad and Tailscale, you should see something like this:

0:      from all lookup local
5208:   from all lookup main suppress_prefixlength 0
5209:   not from all fwmark 0x6d6f6c65 lookup 1836018789
5210:   from all fwmark 0x80000/0xff0000 lookup main
5230:   from all fwmark 0x80000/0xff0000 lookup default
5250:   from all fwmark 0x80000/0xff0000 unreachable
5270:   from all lookup 52
32766:  from all lookup main
32767:  from all lookup default

These are your system's routing rules. The number on the left is a priority, the text on the right is a rule telling the system when to use a particular routing table. Rules 0, 32766, and 32767 exist on basically every Linux system from what I gather. Rules 5210, 5230, 5250, and 5270 are put there by Tailscale and always have the same priority numbers. Rules 5208 and 5209 are put there by Mullvad, which tries to ensure its rules always come before everything else.

We need the rule at 5209 to come after Tailscale's rules, which we can do by running:

ip rule del not from all fwmark 0x6d6f6c65 lookup 1836018789
ip rule add pref 6000 not from all fwmark 0x6d6f6c65 lookup 1836018789

Now if you run ip rule list again, you should see:

0:      from all lookup local
5208:   from all lookup main suppress_prefixlength 0
5210:   from all fwmark 0x80000/0xff0000 lookup main
5230:   from all fwmark 0x80000/0xff0000 lookup default
5250:   from all fwmark 0x80000/0xff0000 unreachable
5270:   from all lookup 52
6000:   not from all fwmark 0x6d6f6c65 lookup 1836018789
32766:  from all lookup main
32767:  from all lookup default

Success! Well, almost. Tailscale is probably broken at this point, and you need another firewall rule to unbreak it. Modify the excludeOutgoing chain to look something like this:

          chain excludeOutgoing {
            type route hook output priority -100; policy accept;
            meta mark & 0x00ff0000 == 0x00080000 ct mark set 0x00000f41;
            ip daddr 100.64.0.0/10 ct mark set 0x00000f41 meta mark set 0x6d6f6c65;
            ip6 daddr fd7a:115c:a1e0::/48 ct mark set 0x00000f41 meta mark set 0x6d6f6c65;
          }

I lowered the priority from 0 to -100 to make sure these apply before Mullvad's firewall rules, and also added the rule meta mark & 0x00ff0000 == 0x00080000 ct mark set 0x00000f41. This allows some additional Tailscale traffic to avoid rejection by Mullvad's firewall rules.

One last hurdle is that any time you disconnect and reconnect to Mullvad, it will undo the routing table reordering that we did. I plan on writing a cron job that periodically checks for this and corrects it, but I haven't actually gotten around to that yet.

@ajgrf
Copy link

ajgrf commented Jan 15, 2023

Also, make sure you use Mullvad's privacy check page before using an exit node configured like this. On some browsers (e.g. Firefox for Android) your Tailscale address is leaked through WebRTC, creating a unique fingerprint that could be used to identify your VPN traffic.

@ImranR98
Copy link
Author

Thanks @ajgrf. When I run ip rule list, the output does match yours, and I've added the firewall rule but it seems there's still something wrong with my setup. The exit node does show up as connected on Tailscale but I can't access the internet on it. Will continue to troubleshoot and post an update if it works.

@ajgrf
Copy link

ajgrf commented Jan 15, 2023

Does your exit node function without Mullvad running? Do you have any extra firewall rules hanging around? (Check with nft list table inet mullvad-ts.)

It's also possible that the NixOS firewall is helping me out too. When I enabled tailscale, I set the NixOS option networking.firewall.checkReversePath = "loose", which runs ip46tables -t mangle -A nixos-fw-rpfilter -m rpfilter --validmark "--loose" -j RETURN at boot. I don't know what that does exactly (I just copied it from somewhere else), and the table name at the very least is NixOS-specific, but it seems like it may be important.

@ajgrf
Copy link

ajgrf commented Jan 15, 2023

I think these are the nftables rules that relate to the ip46tables command in my last post:

table ip mangle {
	chain nixos-fw-rpfilter {
		fib saddr . mark oif != 0 counter packets 6123199 bytes 8391132930 return
		udp sport 67 udp dport 68 counter packets 5 bytes 1690 return
		ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp sport 68 udp dport 67 counter packets 0 bytes 0 return
		counter packets 61 bytes 18614 drop
	}

	chain PREROUTING {
		type filter hook prerouting priority mangle; policy accept;
		counter packets 6123265 bytes 8391153234 jump nixos-fw-rpfilter
	}
}

table ip6 mangle {
	chain nixos-fw-rpfilter {
		fib saddr . mark oif != 0 counter packets 22367 bytes 2401898 return
		counter packets 52 bytes 23627 drop
	}

	chain PREROUTING {
		type filter hook prerouting priority mangle; policy accept;
		counter packets 22419 bytes 2425525 jump nixos-fw-rpfilter
	}
}

Again, I don't know how to interpret this. My notes say it's important for exit nodes and subnet routing, though. Hopefully it's helpful to you trying to get your own exit node working.

@elluisian
Copy link

elluisian commented Dec 17, 2023

Here's my two cents.
Thanks to @ajgrf's suggestion, I was able to set up exit nodes.
My setup is the following:

  • Debian testing x64 (Trixie);

  • I've set dnsmasq+openresolv in order to avoid resolv.conf overwriting.
    Moreover, I forced dnsmasq to use 100.64.0.27 (one of mullvad public dns, more can be found here), whenever I use wg-mullvad and 100.100.100.100 whenever tailscale0 is used, something like this on /etc/dnsmasq.conf:

    server=100.64.0.27@wg-mullvad
    server=<custom public dns>@wlp3s0
    server=100.100.100.100@tailscale0
    

    I instructed openresolv to use the DNS server on localhost (id est, the dnsmasq server itself).

  • Unfortunately, while good, the mnf script is not good enough for me since:

    • I prefer using the Mullvad VPN app (even though I'm also a cli guy);
    • I don't usually stop Mullvad except for specific cases, I prefer to stop tailscale instead whenever I'm finished with it;
    • I have custom iptables rules;

What I did was simply edit the file mullvad.rules this way:

    [...]
    chain excludeOutgoing {
        type route hook output priority -100; policy accept;
        meta mark & 0x00ff0000 == 0x00080000 ct mark set 0x00000f41;
        ip daddr $EXCLUDED_IPS ct mark set 0x00000f41 meta mark set 0x6d6f6c65;
        # Comment the following line if you do not want IPv6 support.
        #ip6 daddr $EXCLUDED_IPV6 ct mark set 0x00000f41 meta mark set 0x6d6f6c65;
     }
    [...]

as suggested by @ajgrf and then, made a script like the following one.
Please note that there's no error checking, it's just to show the basics of it.

#!/bin/bash


if [ "$1" = "up" ]
then
    sudo tailscale set --advertise-exit-node
    sudo tailscale up

    # These are used to adapt tailscale rules to my custom iptables rules
    sudo iptables -D INPUT -j ts-input
    sudo iptables -I INPUT 6 -j ts-input
    sudo iptables -D FORWARD -j ts-forward
    sudo iptables -I FORWARD 3 -j ts-forward
    sudo iptables -t nat -D POSTROUTING -j ts-postrouting
    sudo iptables -t nat -A POSTROUTING -j ts-postrouting

    # This is set so that exit nodes can be used, this is also thanks to ajgrf
    sudo ip rule del not from all fwmark 0x6d6f6c65 lookup 1836018789
    sudo ip rule add pref 6000 not from all fwmark 0x6d6f6c65 lookup 1836018789

    # Use the modified `mullvad.rules` provided by this project
    sudo nft -f ./mullvad.rules

elif [ "$1" = "down" ]
then
    sudo tailscale down
    sudo nft delete table inet mullvad-ts
fi

I was able to use Tailscale on Android to surf the web as if I was connected to Mullvad with this configuration.
This is great since there's no way to setup multiple VPNs on Android.

If the Mullvad check page says that you have DNS leaks, try setting, on the host used as exit node, a custom DNS address and set the one of the Mullvad DNSes (in my case, 100.64.0.27, for I set it on dnsmasq).

This works both with and without Mullvad being enabled.
Of course, if not, only Tailscale is in use, meaning that when browsing, it will use the actual public IP of the exit node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants