Add an architecture analysis for the tension between Tailscale's default resolv.conf management (100.100.100.100) and the managed LAN-resolver resolv.conf (.252/.253). Documents a key finding from live-network probing: knel.net device records only resolve via the Tailscale MagicDNS path; querying the LAN DNS servers directly returns NXDOMAIN because their knel.net zone is stale (SOA serial 2025-06-23). Lays out four options (Tailscale-owned, LAN-pinned, split DNS, Tailscale-pushes-LAN-resolvers) with pros/cons, recommends leaving DNS to Tailscale in the short term (since wazuh/postfix/syslog depend on knel.net names that only resolve there) and fixing the Technitium/Pi-hole knel.net zone before pinning the LAN resolvers. Confirms the NTP (LAN-IP) change is safe regardless. Flags that the managed-resolv.conf change will be overwritten by Tailscale and would break knel.net resolution if it ever sticks. 🤖 Generated with [Crush](https://github.com/charmassociates/crush) Assisted-by: GLM-5 via Crush <crush@charm.land>
12 KiB
Tailscale vs. Managed DNS — Architecture Analysis
Status: analysis for review. No code decisions are final. Read the "Known issues" section before acting on the managed-resolv.conf change.
1. Executive summary
Every host in this build runs the Tailscale client, and Tailscale — by default —
manages /etc/resolv.conf itself, pointing it at 100.100.100.100
(Tailscale's MagicDNS resolver). This directly conflicts with the managed
resolv.conf (pointing at 192.168.3.252/192.168.3.253) that
SetupNewSystem.sh deploys: whichever runs last wins, and Tailscale's daemon
re-wins on every tailscale up and on reboot.
Worse, a probe of the live network shows that knel.net device records only
resolve through the Tailscale 100.100.100.100 path — querying the LAN IPs of
the DNS servers directly returns NXDOMAIN for current hostnames (the Technitium
knel.net zone has the SOA but is stale/empty of actual records). So pointing
resolv.conf at the LAN IPs would break resolution of the very names this
project's modules depend on (tsys-nsm.knel.net, tsys-cloudron.knel.net,
tsys-librenms.knel.net).
This document lays out the options and a recommended path forward.
2. How name resolution actually works today (as measured)
Probed from sectestbed-sandbox (192.168.3.50):
| Query path | External name (github.com) |
knel.net device name (pfv-netinfra-01.knel.net) |
|---|---|---|
Via current resolver = 100.100.100.100 (Tailscale) |
resolves | resolves → 100.70.181.72 (Tailscale CGNAT) |
Direct dig @192.168.3.252 (Technitium, LAN) |
resolves (recurses) | NXDOMAIN (SOA present, no record) |
Direct dig @192.168.3.253 (Pi-hole, LAN) |
resolves (recurses) | NXDOMAIN (SOA present, no record) |
Other measured facts:
dig @192.168.3.252 knel.net SOA→NOERROR, returnsknel.net. 900 IN SOA dns.knel.net. hostadmin.knel.net. 2025062313 …(serial dated 2025-06-23 — the zone exists but is stale).- NTP on both
.252and.253answers time queries (stratum 2/3). - The live
/etc/resolv.confon a deployed host reads:# resolv.conf(5) file generated by tailscale # DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN nameserver 100.100.100.100 nameserver fd7a:115c:a1e0::53 search knel.net
Interpretation: the knel.net device→Tailscale-IP mappings are synthesised
by Tailscale's MagicDNS from the tailnet device registry (every device that
joins the tailnet gets hostname.knel.net → its 100.x.x.x address). The
Technitium knel.net zone is a separate, manually-maintained zone that has
fallen out of date. The two are not the same source of truth.
3. The core tension
| Goal | Who provides it today |
|---|---|
Resolve *.knel.net device names (→ Tailscale IPs) |
Tailscale MagicDNS via 100.100.100.100 |
| Resolve external names with ad-blocking | Pi-hole (.253), reachable via Tailscale → Technitium → Pi-hole chain |
| Redundant, low-latency, tunnel-independent DNS | LAN resolvers .252/.253 — but these lack knel.net records |
| Authoritative time | NTP on .252/.253 (works on either path) |
The conflict: you cannot simply point resolv.conf at the LAN resolvers,
because they do not know about the current knel.net device records, and
several modules in this project resolve knel.net hostnames at runtime
(wazuh manager, postfix relay, syslog target). You also cannot ignore Tailscale,
because it is the only thing that resolves those names today.
4. Options
Option A — Let Tailscale own DNS (status quo, accept-dns=true)
Leave the default. Tailscale writes 100.100.100.100 to resolv.conf; the
control-plane forwarding (100.100.100.100 → Technitium → Pi-hole) handles
external names and ad-blocking; MagicDNS handles knel.net device names.
| Pros | Cons |
|---|---|
Zero per-host config; new machines "just work" on tailscale up |
All DNS depends on the Tailscale daemon being up. If tailscaled dies, every name lookup fails — including the ones you need to SSH in and fix it. |
| MagicDNS + knel.net names resolve automatically | Latency: every query goes host→tailscaled→100.100.100.100→(tunnel)→Technitium→Pi-hole→upstream |
| Ad-blocking preserved (via the Pi-hole hop) | Overwrites the managed resolv.conf — the .252/.253 redundancy is lost |
| Centralised in the Tailscale admin console | Single resolver in resolv.conf (100.100.100.100); no glibc-level failover |
Boot-order risk: early-boot processes have no DNS until tailscaled is up |
Option B — Pin resolv.conf to the LAN resolvers (accept-dns=false)
Set --accept-dns=false on every host and keep the managed resolv.conf
pointing at .252/.253.
| Pros | Cons |
|---|---|
DNS independent of Tailscale — survives tailscaled outages |
*.knel.net device names break (NXDOMAIN) because the LAN resolvers' knel.net zone is stale. This breaks wazuh/postfix/syslog hostname resolution. |
| Lowest latency, full glibc-level failover across two servers | MagicDNS names (*.ts.net) do not resolve |
Managed resolv.conf wins uncontested |
Requires fixing the Technitium/Pi-hole knel.net zone to mirror the Tailscale device records before this is viable |
| Boot-time DNS works immediately | Off-LAN hosts (laptops) can't reach .252/.253 without the tunnel — back to needing Tailscale |
Not recommended as-is. Only viable after the
knel.netzone on.252/.253is repopulated with current device records (see §6).
Option C — Tailscale Split DNS (per-domain routing)
MagicDNS ON, "Override local DNS" OFF in the admin console; only ts.net
(and explicitly split domains) route to 100.100.100.100, everything else stays
on the system resolver.
| Pros | Cons |
|---|---|
| Best of both worlds: MagicDNS names resolve AND general queries go direct | Requires systemd-resolved (or NetworkManager dns=dnsmasq) for per-domain routing. These hosts use a plain /etc/resolv.conf — on which Tailscale cannot do per-domain split; it replaces the whole file. |
| Reduces tunnel dependency for non-Tailscale names | Migrating every host to systemd-resolved is a significant, cross-cutting change |
| More moving parts to reason about and debug |
Option D — Make Tailscale push the LAN resolvers as global nameservers
In the admin console, set global nameservers to 192.168.3.252/192.168.3.253,
keep accept-dns=true.
| Pros | Cons |
|---|---|
| Clients get the LAN resolvers via Tailscale config (consistent) | Tailscale still overwrites resolv.conf |
MagicDNS still works (100.100.100.100 added for ts.net/knel.net) |
On-LAN hosts don't need Tailscale to find .252/.253 — pure indirection |
| Centralised management | Still depends on tailscaled for DNS |
knel.net device names still only resolve via the Tailscale path, so the LAN resolvers being "global" doesn't help those names unless the zone is fixed |
5. Recommendation
Short term (unblock now): Option A — let Tailscale own DNS. Revert/disable
the managed-resolv.conf deployment so provisioning stops fighting Tailscale.
Today, knel.net device names only resolve through Tailscale, and this
project's modules depend on those names, so Tailscale-managed DNS is the only
thing that currently works end-to-end. Keep the NTP change (LAN IPs, no DNS
dependency) — that part is safe and beneficial regardless.
Medium term (the real fix): populate the knel.net zone on the LAN
resolvers, then choose B or C. Concretely:
- Make Technitium (
.252) authoritative forknel.netwith current records (mirror the Tailscale device→IP mappings, or enable a zone-transfer/sync from the Tailscale device registry, or use Technitium's "Tailscale" DNS app if available). Confirmdig @192.168.3.252 pfv-netinfra-01.knel.netreturns an answer, not NXDOMAIN. - Make Pi-hole (
.253) forwardknel.netto Technitium (or also serve the zone), so both resolvers in the pair can answer internal names — otherwise glibc failover to.253would silently break knel.net lookups. - Then pin
resolv.confto.252/.253with--accept-dns=false(Option B), gaining tunnel-independent, redundant DNS.
Long term (optional, if per-domain routing is wanted): Option C — adopt
systemd-resolved and configure Tailscale Split DNS so ts.net/knel.net go
to MagicDNS and everything else goes direct. Only worth the migration cost if
you specifically need *.ts.net short-name resolution alongside direct LAN DNS.
Why not just force .252/.253 today?
Because it regresses name resolution for the hostnames this project already
uses. Concretely, with resolv.conf pinned to the LAN resolvers the following
would fail to resolve:
ProjectCode/Modules/Security/secharden-wazuh.sh→WAZUH_MANAGER="tsys-nsm.knel.net"ProjectCode/SetupNewSystem.sh→postconf -e "relayhost = tsys-cloudron.knel.net"ProjectCode/ConfigFiles/Syslog/rsyslog.conf→*.* @tsys-librenms.knel.net:514
All three resolve cleanly via 100.100.100.100 today and return NXDOMAIN via
.252/.253. Pinning the LAN resolvers before the zone is fixed would break
wazuh, mail relay, and syslog.
6. Known issues / action items
- Technitium
knel.netzone is stale. SOA serial2025062313(2025-06-23); current device names return NXDOMAIN from the LAN interface. Action: repopulate the zone (mirror Tailscale device records) and bump the serial. - Pi-hole (
.253) has noknel.netdevice records either. For the pair to be truly redundant for internal names,.253must either serve the same zone or conditional-forwardknel.netto.252. Action: configure Pi-hole to forwardknel.netto Technitium. - The managed-
resolv.confchange (commitf010fa9) conflicts with Tailscale. As written,SetupNewSystem.shwritesresolv.confwith.252/.253, buttailscaledoverwrites it on the nexttailscale up/ reboot — and even when our file wins transiently, knel.net names break. See §5 for the recommended handling. - NTP change is safe and good.
ntp.confnow uses LAN IPs (192.168.3.252/192.168.3.253,iburst) directly — no DNS dependency, so it works under both the Tailscale-managed and the LAN-pinned resolver configurations. Keep this regardless of the DNS decision. - Split-horizon possibility (unconfirmed). It is possible Technitium serves
a richer
knel.netzone on its Tailscale interface (100.x) than on its LAN interface (192.168.3.252). If so, the fix is to make the LAN view match the Tailscale view. Worth confirming withdig @<technitium-tailscale-ip> knel.net host.
7. Implementation guidance (once the zone is fixed)
When you are ready to move to tunnel-independent DNS (Option B):
- In provisioning, after
tailscale up, set--accept-dns=false:Or bake it into the tailscale systemd unit via a drop-in so re-boots hold.tailscale up --accept-dns=false … - Then deploy the managed
resolv.conf(.252/.253). Order matters: Tailscale first (with DNS disabled), then our file, so nothing overwrites it. - Add a watchdog (timer) that restores
resolv.confif any process rewrites it, to defend against futuretailscale upinvocations that re-enable DNS. - Validate with
Project-Tests/validation/dns-ntp-redundancy.sh— and extend its probe to assert*.knel.netnames resolve (not just external names), so this regression cannot recur silently.
8. TL;DR
- DNS: don't fight Tailscale yet. Today
knel.netnames only resolve via Tailscale, and this project depends on them. Fix the Technitium/Pi-holeknel.netzone first, then pin the LAN resolvers. - NTP: the LAN-IP change is correct and safe; keep it.
- The managed
resolv.conf(.252/.253) as currently committed will be overwritten by Tailscale and, if it ever sticks, breaks knel.net resolution — see §5/§6 before relying on it.