diff --git a/devices/vps6/default.nix b/devices/vps6/default.nix index fd9a4a47..2a0b2db6 100644 --- a/devices/vps6/default.nix +++ b/devices/vps6/default.nix @@ -60,6 +60,7 @@ inputs: fail2ban = {}; beesd."/" = {}; bind = {}; + headscale = {}; }; }; networking.nftables.tables.forward = diff --git a/devices/vps6/secrets.yaml b/devices/vps6/secrets.yaml index 65524e27..eff73e5f 100644 --- a/devices/vps6/secrets.yaml +++ b/devices/vps6/secrets.yaml @@ -46,6 +46,8 @@ coturn: xray-xmu-client: cookie: ENC[AES256_GCM,data:RZ2WFnsX7s/PVqA7ZKhGqw==,iv:CknFoAcHIiIwJI1IEXkFdWXcOCAZr50pfwmQN72OI8o=,tag:w2pNU1APxlSQsGMIEdE2OA==,type:str] tinc: ENC[AES256_GCM,data:E3OrPA67R48x5FJUW0ZbERlclz8Z/XokAaGTeBQLPEHSeqEArHYSZkdJRZejFrBruJPlGZMPNBQzlIBXOfXKwMnlBDaGJIIJHIzPDGG9W7QF4IIRK/BjVZHFwfKvZtbUDGsqLcCSe5+ttmyucBaFGquXhnD/Tu09uyWtRvS10KAJLY0Z2/16CFB1+8egJIcYw2TFXObo+KR92Va0qwiDSepKaJtYLimDGRKk04QGj+BYa5y8PjIG6bz8UG82mmCiV7XM3EPlSMA=,iv:kawsklNGFbRhxKuUwvNL2WyBxuYu2T/uks1cJ4i8NhA=,tag:V+jAaxQX7JCiR5+wIVW4Nw==,type:str] +postgresql: + headscale: ENC[AES256_GCM,data:z2cyyT1TcIhNJCBeGn072aFI2nAioWZQvpyzoky4tWtMymKlw4ilOtSYAsp+kaNOoqvWSmoAQNJLNzeDk1iTCQ==,iv:hZdS/CAVBO0k/AmX3qw3YwTYgK49Aeu5QI3YCAduiZ0=,tag:2l4GPV/T2GHjAAUDX3LaEA==,type:str] sops: age: - recipient: age19ax6vm3pv8rph5tq3mmehd9sy9jk823tw8svsd790r0lkslycquqvlwz9m @@ -66,7 +68,7 @@ sops: ZXFTU3ZCaW1pTVh0RUJzdDdGdHlPYTgK2mlgcX2kEc8+2UDdBnhUm6IIuh8V6agW ooxH9OEPXUVI/4JcDo4v8ZUhAyU1ehLH0Ef7PJCChOZe2KZmWSNbhA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-10-12T08:53:02Z" - mac: ENC[AES256_GCM,data:Nx+PkDiF0Rz1jqO93ylzCPAWOFoc9KFnMGixcHgvzl+hvxFMHFEx0CzPceLGBLaz3s22nSL5PPq2k2fPJ1Yi9+kndWsTQuTu7gHQLABCriFysTshcOHd9m5/I8vgKHNaaYGOfDNjhji8xL/naSx2rpCyJDKSygRvfPvBaNdOYMg=,iv:VRIOc8eSWSZPveq2sbojNs2u9qEyOOoomhGE+Jwgnw4=,tag:xKdg4x/DWjktD0QZpycwGg==,type:str] + lastmodified: "2025-11-06T03:58:47Z" + mac: ENC[AES256_GCM,data:/t09/unE18oWPfoCdyTFdTYCC73C5s3cmB9yLNo1MrLISK8b9DPUzuAOamhW0EXG97/++dNCIAl5VNO/HuU6xT5jH8GFZo3Z7ElFamSmYpKYqDBgTDPlRxGRsc663qeNzpV1VE79hl1ifKk+NrP5cNxG0+FMZ763+dxnde0gdcM=,iv:j1CruHLx3HxV8+joWGKqwU53X9HmvW8LdleSCzACGoM=,tag:rQwSr9W+PDDxhonUDYC49A==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 diff --git a/modules/services/headscale.nix b/modules/services/headscale.nix new file mode 100644 index 00000000..c6503131 --- /dev/null +++ b/modules/services/headscale.nix @@ -0,0 +1,43 @@ +inputs: +{ + options.nixos.services.headscale = let inherit (inputs.lib) mkOption types; in mkOption + { + type = types.nullOr (types.submodule { options = + { + hostname = mkOption { type = types.nonEmptyStr; default = "headscale.chn.moe"; }; + };}); + default = null; + }; + config = let inherit (inputs.config.nixos.services) headscale; in inputs.lib.mkIf (headscale != null) + { + services.headscale = + { + enable = true; + port = 6538; + settings = + { + server_url = "https://${headscale.hostname}"; + prefixes.v4 = "100.97.101.0/24"; + database.postgres = + { + user = "headscale"; + port = 5432; + password_file = inputs.config.nixos.system.sops.secrets."headscale/postgresql".path; + name = "headscale"; + host = "127.0.0.1"; + }; + dns = { base_domain = "hs.chn.moe"; override_local_dns = false; }; + }; + }; + nixos = + { + services = + { + nginx.https.${headscale.hostname}.location."/".proxy = + { upstream = "http://127.0.0.1:6538"; websocket = true; }; + postgresql.instances.headscale = {}; + }; + system.sops.secrets."headscale/postgresql" = { key = "postgresql/headscale"; owner = "headscale"; }; + }; + }; +}