diff --git a/hosts/raptus/default.nix b/hosts/raptus/default.nix index 5d3145f..a310d0d 100644 --- a/hosts/raptus/default.nix +++ b/hosts/raptus/default.nix @@ -13,7 +13,7 @@ in (modulesPath + "/profiles/qemu-guest.nix") ./disk-config.nix ./forgejo.nix - ./headscale.nix + ./headscale ]; age.secrets = { diff --git a/hosts/raptus/headscale.nix b/hosts/raptus/headscale.nix deleted file mode 100644 index 6e3f64a..0000000 --- a/hosts/raptus/headscale.nix +++ /dev/null @@ -1,195 +0,0 @@ -{ - config, - lib, - pkgs, - options, - ... -}: -let - inherit (lib) mkIf; - metricsEnable = false; - mkAcl = src: dst: { - action = "accept"; - inherit src dst; - }; - mkSshAcl = src: dst: users: { - action = "accept"; - inherit src dst users; - }; - domain = "hs.ccnlc.eu"; -in -mkIf config.services.headscale.enable { - environment.systemPackages = [ config.services.headscale.package ]; - - networking.firewall.allowedTCPPorts = [ - 3478 # DERP - ]; - - services = { - headscale = { - address = "127.0.0.1"; - port = 8521; - - settings = { - server_url = "https://${domain}"; - tls_cert_path = null; - tls_key_path = null; - ip_prefixes = [ - "100.64.0.0/10" - "fd7a:115c:a1e0::/48" - ]; - ephemeral_node_inactivity_timeout = "30m"; - node_update_check_interval = "10s"; - metrics_listen_addr = mkIf metricsEnable "127.0.0.1:8086"; - # logging - log = { - format = "text"; - level = "info"; - }; - - logtail.enabled = false; - - dns_config = { - override_local_dns = true; - magic_dns = true; - - # FIX: shan being down takes down entire tailnet - # 1 -> move dns to dedicated device - # 2 -> find out if fallback dns's are supported by headscale - nameservers = [ - "100.64.0.4" - ]; - extra_records = - let - mkRecords = map (sub: { - name = "${sub}.ccnlc.eu"; - type = "A"; - value = "100.64.0.4"; - }); - in - [ - { - name = "ccnlc.eu"; - type = "A"; - value = "100.64.0.4"; - } - { - name = "git.ccnlc.eu"; - type = "A"; - value = "100.64.0.3"; - } - ] - # Tailscale doesn't seem to support wildcard A/AAAA records - # - https://github.com/juanfont/headscale/issues/2159#issuecomment-2393406444 - ++ mkRecords [ - "immich" - "adguard" - "nextcloud" - "kitchenowl" - "navidrome" - "subsonic" - "nextcloud" - "paperless" - "truenas" - "fritz" - ]; - }; - - derp = { - # Reference: https://github.com/juanfont/headscale/issues/1326#issuecomment-1505487881 - server = { - enabled = true; - stun_listen_addr = "0.0.0.0:3478"; - - # Region code and name are displayed in the Tailscale UI to identify a DERP region - region_code = "headscale"; - region_name = "Headscale Embedded DERP"; - region_id = 999; - }; - - urls = [ ]; - paths = [ ]; - - auto_update_enabled = false; - update_frequency = "6h"; - }; - }; - }; - - nginx.virtualHosts.${domain} = { - forceSSL = true; - enableACME = true; - quic = true; - http3 = true; - - locations = { - "/" = { - proxyPass = "http://localhost:${toString config.services.headscale.port}"; - proxyWebsockets = true; - }; - - "/metrics" = mkIf metricsEnable { - proxyPass = "http://${toString config.services.headscale.settings.metrics_listen_addr}/metrics"; - }; - }; - - extraConfig = '' - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; - ''; - }; - }; - - systemd.services = { - tailscaled.after = [ "headscale.service" ]; - headscale = { - environment = { - # required to use ssh - HEADSCALE_EXPERIMENTAL_FEATURE_SSH = "1"; - }; - }; - }; - - services.headscale.settings.acl_policy_path = pkgs.writeTextFile { - name = "headscale-acl.hujson"; - text = builtins.toJSON { - acls = [ - (mkAcl [ "tag:client" ] [ - "tag:client:*" - "tag:server:*" - ]) # client -> {client, server} - (mkAcl [ - "tag:client" - "tag:server" - ] [ "tag:backup:${toString options.modules.server.rsync-daemon.port.default}" ]) - ]; - - ssh = [ - (mkSshAcl [ "tag:client" ] [ - "tag:server" - "tag:client" - ] [ "ny" ]) # client -> {client, server} - ]; - - tags = [ - "tag:client" - "tag:server" - "tag:backup" - ]; - - tagOwners = - let - users = [ "ny" ]; - tags = map (name: "tag:${name}") [ - "server" - "client" - "backup" - ]; - in - lib.genAttrs tags (_: users); - - autoApprovers = { - exitNode = [ "*" ]; - }; - }; - }; -} diff --git a/hosts/raptus/headscale/acls.nix b/hosts/raptus/headscale/acls.nix new file mode 100644 index 0000000..e8facce --- /dev/null +++ b/hosts/raptus/headscale/acls.nix @@ -0,0 +1,61 @@ +{ + pkgs, + options, + lib, + ... +}: +let + mkAcl = src: dst: { + action = "accept"; + inherit src dst; + }; + mkSshAcl = src: dst: users: { + action = "accept"; + inherit src dst users; + }; +in +{ + services.headscale.settings.acl_policy_path = pkgs.writeTextFile { + name = "headscale-acl.hujson"; + text = builtins.toJSON { + acls = [ + (mkAcl [ "tag:client" ] [ + "tag:client:*" + "tag:server:*" + ]) # client -> {client, server} + (mkAcl [ + "tag:client" + "tag:server" + ] [ "tag:backup:${toString options.modules.server.rsync-daemon.port.default}" ]) + ]; + + ssh = [ + (mkSshAcl [ "tag:client" ] [ + "tag:server" + "tag:client" + ] [ "ny" ]) # client -> {client, server} + ]; + + tags = [ + "tag:client" + "tag:server" + "tag:backup" + ]; + + tagOwners = + let + users = [ "ny" ]; + tags = map (name: "tag:${name}") [ + "server" + "client" + "backup" + ]; + in + lib.genAttrs tags (_: users); + + autoApprovers = { + exitNode = [ "*" ]; + }; + }; + }; +} diff --git a/hosts/raptus/headscale/default.nix b/hosts/raptus/headscale/default.nix new file mode 100644 index 0000000..f69172f --- /dev/null +++ b/hosts/raptus/headscale/default.nix @@ -0,0 +1,104 @@ +{ + config, + lib, + ... +}: +let + inherit (lib) mkIf; + metricsEnable = false; + domain = "hs.ccnlc.eu"; + stunPort = 3478; +in +{ + imports = [ + ./dns.nix + ./acls.nix + ]; + + config = mkIf config.services.headscale.enable { + + environment.systemPackages = [ config.services.headscale.package ]; + + networking.firewall.allowedUDPPorts = [ + stunPort # DERP + ]; + + services = { + headscale = { + address = "127.0.0.1"; + port = 8521; + + settings = { + server_url = "https://${domain}"; + tls_cert_path = null; + tls_key_path = null; + ip_prefixes = [ + "100.64.0.0/10" + "fd7a:115c:a1e0::/48" + ]; + ephemeral_node_inactivity_timeout = "30m"; + node_update_check_interval = "10s"; + metrics_listen_addr = mkIf metricsEnable "127.0.0.1:8086"; + # logging + log = { + format = "text"; + level = "info"; + }; + + logtail.enabled = false; + + derp = { + # Reference: https://github.com/juanfont/headscale/issues/1326#issuecomment-1505487881 + server = { + enabled = true; + stun_listen_addr = "0.0.0.0:${toString stunPort}"; + + # Region code and name are displayed in the Tailscale UI to identify a DERP region + region_code = "headscale"; + region_name = "Headscale Embedded DERP"; + region_id = 999; + }; + + urls = [ ]; + paths = [ ]; + + auto_update_enabled = false; + update_frequency = "6h"; + }; + }; + }; + + nginx.virtualHosts.${domain} = { + forceSSL = true; + enableACME = true; + quic = true; + http3 = true; + + locations = { + "/" = { + proxyPass = "http://localhost:${toString config.services.headscale.port}"; + proxyWebsockets = true; + }; + + "/metrics" = mkIf metricsEnable { + proxyPass = "http://${toString config.services.headscale.settings.metrics_listen_addr}/metrics"; + }; + }; + + extraConfig = '' + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + ''; + }; + }; + + systemd.services = { + tailscaled.after = [ "headscale.service" ]; + headscale = { + environment = { + # required to use ssh + HEADSCALE_EXPERIMENTAL_FEATURE_SSH = "1"; + }; + }; + }; + }; +} diff --git a/hosts/raptus/headscale/dns.nix b/hosts/raptus/headscale/dns.nix new file mode 100644 index 0000000..2ff1ab5 --- /dev/null +++ b/hosts/raptus/headscale/dns.nix @@ -0,0 +1,49 @@ +{ + config = { + services.headscale.settings.dns_config = { + override_local_dns = true; + magic_dns = true; + + # FIX: shan being down takes down entire tailnet + # 1 -> move dns to dedicated device + # 2 -> find out if fallback dns's are supported by headscale + nameservers = [ + "100.64.0.4" + ]; + extra_records = + let + mkRecords = map (sub: { + name = "${sub}.ccnlc.eu"; + type = "A"; + value = "100.64.0.4"; + }); + in + [ + { + name = "ccnlc.eu"; + type = "A"; + value = "100.64.0.4"; + } + { + name = "git.ccnlc.eu"; + type = "A"; + value = "100.64.0.3"; + } + ] + # Tailscale doesn't seem to support wildcard A/AAAA records + # - https://github.com/juanfont/headscale/issues/2159#issuecomment-2393406444 + ++ mkRecords [ + "immich" + "adguard" + "nextcloud" + "kitchenowl" + "navidrome" + "subsonic" + "nextcloud" + "paperless" + "truenas" + "fritz" + ]; + }; + }; +}