From d4ae06c73b6aa26926cb52adeda5479472f4a8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= Date: Fri, 11 Oct 2024 13:23:36 -0700 Subject: [PATCH 1/5] nixos/headscale: assert that server_url does not contain base_domain --- nixos/modules/services/networking/headscale.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix index 622a13fe7b61..c2e616d30e87 100644 --- a/nixos/modules/services/networking/headscale.nix +++ b/nixos/modules/services/networking/headscale.nix @@ -500,6 +500,15 @@ in { ]; config = lib.mkIf cfg.enable { + assertions = [ + { + # This is stricter than it needs to be but is exactly what upstream does: + # https://github.com/kradalby/headscale/blob/adc084f20f843d7963c999764fa83939668d2d2c/hscontrol/types/config.go#L799 + assertion = with cfg.settings; dns.use_username_in_magic_dns or false || dns.base_domain == "" || !lib.hasInfix dns.base_domain server_url; + message = "server_url cannot contain the base_domain, this will cause the headscale server and embedded DERP to become unreachable from the Tailscale node."; + } + ]; + services.headscale.settings = lib.mkMerge [ cliConfig { From dfb0f00fc95e4f94ccab732224300d6cfe6980a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= Date: Fri, 11 Oct 2024 13:58:20 -0700 Subject: [PATCH 2/5] nixos/headscale: don't set deprecated options in config We cannot use `mkRenamedOptionModule` or `mkRemovedOptionModule` inside a freeform option. Thus we have to manually assert these deprecated options aren't used rather than aliasing them to their replacement. --- .../modules/services/networking/headscale.nix | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix index c2e616d30e87..fd2fd8dbede9 100644 --- a/nixos/modules/services/networking/headscale.nix +++ b/nixos/modules/services/networking/headscale.nix @@ -20,6 +20,11 @@ settingsFormat = pkgs.formats.yaml {}; configFile = settingsFormat.generate "headscale.yaml" cfg.settings; cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig; + + assertRemovedOption = option: message: { + assertion = !lib.hasAttrByPath option cfg; + message = "The option `services.headscale.${lib.options.showOption option}` was removed. " + message; + }; in { options = { services.headscale = { @@ -82,21 +87,6 @@ in { type = lib.types.submodule { freeformType = settingsFormat.type; - imports = with lib; [ - (mkAliasOptionModule ["acl_policy_path"] ["policy" "path"]) - (mkAliasOptionModule ["db_host"] ["database" "postgres" "host"]) - (mkAliasOptionModule ["db_name"] ["database" "postgres" "name"]) - (mkAliasOptionModule ["db_password_file"] ["database" "postgres" "password_file"]) - (mkAliasOptionModule ["db_path"] ["database" "sqlite" "path"]) - (mkAliasOptionModule ["db_port"] ["database" "postgres" "port"]) - (mkAliasOptionModule ["db_type"] ["database" "type"]) - (mkAliasOptionModule ["db_user"] ["database" "postgres" "user"]) - (mkAliasOptionModule ["dns_config" "base_domain"] ["dns" "base_domain"]) - (mkAliasOptionModule ["dns_config" "domains"] ["dns" "search_domains"]) - (mkAliasOptionModule ["dns_config" "magic_dns"] ["dns" "magic_dns"]) - (mkAliasOptionModule ["dns_config" "nameservers"] ["dns" "nameservers" "global"]) - ]; - options = { server_url = lib.mkOption { type = lib.types.str; @@ -507,6 +497,17 @@ in { assertion = with cfg.settings; dns.use_username_in_magic_dns or false || dns.base_domain == "" || !lib.hasInfix dns.base_domain server_url; message = "server_url cannot contain the base_domain, this will cause the headscale server and embedded DERP to become unreachable from the Tailscale node."; } + (assertRemovedOption ["settings" "acl_policy_path"] "Use `policy.path` instead.") + (assertRemovedOption ["settings" "db_host"] "Use `database.postgres.host` instead.") + (assertRemovedOption ["settings" "db_name"] "Use `database.postgres.name` instead.") + (assertRemovedOption ["settings" "db_password_file"] "Use `database.postgres.password_file` instead.") + (assertRemovedOption ["settings" "db_path"] "Use `database.sqlite.path` instead.") + (assertRemovedOption ["settings" "db_port"] "Use `database.postgres.port` instead.") + (assertRemovedOption ["settings" "db_type"] "Use `database.type` instead.") + (assertRemovedOption ["settings" "db_user"] "Use `database.postgres.user` instead.") + (assertRemovedOption ["settings" "dns_config"] "Use `dns` instead.") + (assertRemovedOption ["settings" "dns_config" "domains"] "Use `dns.search_domains` instead.") + (assertRemovedOption ["settings" "dns_config" "nameservers"] "Use `dns.nameservers.global` instead.") ]; services.headscale.settings = lib.mkMerge [ From b5cb8fb063065d7c6ddaacc33f28d2cf5e0049cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= Date: Fri, 11 Oct 2024 20:10:31 -0700 Subject: [PATCH 3/5] nixos/tests/headscale: set dns.base_domain Otherwise the test fails with dns.base_domain must be set when using MagicDNS --- nixos/tests/headscale.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nixos/tests/headscale.nix b/nixos/tests/headscale.nix index 80188b65dbfc..15f7c7be2ae5 100644 --- a/nixos/tests/headscale.nix +++ b/nixos/tests/headscale.nix @@ -38,6 +38,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: region_id = 999; stun_listen_addr = "0.0.0.0:${toString stunPort}"; }; + dns.base_domain = "tailnet"; }; }; nginx = { @@ -77,6 +78,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: # Check that they are reachable from the tailnet peer1.wait_until_succeeds("tailscale ping peer2") - peer2.wait_until_succeeds("tailscale ping peer1") + peer2.wait_until_succeeds("tailscale ping peer1.tailnet") ''; }) From 0673e982484e96fe1dbf29ff5c698ddd038dcde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= Date: Fri, 11 Oct 2024 20:17:15 -0700 Subject: [PATCH 4/5] nixos/headscale: update option descriptions --- nixos/modules/services/networking/headscale.nix | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix index fd2fd8dbede9..aac6d331a027 100644 --- a/nixos/modules/services/networking/headscale.nix +++ b/nixos/modules/services/networking/headscale.nix @@ -289,7 +289,6 @@ in { default = true; description = '' Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). - Only works if there is at least a nameserver defined. ''; example = false; }; @@ -299,11 +298,13 @@ in { default = ""; description = '' Defines the base domain to create the hostnames for MagicDNS. - {option}`baseDomain` must be a FQDNs, without the trailing dot. - The FQDN of the hosts will be - `hostname.namespace.base_domain` (e.g. - `myhost.mynamespace.example.com`). + This domain must be different from the {option}`server_url` + domain. + {option}`base_domain` must be a FQDN, without the trailing dot. + The FQDN of the hosts will be `hostname.base_domain` (e.g. + `myhost.tailnet.example.com`). ''; + example = "tailnet.example.com"; }; nameservers = { From cc4d29d3532c5ff5ff832aecfbc7f06fe749f2cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= Date: Sat, 12 Oct 2024 18:28:17 -0700 Subject: [PATCH 5/5] nixos/headscale: assert that dns.base_domain is set when using MagicDNS --- nixos/modules/services/networking/headscale.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix index aac6d331a027..9261ec03c532 100644 --- a/nixos/modules/services/networking/headscale.nix +++ b/nixos/modules/services/networking/headscale.nix @@ -498,6 +498,10 @@ in { assertion = with cfg.settings; dns.use_username_in_magic_dns or false || dns.base_domain == "" || !lib.hasInfix dns.base_domain server_url; message = "server_url cannot contain the base_domain, this will cause the headscale server and embedded DERP to become unreachable from the Tailscale node."; } + { + assertion = with cfg.settings; dns.magic_dns -> dns.base_domain != ""; + message = "dns.base_domain must be set when using MagicDNS"; + } (assertRemovedOption ["settings" "acl_policy_path"] "Use `policy.path` instead.") (assertRemovedOption ["settings" "db_host"] "Use `database.postgres.host` instead.") (assertRemovedOption ["settings" "db_name"] "Use `database.postgres.name` instead.")