From 5ec04c3e11263206998bcc368731ee5325f28a50 Mon Sep 17 00:00:00 2001 From: Ziping Sun Date: Mon, 22 Jun 2026 01:32:53 +0800 Subject: [PATCH] deploy headscale and tailscale for laptop to access homelab --- .cursorignore | 2 + flake/hosts.nix | 4 + nixos/hosts/desktop/network.nix | 3 - nixos/hosts/router/network.nix | 7 +- nixos/services/coredns/default.nix | 105 +++++++++++++------------- nixos/services/coredns/szp15.com.zone | 33 ++++---- nixos/services/headscale.nix | 71 +++++++++++++++++ nixos/services/tailscale.nix | 33 ++++++++ terraform/aliyun/network.tf | 7 ++ 9 files changed, 189 insertions(+), 76 deletions(-) create mode 100644 nixos/services/headscale.nix create mode 100644 nixos/services/tailscale.nix diff --git a/.cursorignore b/.cursorignore index 067cd82..4e380e6 100644 --- a/.cursorignore +++ b/.cursorignore @@ -1 +1,3 @@ .decrypted~* +.env +.env.* diff --git a/flake/hosts.nix b/flake/hosts.nix index dda777d..595aafd 100644 --- a/flake/hosts.nix +++ b/flake/hosts.nix @@ -65,6 +65,7 @@ let services.coredns services.fail2ban services.sing-box + services.tailscale system.disko system.systemd-boot ]; @@ -76,6 +77,7 @@ let hosts.laptop system.disko services.sing-box + services.tailscale system.systemd-boot ]; @@ -84,9 +86,11 @@ let networking.wireguard services.blog services.coredns + services.headscale services.nginx services.rke2-hasee.gateway services.sing-box + services.tailscale system.disko system.systemd-boot ]; diff --git a/nixos/hosts/desktop/network.nix b/nixos/hosts/desktop/network.nix index 3261917..e78f9c7 100644 --- a/nixos/hosts/desktop/network.nix +++ b/nixos/hosts/desktop/network.nix @@ -28,7 +28,6 @@ DHCP = "yes"; dhcpV4Config.RoutesToDNS = false; linkConfig.MTUBytes = 9000; - networkConfig.UseDomains = true; }; systemd.network.networks."40-svc" = { matchConfig.Name = "svc"; @@ -40,7 +39,6 @@ "10.112.35.1" "10.112.35.2" ]; - domains = [ "nodes.szp.io" ]; networkConfig.IPv6AcceptRA = false; }; systemd.network.networks."40-wlan0" = { @@ -51,7 +49,6 @@ RouteMetric = 1025; }; ipv6AcceptRAConfig.RouteMetric = 1025; - networkConfig.UseDomains = true; }; networking.hosts."10.112.8.1" = [ "router.nodes.szp.io" diff --git a/nixos/hosts/router/network.nix b/nixos/hosts/router/network.nix index a543a19..4447b4b 100644 --- a/nixos/hosts/router/network.nix +++ b/nixos/hosts/router/network.nix @@ -114,8 +114,6 @@ "10.112.35.2" ]; EmitRouter = true; - EmitDomain = true; - Domain = "nodes.szp.io"; PoolOffset = 100; PoolSize = 100; }; @@ -148,6 +146,8 @@ networking.firewall.interfaces.svc.allowedTCPPorts = [ 53 ]; networking.firewall.interfaces.vm.allowedUDPPorts = [ 53 ]; networking.firewall.interfaces.vm.allowedTCPPorts = [ 53 ]; + networking.firewall.interfaces.tailscale0.allowedUDPPorts = [ 53 ]; + networking.firewall.interfaces.tailscale0.allowedTCPPorts = [ 53 ]; services.fail2ban.ignoreIP = [ "10.112.8.5" # desktop @@ -225,7 +225,8 @@ # allow traffic from VM and wireguard to service networking.firewall.extraForwardRules = '' iifname "lan" ip daddr {10.112.10.0/24, 10.112.12.0/24} accept - iifname {"vm", "wg0"} ip daddr 10.112.10.0/24 accept + iifname {"vm", "wg0", "tailscale0"} ip daddr 10.112.10.0/24 accept + iifname "tailscale0" ip saddr 100.112.36.128/25 ip daddr 10.112.0.0/19 accept ''; networking.nftables.tables.mss-clamping = { diff --git a/nixos/services/coredns/default.nix b/nixos/services/coredns/default.nix index 9bd638f..0e0b554 100644 --- a/nixos/services/coredns/default.nix +++ b/nixos/services/coredns/default.nix @@ -23,45 +23,48 @@ let in { config = lib.mkMerge [ + { + services.coredns.enable = true; + services.coredns.config = '' + (snip) { + errors + loadbalance + log + minimal + } + o.szp.io { + import snip + rewrite name suffix .o.szp.io .szp.io answer auto + forward . /run/systemd/resolve/resolv.conf + } + o.szp15.com { + import snip + rewrite name suffix .o.szp15.com .szp15.com answer auto + forward . /run/systemd/resolve/resolv.conf + } + ''; + } (lib.mkIf (name == primary) { - services.coredns = { - enable = true; - config = '' - (snip) { - errors - loadbalance - log - minimal - root /etc/coredns/zones - transfer { - to ${lib.concatStringsSep " " secondaryAddresses} - } - } - szp15.com { - import snip - file szp15.com.zone { - reload 30s - } + services.coredns.config = '' + (authoritative) { + root /etc/coredns/zones + transfer { + to ${lib.concatStringsSep " " secondaryAddresses} } - szp.io { - import snip - file szp.io.zone { - reload 30s - } + } + szp15.com { + import authoritative + file szp15.com.zone { + reload 30s } - o.szp.io { - import snip - rewrite name suffix .o.szp.io .szp.io answer auto - forward . /run/systemd/resolve/resolv.conf + } + szp.io { + import authoritative + file szp.io.zone { + reload 30s } - o.szp15.com { - import snip - rewrite name suffix .o.szp15.com .szp15.com answer auto - forward . /run/systemd/resolve/resolv.conf - } - ''; - }; - + } + ''; environment.etc."coredns/zones/szp15.com.zone".source = ./szp15.com.zone; environment.etc."coredns/zones/szp.io.zone".source = ./szp.io.zone; @@ -71,26 +74,20 @@ in ]; }) (lib.mkIf (name != primary) { - services.coredns = { - enable = true; - config = '' - (snip) { - errors - loadbalance - log - minimal - secondary { - transfer from ${primaryAddress} - } - } - szp15.com { - import snip - } - szp.io { - import snip + services.coredns.config = '' + (authoritative) { + import snip + secondary { + transfer from ${primaryAddress} } - ''; - }; + } + szp15.com { + import authoritative + } + szp.io { + import authoritative + } + ''; }) { networking.netns.coredns = { diff --git a/nixos/services/coredns/szp15.com.zone b/nixos/services/coredns/szp15.com.zone index 5afd02e..d4009ab 100644 --- a/nixos/services/coredns/szp15.com.zone +++ b/nixos/services/coredns/szp15.com.zone @@ -1,19 +1,20 @@ $ORIGIN szp15.com. $TTL 600 -@ IN SOA ns1.szp.io. me.szp.io. ( - 2026060902 ; serial Tue, 09 Jun 2026 00:28:00 UTC - 3H ; refresh - 40M ; retry - 1W ; expire - 30M ; minimum - ) - IN NS ns1.szp.io. - IN A 47.96.145.133 - IN AAAA 2408:4005:3c4:a40:d5b6:55ed:95b4:4eed +@ IN SOA ns1.szp.io. me.szp.io. ( + 2026062001 ; serial Sat, 20 Jun 2026 00:14:00 UTC + 3H ; refresh + 40M ; retry + 1W ; expire + 30M ; minimum + ) + IN NS ns1.szp.io. + IN A 47.96.145.133 + IN AAAA 2408:4005:3c4:a40:d5b6:55ed:95b4:4eed -hgh0 IN CNAME @ -s3 IN CNAME ingress.k8s.szp.io. -*.s3 IN CNAME ingress.k8s.szp.io. -whoami IN CNAME ingress.k8s.szp.io. -vault IN CNAME ingress.k8s.szp.io. -argocd IN CNAME ingress.k8s.szp.io. +hgh0 IN CNAME hgh0.o +tailnet IN CNAME tailnet.o +s3 IN CNAME ingress.k8s.szp.io. +*.s3 IN CNAME ingress.k8s.szp.io. +whoami IN CNAME ingress.k8s.szp.io. +vault IN CNAME ingress.k8s.szp.io. +argocd IN CNAME ingress.k8s.szp.io. diff --git a/nixos/services/headscale.nix b/nixos/services/headscale.nix new file mode 100644 index 0000000..17ea860 --- /dev/null +++ b/nixos/services/headscale.nix @@ -0,0 +1,71 @@ +{ pkgs, ... }: +let + domain = "tailnet.szp15.com"; + port = 40010; + metricsPort = 40011; + jsonFormat = pkgs.formats.json { }; +in +{ + # To setup headscale, run the following commands: + # + # headscale users create ziping-sun --email ziping-sun@szp.io --display-name "Ziping Sun" + # + services.headscale = { + enable = true; + settings = { + server_url = "https://${domain}"; + listen_addr = "[::1]:${toString port}"; + metrics_listen_addr = "[::1]:${toString metricsPort}"; + trusted_proxies = [ "::1/128" ]; + prefixes = { + v4 = "100.112.36.0/24"; + v6 = "fd7a:115c:a1e0:7024::/64"; + }; + + # DERP + derp.server = { + enabled = true; + stun_listen_addr = "0.0.0.0:3478"; + }; + + disable_check_updates = true; + + # POLICY + # https://github.com/juanfont/headscale/blob/v0.25.1/hscontrol/policy/acls_types.go + policy = { + mode = "file"; + path = jsonFormat.generate "policy.json" { + }; + }; + + # DNS + dns = { + magic_dns = false; + override_local_dns = false; + nameservers.split = { + "szp.io" = [ "10.112.35.1" ]; + "szp15.com" = [ "10.112.35.1" ]; + }; + }; + taildrop.enabled = false; + }; + }; + + preservation.preserveAt.default.directories = [ + { + directory = "/var/lib/headscale"; + mode = "0700"; + user = "headscale"; + group = "headscale"; + } + ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://[::1]:${toString port}"; + proxyWebsockets = true; + }; + }; +} diff --git a/nixos/services/tailscale.nix b/nixos/services/tailscale.nix new file mode 100644 index 0000000..78254e3 --- /dev/null +++ b/nixos/services/tailscale.nix @@ -0,0 +1,33 @@ +{ pkgs, ... }: +let + pkg = pkgs.tailscale.overrideAttrs (oldAttrs: { + patches = (oldAttrs.patches or [ ]) ++ [ + # Read CGNAT CIDR range from TS_CGNAT_RANGE environment variable if set + (pkgs.fetchpatch { + url = "https://github.com/sunziping2016/tailscale/commit/a3027f992d10b7be3148b04fbe685198445f1b6d.patch"; + hash = "sha256-RmfFLlucyKJzQwZxG2WwRG5ER3uIoj1qVVCYBH9g2bQ="; + }) + ]; + }); +in +{ + services.tailscale = { + enable = true; + package = pkg; + openFirewall = true; + extraDaemonFlags = [ "--no-logs-no-support" ]; + }; + + preservation.preserveAt.default.directories = [ + { + directory = "/var/lib/tailscale"; + mode = "0700"; + } + ]; + + # Limit tailscale to a smaller CIDR + # For Aliyun DNS, and reduce the risk of conflicting with ISP's PPPoE IP + systemd.services.tailscaled.environment = { + TS_CGNAT_RANGE = "100.112.36.0/24"; + }; +} diff --git a/terraform/aliyun/network.tf b/terraform/aliyun/network.tf index 08a40cf..ecc47ca 100644 --- a/terraform/aliyun/network.tf +++ b/terraform/aliyun/network.tf @@ -16,3 +16,10 @@ module "hgh_vpc" { resource "alicloud_vpc_ipv6_gateway" "hgh" { vpc_id = module.hgh_vpc.vpc_id } + +resource "alicloud_route_entry" "hgh_svc" { + route_table_id = module.hgh_vpc.route_table_id + destination_cidrblock = "10.112.32.0/24" + nexthop_type = "Instance" + nexthop_id = alicloud_instance.hgh0.id +}