The Complete Computing Environment

From Wireguard to Tailscale

LifeTechEmacsTopicsArcology

I'm converting my Wobserver from using "plain old wireguard" and my laptops' Wireguard Configurations to using Tailscale for my private network. This setup is almost entirely lifted from Christine's post on setting up a Minecraft server on NixOS and Tailscale.

Why move to a proprietary VPN solution?

"Wireguard is a protocol not a product" – setting up iptables, sysctl and all this nonsense just to create a more-secure hub-and-spoke model is fine, but COVID-19 wfh has changed the nature of my network topology a fair bit, and I'll be moving more than a few milliseconds away from Wobscale soon, so having auto-discovered LAN routing will be really nice. I have tried some options which kind of work here, but each ahve a fair bit of tradeoffs and don't solve the auto-discovery.

pivpnalgoeasy-wg-quick

These are all decent enough, but each have different tradeoffs and non fit in to my systems philosophy and network topology. Tailscale solves this all.

Wireguard in a NixOS homogeneous environment could be alright because it would be managed programmatically alongside the rest of the host configuration, but dealing with generating configurations for mobile, windows, mac, etc, is a pain in the ass still. I'm not interested in building out shared-secret management, and Peering, and IP Address Management on top of a "protocol not a product", i guess.

Tailscale also gives me extensible social peering as well, as described in Christine's post, unlocking some convivial properties of the Arroyo Systems philosophy. It's not unrealistic to imagine using the social features of Tailscale to build out a private network of Arroyos, Arcologies and Wobservers.

Tailscale is built by people I generally trust and provide a really reasonable free tier, though I wouldn't mind a bit more server sharing. If they turn evil I'll have a bear of a time getting something as straightforward as MagicDNS working, but then so will a bunch of other folks and maybe we can build an open source version of this thing.

Tailscale on NixOS

This will eventually run on The Wobserver, but for now it only runs on the endpoint configuration.

Well here it is:

{ config, pkgs, lib, ... }:

{
  <<tailscale-option>>
  config = {
    <<tailscale-pkg>>
    <<tailscale-autoconnect>>
    <<tailscale-firewall>>
  };
}

Once you've deployed a configuration for the first time with Morph, check the machine list to see if the new machine is there.

Let's dig in to what's happening here.

Installing it

Tailscale is packaged in nixpkgs, and there is a NixOS module for it. Enable them both.

environment.systemPackages = [ pkgs.tailscale ];
services.tailscale.enable = true;

First-setup and Authentication

Initial setup is accomplished with an systemd one-shot service that Christine wrote. I extend it slightly by making an option which my various hosts can set the first-connect auth-key to as services.tailscale.authKey. Fetch a key from the tailscale settings panel, generate an auth key that isn't reusable, and set it in the host configuration like Virtuous Cassette.

options.services.tailscale = with lib; {
  authKey = mkOption {
    type = types.str;
    description = "Set the tailscale auth key";
  };
};

I love being able to set up systemd services with this script option like tailscale-autoconnect does. Very good stuff.

systemd.services.tailscale-autoconnect = {
  description = "Automatic connection to Tailscale";

  # make sure tailscale is running before trying to connect to tailscale
  after = [ "network-pre.target" "tailscale.service" ];
  wants = [ "network-pre.target" "tailscale.service" ];
  wantedBy = [ "multi-user.target" ];

  # set this service as a oneshot job
  serviceConfig.Type = "oneshot";

  # have the job run this shell script
  script = with pkgs; ''
    # wait for tailscaled to settle
    sleep 2

    # check if we are already authenticated to tailscale
    status="$(${tailscale}/bin/tailscale status -json | ${jq}/bin/jq -r .BackendState)"
    if [ $status = "Running" ]; then # if so, then do nothing
      exit 0
    fi

    # otherwise authenticate with tailscale
    ${tailscale}/bin/tailscale up -authkey ${config.services.tailscale.authKey}
  '';
};

Poking the Firewall

Tailscale needs a firewall port open – and it makes some amount of sense to mark the tailscale0 interface as trusted. Make sure to set ACLs in Tailscale!!

networking.firewall.allowedUDPPorts = [ config.services.tailscale.port ];
networking.firewall.trustedInterfaces = [ "tailscale0" ];
# warning: Strict reverse path filtering breaks Tailscale exit node
# use and some subnet routing setups. Consider setting
# `networking.firewall.checkReversePath` = 'loose'
networking.firewall.checkReversePath = "loose";