From e4b335be234b0a65fac673af8692b34cb26ed467 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Sat, 25 Jul 2026 14:53:28 +1000 Subject: [PATCH] refactor(tailscale): rename exit-node to subnet-router; drop --advertise-exit-node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host was always intended as a LAN subnet router (--advertise-routes), not a full exit node (--advertise-exit-node). Rename every trace of "exit-node" to "subnet-router" and remove the --advertise-exit-node flag from extraSetFlags; the operator supplies --advertise-routes at first tailscale up and Tailscale persists it in state across reboots. Routing sysctls (useRoutingFeatures = "server"), openFirewall, and trustedInterfaces = ["tailscale0"] are still required for subnet routing to work, so the module is kept — just correctly named. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01TF2dsuKZAiyZWQ1D7CuHJm --- flake.nix | 6 ++--- hosts/tailscale-exit-node/host.nix | 12 --------- hosts/tailscale-subnet-router/host.nix | 10 +++++++ modules/build-types/tailscale-exit-node.nix | 21 --------------- .../build-types/tailscale-subnet-router.nix | 19 +++++++++++++ modules/tailscale/exit-node.nix | 27 ------------------- modules/tailscale/subnet-router.nix | 15 +++++++++++ 7 files changed, 47 insertions(+), 63 deletions(-) delete mode 100644 hosts/tailscale-exit-node/host.nix create mode 100644 hosts/tailscale-subnet-router/host.nix delete mode 100644 modules/build-types/tailscale-exit-node.nix create mode 100644 modules/build-types/tailscale-subnet-router.nix delete mode 100644 modules/tailscale/exit-node.nix create mode 100644 modules/tailscale/subnet-router.nix diff --git a/flake.nix b/flake.nix index 0e13b20..ab25099 100644 --- a/flake.nix +++ b/flake.nix @@ -96,9 +96,9 @@ proxmox-pxe-boot = mkTarget { platform = "proxmox"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; }; lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; }; - linode-tailscale-exit-node = mkTarget { platform = "linode"; buildType = "tailscale-exit-node"; hostPath = ./hosts/tailscale-exit-node/host.nix; }; - proxmox-tailscale-exit-node = mkTarget { platform = "proxmox"; buildType = "tailscale-exit-node"; hostPath = ./hosts/tailscale-exit-node/host.nix; }; - lxc-tailscale-exit-node = mkTarget { platform = "lxc"; buildType = "tailscale-exit-node"; hostPath = ./hosts/tailscale-exit-node/host.nix; }; + linode-tailscale-subnet-router = mkTarget { platform = "linode"; buildType = "tailscale-subnet-router"; hostPath = ./hosts/tailscale-subnet-router/host.nix; }; + proxmox-tailscale-subnet-router = mkTarget { platform = "proxmox"; buildType = "tailscale-subnet-router"; hostPath = ./hosts/tailscale-subnet-router/host.nix; }; + lxc-tailscale-subnet-router = mkTarget { platform = "lxc"; buildType = "tailscale-subnet-router"; hostPath = ./hosts/tailscale-subnet-router/host.nix; }; lxc-tor-relay = mkTarget { platform = "lxc"; buildType = "tor-relay"; hostPath = ./hosts/tor-relay/host.nix; }; }; diff --git a/hosts/tailscale-exit-node/host.nix b/hosts/tailscale-exit-node/host.nix deleted file mode 100644 index 26deb46..0000000 --- a/hosts/tailscale-exit-node/host.nix +++ /dev/null @@ -1,12 +0,0 @@ -_: - -{ - networking.hostName = "exit-node"; - - # No networking.hostId: only ZFS-touching hosts (server, docker) need one - # for pool-import safety, and this host does neither. - - # A genuinely new host (not a pre-refactor carry-over), so it tracks the - # flake's current nixpkgs release rather than being pinned to an older one. - system.stateVersion = "26.05"; -} diff --git a/hosts/tailscale-subnet-router/host.nix b/hosts/tailscale-subnet-router/host.nix new file mode 100644 index 0000000..b6ba534 --- /dev/null +++ b/hosts/tailscale-subnet-router/host.nix @@ -0,0 +1,10 @@ +_: + +{ + networking.hostName = "tailscale-router"; + + # No networking.hostId: only ZFS-touching hosts (server, docker) need one + # for pool-import safety, and this host does neither. + + system.stateVersion = "26.05"; +} diff --git a/modules/build-types/tailscale-exit-node.nix b/modules/build-types/tailscale-exit-node.nix deleted file mode 100644 index c96ddc0..0000000 --- a/modules/build-types/tailscale-exit-node.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ ... }: - -{ - imports = [ - ../tailscale/exit-node.nix - ]; - - # "server", not "both": this build type only ever advertises itself as an - # exit node (see ../tailscale/exit-node.nix) -- it doesn't advertise LAN - # subnet routes, so it doesn't need the "client"-side loose reverse-path - # filtering that "both" would also turn on. Deliberately left unbundled - # from LAN-subnet-route advertisement so this build type stays valid on - # every platform, including linode (a remote VPS with no network path to - # the home LAN at all). - services.tailscale.useRoutingFeatures = "server"; - - # Forwarded exit-node traffic arrives on tailscale0 already - # tailscale-authenticated -- the firewall's normal per-port allow-list - # would otherwise drop it. Standard NixOS/Tailscale exit-node guidance. - networking.firewall.trustedInterfaces = [ "tailscale0" ]; -} diff --git a/modules/build-types/tailscale-subnet-router.nix b/modules/build-types/tailscale-subnet-router.nix new file mode 100644 index 0000000..be481d2 --- /dev/null +++ b/modules/build-types/tailscale-subnet-router.nix @@ -0,0 +1,19 @@ +{ ... }: + +{ + imports = [ + ../tailscale/subnet-router.nix + ]; + + # "server", not "both": this build type advertises LAN subnet routes but + # doesn't use another tailscale exit node itself, so it doesn't need the + # "client"-side loose reverse-path filtering that "both" would also enable. + # Deliberately kept explicit here (not just relying on subnet-router.nix's + # own setting) so the intent is clear at the build-type level. + services.tailscale.useRoutingFeatures = "server"; + + # Forwarded subnet-router traffic arrives on tailscale0 already + # tailscale-authenticated -- the firewall's normal per-port allow-list + # would otherwise drop it. Standard NixOS/Tailscale subnet-router guidance. + networking.firewall.trustedInterfaces = [ "tailscale0" ]; +} diff --git a/modules/tailscale/exit-node.nix b/modules/tailscale/exit-node.nix deleted file mode 100644 index 1559ae2..0000000 --- a/modules/tailscale/exit-node.nix +++ /dev/null @@ -1,27 +0,0 @@ -_: - -{ - imports = [ ./enable-service.nix ]; - - services.tailscale = { - # Enables the sysctl forwarding settings exit nodes/subnet routers need; - # without this, --advertise-exit-node has no effect. - useRoutingFeatures = "server"; - - # Lets peers reach this node directly over the tailscale UDP port - # instead of relaying through DERP. - openFirewall = true; - - # extraSetFlags (tailscale set, via the always-on tailscaled-set - # service), not extraUpFlags -- extraUpFlags is only ever applied by - # tailscaled-autoconnect, which itself only runs when - # services.tailscale.authKeyFile is set (nothing in this repo sets one, - # so tailscale up is a manual, one-time operator step on every host that - # uses this service). extraSetFlags has no such gate, so - # --advertise-exit-node self-reapplies on every boot once the operator - # has authenticated the node once. - extraSetFlags = [ - "--advertise-exit-node" - ]; - }; -} diff --git a/modules/tailscale/subnet-router.nix b/modules/tailscale/subnet-router.nix new file mode 100644 index 0000000..286c896 --- /dev/null +++ b/modules/tailscale/subnet-router.nix @@ -0,0 +1,15 @@ +_: + +{ + imports = [ ./enable-service.nix ]; + + services.tailscale = { + # Enables the sysctl forwarding settings subnet routers need; + # without this, --advertise-routes has no effect. + useRoutingFeatures = "server"; + + # Lets peers reach this node directly over the tailscale UDP port + # instead of relaying through DERP. + openFirewall = true; + }; +} -- 2.54.0