Add Tailscale support to NixOS configurations
This commit is contained in:
@@ -50,6 +50,9 @@
|
||||
};
|
||||
|
||||
sops.secrets."users/server/hashedPassword".neededForUsers = true;
|
||||
sops.secrets."tailscale/auth-key" = {
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
security.sudo.wheelNeedsPassword = true;
|
||||
|
||||
@@ -63,6 +66,11 @@
|
||||
networking = {
|
||||
hostName = "nix-server";
|
||||
networkManager.enable = true;
|
||||
tailscale = {
|
||||
enable = true;
|
||||
authKeyFile = config.sops.secrets."tailscale/auth-key".path;
|
||||
subnetRouter.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
caching.attic = {
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
group = "users";
|
||||
mode = "0400";
|
||||
};
|
||||
sops.secrets."tailscale/auth-key" = {
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
chiasson.system.librepods.enable = true;
|
||||
chiasson.system.palera1n.enable = true;
|
||||
@@ -106,6 +109,11 @@
|
||||
networking = {
|
||||
hostName = "t2mbp";
|
||||
networkManager.enable = true;
|
||||
tailscale = {
|
||||
enable = true;
|
||||
authKeyFile = config.sops.secrets."tailscale/auth-key".path;
|
||||
acceptRoutes = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
self.nixosModules.systemLocalization
|
||||
self.nixosModules.systemFonts
|
||||
self.nixosModules.systemNetworking
|
||||
self.nixosModules.systemTailscale
|
||||
self.nixosModules.systemLocalsend
|
||||
self.nixosModules.systemChromiumHevcVaapi
|
||||
self.nixosModules.systemMonitorInput
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.systemTailscale =
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.chiasson.system.networking.tailscale;
|
||||
tsCfg = config.services.tailscale;
|
||||
in
|
||||
{
|
||||
options.chiasson.system.networking.tailscale = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Tailscale mesh VPN. Joins this host to your tailnet for encrypted access
|
||||
from anywhere without opening inbound ports on your router.
|
||||
'';
|
||||
|
||||
authKeyFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = "/run/secrets/tailscale/auth-key";
|
||||
description = ''
|
||||
Reusable Tailscale auth key (sops path). Create one at
|
||||
https://login.tailscale.com/admin/settings/keys — enable Reusable and
|
||||
Pre-approved. Store under `tailscale/auth-key` in secrets/secrets.yaml.
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Allow Tailscale UDP 41641 through the host firewall.";
|
||||
};
|
||||
|
||||
acceptRoutes = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Accept subnet routes advertised by other tailnet nodes (e.g. home LAN
|
||||
via nix-server). Enable on portable clients like t2mbp.
|
||||
'';
|
||||
};
|
||||
|
||||
subnetRouter = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Advertise the home LAN through this node so other tailnet devices can
|
||||
reach local IPs (SSH, Attic, Gitea, Jellyfin, etc.). Enable on an
|
||||
always-on home host (nix-server). Approve the route in the Tailscale
|
||||
admin console after first connect.
|
||||
'';
|
||||
|
||||
routes = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "192.168.2.0/24" ];
|
||||
example = [ "192.168.2.0/24" ];
|
||||
description = "Subnets to advertise to the tailnet.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
services.tailscale = {
|
||||
enable = true;
|
||||
openFirewall = cfg.openFirewall;
|
||||
authKeyFile = cfg.authKeyFile;
|
||||
};
|
||||
|
||||
networking.firewall.trustedInterfaces = lib.mkAfter [ "tailscale0" ];
|
||||
}
|
||||
(lib.mkIf cfg.acceptRoutes {
|
||||
services.tailscale.useRoutingFeatures = "client";
|
||||
services.tailscale.extraUpFlags = [ "--accept-routes" ];
|
||||
})
|
||||
(lib.mkIf cfg.subnetRouter.enable {
|
||||
services.tailscale.useRoutingFeatures = "both";
|
||||
services.tailscale.extraUpFlags =
|
||||
map (route: "--advertise-routes=${route}") cfg.subnetRouter.routes;
|
||||
})
|
||||
(lib.mkIf (cfg.authKeyFile != null) {
|
||||
# Upstream uses `$(cat $authKeyFile)` which breaks when sops leaves a trailing newline.
|
||||
systemd.services.tailscaled-autoconnect.script = lib.mkOverride 50 ''
|
||||
getState() {
|
||||
tailscale status --json --peers=false | jq -r '.BackendState'
|
||||
}
|
||||
|
||||
lastState=""
|
||||
while state="$(getState)"; do
|
||||
if [[ "$state" != "$lastState" ]]; then
|
||||
case "$state" in
|
||||
NeedsLogin|NeedsMachineAuth|Stopped)
|
||||
echo "Server needs authentication, sending auth key"
|
||||
tailscale up --auth-key "$(tr -d '\n\r' < ${cfg.authKeyFile})" ${lib.escapeShellArgs tsCfg.extraUpFlags}
|
||||
;;
|
||||
Running)
|
||||
echo "Tailscale is running"
|
||||
systemd-notify --ready
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Waiting for Tailscale State = Running or systemd timeout"
|
||||
;;
|
||||
esac
|
||||
echo "State = $state"
|
||||
fi
|
||||
lastState="$state"
|
||||
sleep .5
|
||||
done
|
||||
'';
|
||||
})
|
||||
]);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user