Mesh VPNs, and self-hosting

So I recently came to know about mesh-networking, and was very intrigued by this radical new tech. Basically, mesh-networking allows any two devices to connect directly with each other, without the VPN server acting as the middleman. There are many implementation and use-cases for mesh-networking, from mesh-Wifis to blockchains, IoT networks, emergency systems.

One of these implementations is mesh-VPNs, which allows devices in a private network to be directly accessible by any other device within the same network. There are many products available for setting up a mesh VPN. It ranges from free-as-in-free-beer solutions like ZeroTier, to free-as-in-freedom solutions like Tailscale, Nebula, Freelan etc.

Prologue

Before I go ahead and explain how I set up mesh VPN for my system, let me tell you why I did it in the first place. Currently I host a WireGuard instance that I use to access and remotely manage my home network. I also use it to stream my desktop while I’m on the move.

Streaming my PC’s screen at a respectable FPS (frames per sec) means the local network card gets unnecessarily loaded by the same traffic twice – once, when the frame buffer leaves the desktop computer with the server as the destination, and then again when the same data is routed from the WireGuard server to my phone/laptop. This shenanigan means my server adds an extra bit of latency to the frames, and also adds potential frame skips when the server is under heavy load (I’m running this and many more things on an RPi4).

Wouldn’t it be awesome if my PC could just stream directly to my laptop? That’s where mesh VPNs step in. Now, after doing a quick round up of Reddit posts at r/self-hosted and checking up on the consensus in general, I went for Tailscale.

Personal Notes

Instead of boring you with the sales pitch, let me tell you the features I found interesting and what do they mean for a self-hosting user.

  1. Deploying the docker image won’t just magically set up a VPN – you will need to perform specific commands to configure your network as per your needs. There’s next to no configurability by just using some environment variables.
  2. Every device gets a 100.0.0.x IPv4 address assigned, once they join the Tailscale mesh VPN, or “tailnet”.
  3. If you don’t like IP addresses, you can use the hostnames of each device. These are MagicDNS names within Tailscale, not mDNS or zeroconf, which require multicast, and therefore do not traverse a layer 3 network like Tailscale. source. You can rename your devices whenever you’d like to.
  4. You’ll need to expose your local network if you want it to be accessible through your tailnet. That way you can even access devices that do not have tailscale installed on them. Think devices like a stock Wifi router.
  5. You’ll need to “advertise a node” as an exit node if you want to route traffic of a device through one of the tailnet devices. This is how you can use your tailnet as a point to point VPN to access internet from another tailnet node. Think a WireGuard connection between a server and a client. Then, any other node in the tailnet can choose one of the advertisers as an exit node.
  6. You set up your own DNS for the tailnet so you can incorporate your current Adguard/Pi-Hole instance with Tailscale.

Points 4 and 5 require approvals from the coordination server. Yes, there is a bit of centralization here. To understand the purpose of a coordination server, we need to go back to the basic concepts of mesh-VPNs.

Remember how awesome mesh-VPNs are, for seamlessly connecting any two nodes within a tailnet? Yeah well, someone needs to execute that “seamlessly connecting” bit. The coordination server, the primary point-of-contact for each and every device within the tailnet, acts as a liaison between any two devices that wish to communicate with each other. It talks to each of the two devices, negotiates a random port number in the very high (40k+) port range per-connection, and sends the port number of the other device to each device. At this point, the Tailscale application at their device has set up a VPN on the device to intercept outgoing/ incoming traffic, and can reliably route traffic directly to and from the other device directly, reliably circumvent mortal matters like NATs. This is also called NAT hole-punching.

Tailscale

Deploying Tailscale is surprisingly simple, there are apps for every major platform. If I had to nitpick, the Linux side of things are a bit lacking in terms of a good GUI, but who uses GUI when you know bash-foo? There’s a docker image available to conveniently deploy it on servers too. The quirk with Tailscale, however, is that the iOS application, MacOS application, and the Windows application are all closed-source. What makes it even worse, is that the coordination server for Tailscale is also closed source! So at this point, the only bits available as free-as-in-freedom are the Tailscale’s node logic, Android app, a few packages (Synology, QNAP, Chocolatey) and that’s it. That means the node logic with CLI support is open source, along with the Android app. However, the good news is that the coordination server URL is configurable in all of the applications, be it the open source CLI and Android applications or the closed source ones – Windows, iOS, maybe MacOS too.

But Piyush, what about the closed-source coordination server? I don’t want any company to be able to access my computer and LAN like a candy store!

a concerned citizen

This is where the main star of the show steps in.

Headscale

Headscale an open source, self-hosted implementation of the Tailscale control server. It still carries the same amount of features as a “base” tailscale control server, with all configuration through a YAML file and quite a powerful CLI for easy observability of any configured networks. That being said, since Tailscale (and Headscale, consequently) are open source and powered by WireGuard internally, there’s no possibility of snooping or any leak of private information on the tailnet.

Conclusion

Hosting Headscale and Tailscale is simple, convenient, and easily one of my favorite additions to my private server. Not only is it “a shiny new technology”, but it has also optimized my networking and allowed me to access even more of my local network remotely with no worries that come with exposing such services directly. Everything stays securely in your tailnet and is exposed only to you on-demand and encrypted with best-in-class encryption standards.

And with this new addition, I can close down my Wireguard and Sunshine public-facing ports. If you too have a few services like file sharing within your LAN and you’d like to access them remotely, do give mesh VPNs a try.

If you made it this far, I hope you try Tailscale for yourself, and even go beyond by deploying your own Headscale instance. Until next time!