Watch
0
0
Fork
You've already forked hyperhive
0

the bridge resolver answers other record types for its own names locally instead of waiting on an upstream

This commit is contained in:
atlas 2026-11-05 16:04:40 +01:00
commit 89bbeb9757
2 changed files with 48 additions and 13 deletions

View file

@ -11,6 +11,11 @@
networkCfg,
hyperhiveDomain,
}:
let
# The domains this resolver answers itself: the hive domain and every
# name a module contributed.
localDomains = lib.optional (hyperhiveDomain != null) hyperhiveDomain ++ lib.unique cfg.localNames;
in
{
enable = true;
# ON for its *plumbing*, not for the address it publishes.
@ -52,18 +57,25 @@
# Hive authoritative records — answer queries for the hive domain
# + its sub-domains with the bridge IP, where nginx is reachable
# from every container netns.
address =
lib.optional (hyperhiveDomain != null) "/${hyperhiveDomain}/${networkCfg.bridgeIp}"
# Names contributed by the modules that own them
# (`gateway.localNames`). Same address as everything above — the
# bridge IP is the gateway's answer for anything it fronts, and a
# contributing module neither knows nor should know it.
#
# `unique` is not tidiness: two modules claiming one name would
# otherwise emit two `address=` rules for it, and dnsmasq resolves
# that by precedence rather than by complaining. An assertion in
# ./default.nix makes the collision loud instead.
++ map (name: "/${name}/${networkCfg.bridgeIp}") (lib.unique cfg.localNames);
#
# Names contributed by the modules that own them
# (`gateway.localNames`) get the same address — the bridge IP is the
# gateway's answer for anything it fronts, and a contributing module
# neither knows nor should know it.
#
# `unique` is not tidiness: two modules claiming one name would
# otherwise emit two `address=` rules for it, and dnsmasq resolves
# that by precedence rather than by complaining. An assertion in
# ./default.nix makes the collision loud instead.
address = map (name: "/${name}/${networkCfg.bridgeIp}") localDomains;
# Every domain above is also `local=`. Since 2.86 an `address=` rule
# answers only the queries of its own address family and sends the rest
# upstream, so an AAAA query for a name answered with an IPv4 address
# waits on the host's upstream resolver, and a host with none that
# answers (no network) leaves the client to its own timeout. `local=`
# with the same domain keeps those queries here: the A answer is
# unchanged and the other types get an empty answer.
local = map (name: "/${name}/") localDomains;
# DHCP pool covering all usable host addresses on the bridge
# subnet — bounds computed by hive-network.nix from
# bridgeIp/bridgePrefixLength. All containers (agents and service

View file

@ -364,6 +364,21 @@ let
&& allLocal.services.hyperhive.otel.collector.host == "hive-collector.hive"
&& lib.elem "/hive-collector.hive/${bridge}" allLocal.services.dnsmasq.settings.address;
}
{
# An `address=` rule alone sends the other record types upstream
# (dnsmasq 2.86), so each of its domains is also `local=`, and no other
# domain is: `local=` on a domain with public records would hide them.
name = "the resolver keeps queries for every domain it answers itself, and only those, off the upstream";
ok =
let
s = allLocal.services.dnsmasq.settings;
domain = rule: builtins.elemAt (lib.splitString "/" rule) 1;
answered = map domain s.address;
in
lib.elem "hive-collector.hive" answered
&& map domain (s.local or [ ]) == answered
&& lib.all (rule: lib.hasSuffix "/" rule) (s.local or [ ]);
}
{
# The absence arm, and what makes the agents' queue address cases above able
# to fail.