{ config, pkgs, lib, ... }: let cfg = config.services.tailscale; tailscaleInterface = cfg.interfaceName; inherit (config.networking) hostName; tailnetHost = "${hostName}.tail05275.ts.net"; in { networking.wireguard.enable = true; networking.firewall.trustedInterfaces = [ tailscaleInterface ]; systemd.network = { # Fixes issues with other systemd networks when tailscale exist nodes are used config.networkConfig = { ManageForeignRoutes = false; ManageForeignRoutingPolicyRules = false; }; wait-online.ignoredInterfaces = [ "tailscale0" ]; }; services.networkd-dispatcher = { enable = true; rules = { # exclude LANs from tailscale subnet routes (when using `--accept-routes`) "50-tailscale-exclude-lan-routes" = { onState = [ "routable" ]; script = '' #!${pkgs.runtimeShell} # shellcheck disable=SC2010 lan_interfaces=$(ls /sys/class/net | grep -E '^(enp|eth|wlp)') if [[ "$lan_interfaces" == "" ]]; then exit 0; fi echo "$lan_interfaces" | while IFS= read -r lan_if; do for ipv in 4 6; do subnets=$(${pkgs.iproute2}/bin/ip -"$ipv" route show dev "$lan_if" proto kernel | cut -f1 -d' ' | grep '/') if [[ "$subnets" == "" ]]; then break; fi echo "$subnets" | while IFS= read -r subnet; do if ${pkgs.iproute2}/bin/ip -"$ipv" route show table 52 | grep -q "$subnet dev tailscale0"; then ${pkgs.iproute2}/bin/ip -"$ipv" route del "$subnet" dev tailscale0 table 52 ${pkgs.iproute2}/bin/ip -"$ipv" route add throw "$subnet" table 52 fi done done done ''; }; # UDP throughput improvements # https://tailscale.com/kb/1320/performance-best-practices?q=gro#linux-optimizations-for-subnet-routers-and-exit-nodes "50-tailscale-rx-udp-gro-forwarding" = { onState = [ "routable" ]; script = '' for dev in $(${pkgs.iproute2}/bin/ip route show 0/0 | cut -f5 -d' '); do ${lib.getExe pkgs.ethtool} -K "$dev" rx-udp-gro-forwarding on rx-gro-list off done ''; }; }; }; services.tailscale = { enable = true; package = pkgs.unstable.tailscale; openFirewall = true; useRoutingFeatures = lib.mkDefault "client"; extraUpFlags = [ "--reset" "--exit-node-allow-lan-access" "--exit-node=de-fra-wg-106.mullvad.ts.net" ]; }; systemd.services.tailscaled = { serviceConfig.Environment = [ "TS_DEBUG_FIREWALL_MODE=auto" ]; after = [ "network-online.target" "systemd-resolved.service" ]; }; # call taiscale up without --auth-key systemd.services.tailscaled-autoconnect = lib.mkIf (cfg.authKeyFile == null) { after = [ "tailscaled.service" ]; wants = [ "tailscaled.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.Type = "oneshot"; script = '' status=$(${config.systemd.package}/bin/systemctl show -P StatusText tailscaled.service) if [[ $status != Connected* ]]; then ${cfg.package}/bin/tailscale up fi # some options cannot be set immediately ${cfg.package}/bin/tailscale up ${lib.escapeShellArgs cfg.extraUpFlags} ${cfg.package}/bin/tailscale cert ${tailnetHost} '' + lib.optionalString config.services.nginx.enable '' chown nginx:nginx /var/lib/tailscale/certs/${tailnetHost}.{key,crt} ''; }; services.nginx.virtualHosts.${tailnetHost} = { sslCertificate = "/var/lib/tailscale/certs/${tailnetHost}.crt"; sslCertificateKey = "/var/lib/tailscale/certs/${tailnetHost}.key"; }; # TODO Tailscale Mullvad exit nodes currently don't support IPv6 and this is # causing issues with nginx (proxy pass) requests timing out and high CPU load. # Until Mullvad exit nodes support IPv6, we'll just disable IPv6 for nginx. services.nginx.resolver.ipv6 = false; }