My self-hosting journey started with Nextcloud back in 2017 in a bid to move away from Google's services i.e. gmail, photos, et al. Since then the hardware I have used has evolved and so has the number of services. What hasn't changed though is the fact that I had to open ports 80 and 443 to allow traffic to pass through DD-WRT running router to the server sitting in my closet (yep, in all these years couldn't figure out a better place for it). Call it my naivety but having these ports wide open to the rest of the internet didn't really affect me. I did use Cloudflare, as a shield, for my IP address to sit behind it and enjoy its security offerings, however, there wasn't anything within my network protecting either the server or hosted services.

Here's how it looked,

Before_Tailscale_Mesh_VPN

It wasn't until late 2021 when I saw malicious IP addresses hitting my IP address directly. This was causing my router to remain at 100% cpu utilization rate pretty much all the time during the duration of attack (~4 days) and ended up affecting its own Wi-Fi capabilities. Fortunately, after a couple of iptable rules blocking handful of IP addresses, attack subsided. However, it was then I realized the need for a robust solution, something that can better handle these situations, a solution that doesn't involve me exposing any ports, in other words I didn't want any of my self-hosted web property to be pointing to home IP address directly.

While this was happening, I was already toying around with Wireguard (installed on a Raspberry Pi 3) for getting on my home network to leverage the wonderful capabilities of Pi-hole and it was working out quite well. Needless to say, that turned out to be my first choice. In order to close all ports, I wanted a way to have the home server be reachable by a server hosted in cloud (Oracle Cloud in my case) that'll be running an instance of Traefik which will then forward all requests to home server. In Cloudflare, I'll simply update DNS records to point to Oracle Cloud instead.

Here's what I was roughly aiming for,

Proposed_Solution

However, there was one issue, I was using PiVPN to create/manage clients. While it worked just fine, I couldn't really find a nice, clean & maintained UI for managing the clients and since I was planning to use this setup for creating a sort of a self-hosted, self-managed mesh vpn, I wasn't really happy with the solutions that were available. Digging around for such a solution, I came across Tailscale which albeit a 3rd party offering just happened to be the perfect solution I was looking for! It offered a clean UI, had option to set custom DNS (in my case, I'll be using Pi-hole), custom ACL's (for when the need arises) and best of all creates point to point mesh network. The last point being the best, all nodes in mesh can communicate with each other directly when possible and through relay node otherwise.

Here's a representation of mesh network directly pulled from an introductory blog post about Tailscale on their blog,

Tailscale_Mesh_Network

After playing around with the service in couple of throw away servers, I set it up on the following 3 servers,

1. Raspberry Pi 3 (running Pi-hole in home network)

Removed PiVPN and replaced with Tailscale to use this as DNS server when using Tailscale client on my android phone and my wife's iPhone. To achieve that all I had to do was instruct Tailscale to not use dns server that's set using their admin panel,

sudo tailscale up --accept-dns=false

2. Home Server (running self-hosted services in home network)

Same setup here, since it is already using Pi-hole as DNS server, I didn't want Tailscale client to interfere with that. And so, the command was the same,

sudo tailscale up --accept-dns=false

3. Cloud Server

Created an instance in Oracle Cloud (you can use whatever service provider you want) and performed following additional steps,

  1. Installed Tailscale and once again disabled it's incoming DNS capability (used --accept-dns=false).
  2. Installed Traefik and copied over the configs (of Traefik instance running on home server). All that was needed to be done was change IP addresses to those of Tailscale.
  3. Updated DNS entries in Cloudflare to point to the newly setup cloud server.
  4. Ensure everything works as expected and turn off Traefik instance on home server.
  5. Remove port forwards from your router's admin panel.

Things To Note

  1. It is safe to use Tailscale's IP addresses in your Traefik config since once issues, the IP addresses do not change (unless you remove/re-add device)
  2. I realized that changing flags passed cannot be updated after it is initially activate. To do so you need to use --reset. This is true at-least for version 1.28.0. Eg,
    sudo tailscale up --accept-dns=false --reset
  3. Disable key expiry for the cloud and home server to avoid the need of re-authenticating (and possible down time). This can be done through Tailscale's admin panel,

Tailscale_Disable_Key_Expiry

While this does add extra cost to your setup and some dependence on a cloud provider but for the benefits that it offers, in my humble opinion it is worth it. Worst case scenario, if for whatsoever reason,

  • cloud server goes down, you can rely on Tailscale to access self-hosted services directly. There's also MagicDNS to ease the pain of remembering IP addresses and instead use machine names.
  • both cloud server and Tailscale are down, as a fallback, you can keep PiVPN installed. It will still require you to open a port but it'll be less susceptible compared to ports 80/443.