Run Headscale With DNS Challenge [network/tailscale-dns-challenge]

Sometimes you don’t have access to ports :80 and :443 for certain reasons. I won’t ask why, but here’s a possible solution:

Configure both Headscale and its DERP server to run on some ports other than :80 and :443, and use Let’s Encrypt DNS challenge to acquire the SSL certificate:

security.acme.acceptTerms = true;
security.acme.certs."<your-domain>" = {
  email = "<your-email>";
  dnsProvider = "<dns-provider>";
  # agenix path to the systemd environmentFile in my case,
  # free to use all other means to provide access keys
  environmentFile = config.age.secrets.acme-env.path;
};

services.headscale = {
  # choose a atypical port
  port = 11366;
  # other options stays the same, omitted
  settings = {
    server_url = "https://<your-domain>:11366";
    derp = {
      server = {
        # anything different from the headscale server port
        stun_listen_addr = "0.0.0.0:11367";
        # ...
      };
    # ...
  };
};

# Also open the corresponding ports in firewall!

and on the client, login with tailscale login --login-server https://<your-domain>:11366.

The Nix security.acme option uses Lego for ACME challenges, which integrates with a great many domainproviders. Check it out on how to configure yours!

Note that you still need a domain name for this, which is not free. It may no longer be true with Let’s Encrypt IP certificates in the future, but with those limitations posed I’m not sure it will work.

By the way, if you don’t have a domain but your server is able to do a HTTP-01 challenge, you can consider using a IP-based domain service like sslip.io.