summaryrefslogtreecommitdiff
path: root/os/kay/modules/network/headscale
diff options
context:
space:
mode:
authorsinanmohd <sinan@sinanmohd.com>2025-12-27 09:01:13 +0530
committersinanmohd <sinan@sinanmohd.com>2025-12-27 09:08:01 +0530
commit5b32b947de3ac1adb4317e9c92094d67561d1230 (patch)
treee0edc6f33674cd93c251e71d389d09923f4acf19 /os/kay/modules/network/headscale
parent04381c13682a9a7f1e29595bf3edf2abdc55c3b3 (diff)
chore(os/kay): refactor sops
Diffstat (limited to 'os/kay/modules/network/headscale')
-rw-r--r--os/kay/modules/network/headscale/default.nix210
-rw-r--r--os/kay/modules/network/headscale/secrets.yaml33
2 files changed, 243 insertions, 0 deletions
diff --git a/os/kay/modules/network/headscale/default.nix b/os/kay/modules/network/headscale/default.nix
new file mode 100644
index 0000000..6f35c5d
--- /dev/null
+++ b/os/kay/modules/network/headscale/default.nix
@@ -0,0 +1,210 @@
+{
+ config,
+ pkgs,
+ lib,
+ headplane,
+ namescale,
+ ...
+}:
+let
+ url = "https://headscale.${config.global.userdata.domain}";
+ stunPort = 3478;
+
+ # A workaround generate a valid Headscale config accepted by Headplane when `config_strict == true`.
+ settings = lib.recursiveUpdate config.services.headscale.settings {
+ tls_cert_path = "/dev/null";
+ tls_key_path = "/dev/null";
+ policy.path = "/dev/null";
+ };
+ format = pkgs.formats.yaml { };
+ headscaleConfig = format.generate "headscale.yml" settings;
+
+ policyFormat = pkgs.formats.json { };
+ policy = {
+ groups = {
+ "group:owner" = [ "sinan@" ];
+ "group:bud" = [
+ "sinan@"
+ "ann@"
+ ];
+ };
+ tagOwners = {
+ "tag:internal" = [ "group:owner" ];
+ "tag:bud_clients" = [ "group:bud" ];
+ "tag:cusat" = [ "group:owner" ];
+ "tag:gaijin" = [ "group:owner" ];
+ };
+ autoApprovers = {
+ routes = {
+ "192.168.43.0/24" = [
+ "group:owner"
+ "tag:internal"
+ ];
+ "192.168.38.0/24" = [
+ "group:owner"
+ "tag:internal"
+ ];
+ };
+ exitNode = [
+ "group:owner"
+ "tag:internal"
+ ];
+ };
+ acls = [
+ {
+ action = "accept";
+ src = [ "*" ];
+ dst = [ "namescale@:53" ];
+ }
+ {
+ action = "accept";
+ src = [ "headplane@" ];
+ dst = [ "*:*" ];
+ }
+
+ {
+ action = "accept";
+ src = [ "group:owner" ];
+ dst = [ "*:*" ];
+ }
+ {
+ action = "accept";
+ src = [ "nazer@" ];
+ dst = [ "autogroup:internet:*" ];
+ }
+
+ {
+ action = "accept";
+ src = [ "group:bud" ];
+ dst = [ "tag:bud_clients:*" ];
+ }
+ {
+ action = "accept";
+ src = [ "tag:bud_clients" ];
+ dst = [ "tag:bud_clients:80,443" ];
+ }
+ ];
+ };
+in
+{
+ imports = [
+ headplane.nixosModules.headplane
+ namescale.nixosModules.namescale
+ ];
+
+ nixpkgs.overlays = [ headplane.overlays.default ];
+ environment.systemPackages = [ config.services.headscale.package ];
+
+ sops.secrets = {
+ # server
+ "headplane/cookie_secret" = {
+ owner = config.services.headscale.user;
+ sopsFile = ./secrets.yaml;
+ };
+ "headplane/preauth_key" = {
+ owner = config.services.headscale.user;
+ sopsFile = ./secrets.yaml;
+ };
+ "namescale/preauth_key".sopsFile = ./secrets.yaml;
+ "headscale/noise_private_key" = {
+ owner = config.services.headscale.user;
+ sopsFile = ./secrets.yaml;
+ };
+ "headscale/derp_private_key" = {
+ owner = config.services.headscale.user;
+ sopsFile = ./secrets.yaml;
+ };
+ # client
+ "headscale/pre_auth_key".sopsFile = ./secrets.yaml;
+ };
+
+ networking = {
+ nameservers = [ "100.100.100.100" ];
+ search = [ config.services.headscale.settings.dns.base_domain ];
+
+ firewall = {
+ interfaces.ppp0.allowedUDPPorts = [ stunPort ];
+ trustedInterfaces = [ config.services.tailscale.interfaceName ];
+ };
+ };
+ # for exit node only
+ boot.kernel.sysctl = {
+ "net.ipv4.ip_forward" = true;
+ "net.ipv6.conf.all.forwarding" = true;
+ };
+
+ services = {
+ headscale = {
+ enable = true;
+ port = 8139;
+
+ settings = {
+ logtail.enabled = false;
+ server_url = url;
+ noise.private_key_path = config.sops.secrets."headscale/noise_private_key".path;
+ dns = {
+ base_domain = "tsnet.${config.global.userdata.domain}";
+ override_local_dns = false;
+ nameservers.split."${config.services.headscale.settings.dns.base_domain}" = [
+ "100.64.0.12"
+ "fd7a:115c:a1e0::c"
+ ];
+ };
+ derp = {
+ server = {
+ enabled = true;
+ private_key_path = config.sops.secrets."headscale/derp_private_key".path;
+ region_code = config.networking.hostName;
+ region_name = config.networking.hostName;
+ stun_listen_addr = "0.0.0.0:${toString stunPort}";
+ region_id = 6969;
+ automatically_add_embedded_derp_region = true;
+ };
+ urls = [ ];
+ };
+ policy = {
+ mode = "file";
+ path = policyFormat.generate "acl.json" policy;
+ };
+ };
+ };
+
+ headplane = {
+ enable = true;
+ settings = {
+ server = {
+ port = 8140;
+ cookie_secret_path = config.sops.secrets."headplane/cookie_secret".path;
+ };
+ headscale = {
+ inherit url;
+ config_path = "${headscaleConfig}";
+ };
+ integration.agent = {
+ enabled = true;
+ pre_authkey_path = config.sops.secrets."headplane/preauth_key".path;
+ };
+ };
+ };
+
+ tailscale = {
+ enable = true;
+ interfaceName = "headscale";
+ openFirewall = true;
+
+ authKeyFile = config.sops.secrets."headscale/pre_auth_key".path;
+ extraUpFlags = [
+ "--login-server=${url}"
+ "--advertise-exit-node"
+ "--advertise-routes=192.168.43.0/24,192.168.38.0/24"
+ "--advertise-tags=tag:internal"
+ ];
+ };
+
+ namescale = {
+ enable = true;
+ environmentFile = config.sops.secrets."namescale/preauth_key".path;
+ settings.tsnet.coordination_server_url = url;
+ };
+ };
+}
diff --git a/os/kay/modules/network/headscale/secrets.yaml b/os/kay/modules/network/headscale/secrets.yaml
new file mode 100644
index 0000000..d45ed72
--- /dev/null
+++ b/os/kay/modules/network/headscale/secrets.yaml
@@ -0,0 +1,33 @@
+headplane:
+ cookie_secret: ENC[AES256_GCM,data:lJxNpktCyTn99/6ihN+Igz+u3V5LRvh3QxjIiwZ25bU=,iv:YppMXzI2raebGkgyGnFl7jDWtvQgyc5YRmNesby6iOE=,tag:LRWMzUiXqPA7Q1qakeABhQ==,type:str]
+ preauth_key: ENC[AES256_GCM,data:UthaalTL3pw5YLvDGU/j7FoKWstsize+Z3TQ8EGwEBS2769HaZFGwjArPM5emXEV,iv:3hbdnFUeUMoRyEC9EcoxTpK9YPI0wbPT9dP70X2bsFo=,tag:ety2kbaCU0VsJj8d6rvSSQ==,type:str]
+namescale:
+ preauth_key: ENC[AES256_GCM,data:4gLo5nknO081l4YZ8oa0/PPQNcB/Ef2hv3OgjTb90gHj6jC3pccMKxf0FBKxbQSii7GNQbSd9cYXHkHRBg==,iv:XnwajNYuBtRTyjP+QDzsjgLeq5qUM3O+PmHX4eIPWuY=,tag:Khp+LU0lovF2hF/Ak8Lpqg==,type:str]
+headscale:
+ noise_private_key: ENC[AES256_GCM,data:Hh67ck/aaWmWSPmWpWG8op8kruhVUg32TzBs6TgNZxy/FuB5CUqbWZ9C86yq9awS8fgZDFmqPtSUWQdtNP2AglC2PjVcZVlP,iv:unfxTZ5WV1a01dYZWFYGeQh4ytmruWq7Ytb0xbPERDg=,tag:qK7O4tA/Y38pwyDE4gyN/w==,type:str]
+ derp_private_key: ENC[AES256_GCM,data:H154flCLVnRv8U3hOXyaEtKPQQo4UWAiZnLZxpf99Cmppp6VEt5hXsu+PQYiUERWWjL3Bry5Az2JBBENOk75S2cXXuHzehkY,iv:ALLHs2S1xkNf/tFYCKYFLHOV9AXeCFUjgq65LoKtMqA=,tag:MDRlnksrUwWOD1KO086VgA==,type:str]
+ pre_auth_key: ENC[AES256_GCM,data:lFAVurZSbAIGLX+C/Y9IWHUdBnAK2+r8gfyptYDxxM1vUjfZLysc2Znl8J6hOTt9,iv:KnqejSz5Dso6Qt6AUEHL5WaKFkPjzfKPKcFPrQ08vak=,tag:cj20F8xBlUt+7YIH0898UQ==,type:str]
+sops:
+ age:
+ - recipient: age1q5sfy74d53n6jxlgsc2zrsz4wcl9d830nxuagc3wfmdkrrp55ckq9ev6nv
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBocjExRHZxTlBLQno5d0w1
+ WjFuTnpRZjVDQTR0WnZxc1ZFVHVXZVpHVG1NCmVlbWdMS1RkeWtacE1rTTRaVE9h
+ RE1XRDhoU2JmbTJKR3RCMnMvZWZMWlkKLS0tIFIvVEtWb0F5QmRPRHNuU2xhcWdm
+ ZUpud0gveWlDNWl4andHTm85OWZuQ28KigPDOPCX3Q6LnTZAe8triJUpz0nhmJ+K
+ XD+VuUHhX/52GU7p3selEwn5O//Bc7zeneolniA79F69RDBpK4OYeA==
+ -----END AGE ENCRYPTED FILE-----
+ - recipient: age15989j5lkkf2kn5wa2p6qc8wlxjjksc63k5ync8rz8t4e87394pzqm7h4rm
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCQmRMaGRBcmJwZVI0RWlT
+ ajl0WHZwTnBLdEcybHRHYzVjTWlkczlYVEVBCmxrQXNiM1dRSlJoRDdtVDZEZjVZ
+ WUVtZjMrbGhjdWFITElWa3VGZHpxYUUKLS0tIGJ5eUovbHE2R1JzcTRwdXBkNVM4
+ NExtOUJrT3pvTmNndUlxekVhbXAxNDAKVYgJ3XNqWyJ5XHYbNnODMUdufFTBG+xE
+ Nkks0GuJCWMyho4jkksF+mWohGJz50DAZCcve38fGcD9Zkhp1gcYgw==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2025-12-27T03:24:25Z"
+ mac: ENC[AES256_GCM,data:wDNxc6cPdUOILZKJ86dYdC+YuOYpC/tkrGQkFSaC84rjhYzYm5rSkwo4NNTqZVamNC5i+8IaFiR+zq8NJM88KUoiFij7FMda6yDdexhgeBLMwCdX6nrjIFavZpJEhwxyHfjuy2mBYO4TU5xGaAgG2Dr6N0eqBNDMJoZzlKmHgo0=,iv:09nC3k+fCCxp+u8qKba3m2mX7K5izUnPcmhGpXjDr7M=,tag:fQFMRFnIIUcmWJGQEfiBzQ==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.11.0