DNS Architecture¶
DNS in this estate is split into five deliberately separate planes: an external authoritative service, an internal authoritative + resolver plane, a split-horizon view resolver, a public recursive resolver run as a community service, and a fixed upstream policy. Each plane fails independently of the others.
flowchart TD
subgraph external["External / authoritative"]
bind["BIND 9.20\nnameserver ns — LB .53\npublic 31.3.128.59"]
dnsdist["dnsdist on BPI-R4\nnative IPv6 frontend\nPROXYv2 → BIND"]
hap["haproxy on BPI-R4\npublic v4 TCP :53\nsend-proxy-v2 → BIND"]
end
subgraph internal["Internal"]
tech["Technitium — LB .54\ninternal zones + DHCP"]
unb["unbound split-horizon\nLB 192.168.1.1\ninternal views"]
end
opennic["OpenNIC tier-2\nrecursive — LB .44"]
rtr["bpi-r4 dnsmasq\n.254"]
nextdns["NextDNS\n(+ Quad9 last resort)"]
v6c["IPv6 clients"] --> dnsdist --> bind
v4t["IPv4 TCP clients"] --> hap --> bind
v4u["IPv4 UDP clients"] -->|DNAT| bind
lan["LAN clients"] --> rtr
lan --> unb --> tech
rtr -->|"own zones"| unb
rtr --> tech
tech -.->|"DoT upstream"| nextdns
rtr -.->|"fallback"| nextdns
world["Public resolver users"] --> opennic
External authoritative — BIND¶
ns.mdapi.ch (BIND 9.20 in the nameserver namespace, LoadBalancer 192.168.1.53,
public 31.3.128.59) is authoritative for the public zones, with DNSSEC and
external secondaries (zone transfers restricted to them). Dynamic updates arrive
TSIG-signed (cert-manager DNS-01, router ACME).
What makes the front end unusual is client-address preservation through the PROXY protocol on both address families:
- IPv6 terminates natively at dnsdist on the router — no NAT64 —
and reaches BIND over IPv4 carrying a PROXYv2 header per UDP datagram. DNS is
the one protocol ecosystem where PROXY-over-UDP exists on both ends (dnsdist
sends it, BIND ≥ 9.20 accepts it on a dedicated
proxy plainlistener). - IPv4 TCP is fronted by haproxy on the router with
send-proxy-v2into the same listener; plain UDP is a straight DNAT.
The result: BIND sees every real client address on every transport, so rate-limiting and ACLs operate per-client rather than per-translator.
Internal — Technitium and split-horizon unbound¶
Technitium (DNS LB .54, DHCP LB .55) is the internal authoritative server and DHCP for the
LAN — internal zones, per-client control, and an API that the automation plane
drives (zone diffing, health probes, query-log shipping run as Windmill probes).
It also serves as unbound's recursive upstream, resolving external names on the
LAN's behalf (itself over DoT to NextDNS) as well as the internal zones.
unbound (LB 192.168.1.1 — deliberately the address every DHCP client
already trusts) serves the split-horizon view: internal names resolve to internal
addresses, everything else is forwarded. Public names of self-hosted services resolve
to their LAN addresses here, so internal traffic never hairpins through the edge.
Non-override queries forward to Technitium; unbound has no other upstream, and
fallback happens one layer up — at the clients' second resolver and in CoreDNS.
LAN clients are handed two resolvers in their DHCP offer — the router at
.254 first, then .1 — so neither the cluster nor the router is a single resolver
point of failure. The router's dnsmasq sends the split-horizon zones to .1 and
everything else to Technitium, then NextDNS, then Quad9, so it stays independent of
the split-horizon resolver; if the cluster side stops answering entirely it can also
arm a harvested home.tillo.ch hosts-file zone, keeping the estate's own names
resolvable through a full cluster outage.
OpenNIC tier-2¶
A public recursive resolver (LB .44, IPv6 via the NAT64 prefix) run as an
OpenNIC community tier-2 — one of the estate's public-service contributions,
alongside the NTP pool membership.
Upstream policy¶
Recursion is internal-first: unbound forwards non-override queries to Technitium,
which is the actual recursive resolver (DoT upstream to NextDNS). NextDNS is
the external fallback, with Quad9 as a final last-resort rung, and every chain is
strictly sequential — one place for filtering policy, no fan-out across public
resolvers, and no outage cascades from a single upstream being unreachable.
In-cluster CoreDNS is internal-first in the same way: .1, then .254, then the
NextDNS pair, then Quad9.