nix: add dns.certificates and dns.challenge options

Updates #2137
This commit is contained in:
Kristoffer Dalby committed 2026-10-18 20:53:18 +02:00
1 parent 694587bfa0
commit 7ed3dead3e
2 files changed
+68 -1

No files matched your search

+56
View File
@@ -426,6 +426,55 @@ in
''; '';
example = [ "mydomain.internal" ]; example = [ "mydomain.internal" ];
}; };
certificates = {
enabled = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether nodes can get TLS certificates for their MagicDNS name
with `tailscale cert`. Needs a public {option}`base_domain` and
{option}`dns.challenge.provider`.
'';
};
zone = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
DNS zone the challenge records are written to. Empty means
{option}`base_domain`.
'';
example = "example.com";
};
};
challenge.provider = lib.mkOption {
type = lib.types.nullOr (
lib.types.submodule {
freeformType = settingsFormat.type;
options.name = lib.mkOption {
type = lib.types.str;
description = ''
DNS provider that writes ACME DNS-01 challenge records:
acmedns, cloudflare, gigahost, hetzner, powerdns or rfc2136.
'';
};
}
);
default = null;
description = ''
DNS provider for ACME DNS-01 challenges. Every key besides
`name` is a setting of the provider; a text setting can be read
from a file by adding `_path` to its name.
'';
example = lib.literalExpression ''
{
name = "cloudflare";
api_token_path = "''${CREDENTIALS_DIRECTORY}/cloudflare-token";
}
'';
};
}; };
oidc = { oidc = {
@@ -677,6 +726,13 @@ in
assertion = with cfg.settings; dns.override_local_dns -> dns.nameservers.global != [ ]; assertion = with cfg.settings; dns.override_local_dns -> dns.nameservers.global != [ ];
message = "dns.nameservers.global must be set when overriding local DNS"; message = "dns.nameservers.global must be set when overriding local DNS";
} }
{
assertion =
with cfg.settings;
dns.certificates.enabled
-> dns.magic_dns && dns.base_domain != "" && dns.challenge.provider != null;
message = "dns.certificates.enabled needs dns.magic_dns, dns.base_domain and dns.challenge.provider";
}
(assertRemovedOption [ "settings" "acl_policy_path" ] "Use `policy.path` instead.") (assertRemovedOption [ "settings" "acl_policy_path" ] "Use `policy.path` instead.")
(assertRemovedOption [ "settings" "db_host" ] "Use `database.postgres.host` instead.") (assertRemovedOption [ "settings" "db_host" ] "Use `database.postgres.host` instead.")
(assertRemovedOption [ "settings" "db_name" ] "Use `database.postgres.name` instead.") (assertRemovedOption [ "settings" "db_name" ] "Use `database.postgres.name` instead.")
+12 -1
View File
@@ -24,13 +24,21 @@ in
nodes = { nodes = {
headscale = { headscale = {
imports = [ self.nixosModules.testkit ]; imports = [ self.nixosModules.testkit ];
services.headscale.settings.dns.extra_records = [ services.headscale.settings.dns = {
extra_records = [
{ {
name = "foo.bar"; name = "foo.bar";
type = "A"; type = "A";
value = "100.64.0.2"; value = "100.64.0.2";
} }
]; ];
# Startup builds the provider but does not call it.
certificates.enabled = true;
challenge.provider = {
name = "rfc2136";
server = "127.0.0.1:53";
};
};
}; };
# All of peer1's noise goes over the kit's TLS listener, not plain :80. # All of peer1's noise goes over the kit's TLS listener, not plain :80.
peer1 = { peer1 = {
@@ -56,6 +64,9 @@ in
peer2.wait_until_succeeds("tailscale ping --until-direct=false -c 1 peer1.tailnet") peer2.wait_until_succeeds("tailscale ping --until-direct=false -c 1 peer1.tailnet")
res = peer1.wait_until_succeeds("${lib.getExe pkgs.dig} +short foo.bar").strip() res = peer1.wait_until_succeeds("${lib.getExe pkgs.dig} +short foo.bar").strip()
assert res == "100.64.0.2", res assert res == "100.64.0.2", res
peer1.wait_until_succeeds(
"tailscale status --json | ${lib.getExe pkgs.jq} -e '.CertDomains == [\"peer1.tailnet\"]'"
)
headscale.succeed("headscale policy set -f ${policy}") headscale.succeed("headscale policy set -f ${policy}")