feat: forward-by-default, auto recursive mode, Linux install fixes #27

Merged
razvandimescu merged 7 commits from feat/auto-recursive-install-fixes into main 2026-04-01 13:49:16 +08:00
razvandimescu commented 2026-04-01 10:55:46 +08:00 (Migrated from github.com)

Summary

  • Forward mode as default: transparent proxy to system DNS — caching, .numa domains, ad blocking, developer overrides. Changes nothing about the network. Recursive and auto modes are explicit opt-in via config.
  • Auto upstream mode: probes up to 3 root servers on startup — recursive if outbound DNS works, Quad9 DoH fallback if blocked. Opt-in via mode = "auto".
  • Recursive mode: trusts user intent, no probe needed. Opt-in via mode = "recursive".
  • DNSSEC off by default: no-op in forward mode, avoids misleading dashboard. Enable with [dnssec] enabled = true alongside recursive mode.
  • Post-install discovery: both macOS and Linux print mode = "recursive" suggestion after install.
  • Linux install fixes: DNSStubListener=no in resolved drop-in, correct service start ordering (DNS config before numa), skip 127.0.0.53 stub in upstream detection.
  • macOS install health check: verifies numa is listening before redirecting DNS — if service fails to start (e.g. port 53 in use), unloads service and aborts instead of breaking DNS.
  • Search domain forwarding: parses resolv.conf search/domain lines and creates conditional forwarding rules to VPC resolver (fixes internal hostname resolution on cloud VMs).
  • Unified install/uninstall: numa install does everything (service + DNS + CA trust), numa uninstall is the mirror.
  • Consistent loopback filtering: extracted is_loopback_or_stub() replacing 4 inconsistent inline checks.
  • Hardened resolvectl parsing: validates IPs to avoid IPv6 fragment extraction.
  • DEFAULT_API_PORT constant: extracted from hardcoded magic number.

Test plan

  • make all passes (fmt, clippy, audit, build, 111 tests)
  • CI green (Linux clippy + test)
  • Forward mode default — Docker: mode: "forward", detects system DNS
  • mode = "recursive" — EC2: resolves from root, DNSSEC AD flag set
  • mode = "auto" — EC2: probe succeeded, resolved to recursive
  • curl /stats | jq .mode — returns correct mode in all three configurations
  • DNS resolution — dig @127.0.0.1 example.com returns valid answer
  • DNSSEC — AD flag set in recursive mode with enabled = true
  • Dashboard — HTTP 200
  • DoH fallback — Docker (blocked network): probe failed on 3 hints → Quad9 DoH
  • sudo numa install (Ubuntu EC2) — systemd service, resolved drop-in, CA trust
  • Post-install message — shows mode = "recursive" suggestion
  • sudo numa uninstall (Ubuntu EC2) — service stopped, DNS restored, CA removed
  • Search domain forwarding — auto-detected .eu-central-1.compute.internal → 172.31.0.2
  • Port 53 conflict — numa exits when port is taken
  • macOS health check — port 53 blocked, service unloaded, DNS not redirected

🤖 Generated with Claude Code

## Summary - **Forward mode as default**: transparent proxy to system DNS — caching, .numa domains, ad blocking, developer overrides. Changes nothing about the network. Recursive and auto modes are explicit opt-in via config. - **Auto upstream mode**: probes up to 3 root servers on startup — recursive if outbound DNS works, Quad9 DoH fallback if blocked. Opt-in via `mode = "auto"`. - **Recursive mode**: trusts user intent, no probe needed. Opt-in via `mode = "recursive"`. - **DNSSEC off by default**: no-op in forward mode, avoids misleading dashboard. Enable with `[dnssec] enabled = true` alongside recursive mode. - **Post-install discovery**: both macOS and Linux print `mode = "recursive"` suggestion after install. - **Linux install fixes**: `DNSStubListener=no` in resolved drop-in, correct service start ordering (DNS config before numa), skip `127.0.0.53` stub in upstream detection. - **macOS install health check**: verifies numa is listening before redirecting DNS — if service fails to start (e.g. port 53 in use), unloads service and aborts instead of breaking DNS. - **Search domain forwarding**: parses resolv.conf search/domain lines and creates conditional forwarding rules to VPC resolver (fixes internal hostname resolution on cloud VMs). - **Unified install/uninstall**: `numa install` does everything (service + DNS + CA trust), `numa uninstall` is the mirror. - **Consistent loopback filtering**: extracted `is_loopback_or_stub()` replacing 4 inconsistent inline checks. - **Hardened resolvectl parsing**: validates IPs to avoid IPv6 fragment extraction. - **`DEFAULT_API_PORT` constant**: extracted from hardcoded magic number. ## Test plan - [x] `make all` passes (fmt, clippy, audit, build, 111 tests) - [x] CI green (Linux clippy + test) - [x] Forward mode default — Docker: `mode: "forward"`, detects system DNS - [x] `mode = "recursive"` — EC2: resolves from root, DNSSEC AD flag set - [x] `mode = "auto"` — EC2: probe succeeded, resolved to recursive - [x] `curl /stats | jq .mode` — returns correct mode in all three configurations - [x] DNS resolution — `dig @127.0.0.1 example.com` returns valid answer - [x] DNSSEC — AD flag set in recursive mode with `enabled = true` - [x] Dashboard — HTTP 200 - [x] DoH fallback — Docker (blocked network): probe failed on 3 hints → Quad9 DoH - [x] `sudo numa install` (Ubuntu EC2) — systemd service, resolved drop-in, CA trust - [x] Post-install message — shows `mode = "recursive"` suggestion - [x] `sudo numa uninstall` (Ubuntu EC2) — service stopped, DNS restored, CA removed - [x] Search domain forwarding — auto-detected `.eu-central-1.compute.internal → 172.31.0.2` - [x] Port 53 conflict — numa exits when port is taken - [x] macOS health check — port 53 blocked, service unloaded, DNS not redirected 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.