main
1{lib}: let
2 fromClearOrSecret = addr:
3 if addr.clearText != null
4 then {
5 kind = "clear";
6 value = addr.clearText;
7 }
8 else if addr.secret.name != null && addr.secret.file != null
9 then {
10 kind = "secret";
11 name = addr.secret.name;
12 file = addr.secret.file;
13 }
14 else null;
15
16 mkClear = value:
17 if value != null
18 then {
19 kind = "clear";
20 inherit value;
21 }
22 else null;
23
24 resolve = host: domain: conf: let
25 prefer = conf.prefer or null;
26 mode =
27 if prefer != null
28 then prefer
29 else host.settings.services.direct-dns.mode or null;
30 v4 =
31 if mode == "direct"
32 then fromClearOrSecret host.address.ipv4
33 else if mode == "tailscale"
34 then mkClear host.address.ipv4.tailscale
35 else null;
36 v6 =
37 if mode == "direct"
38 then fromClearOrSecret host.address.ipv6
39 else if mode == "tailscale"
40 then mkClear host.address.ipv6.tailscale
41 else null;
42 in
43 assert lib.assertMsg (mode != "direct" || v4 != null || v6 != null)
44 "direct-dns: domain '${domain}' on host '${host.name}' prefers 'direct' but the host has no public IPv4/IPv6 configured."; {
45 inherit domain mode v4 v6;
46 hostName = host.name;
47 };
48in {
49 mkDirectEntries = reverseProxy:
50 lib.unique (
51 lib.concatMap (
52 r:
53 lib.mapAttrsToList (resolve r.source.host) (
54 lib.filterAttrs (_: conf: !(conf.tailscale or false)) r.value
55 )
56 )
57 reverseProxy
58 );
59}