feat: Unbound DNS-Resolver — vollständig (Renderer + Handler + UI)

Stub raus, vollständig implementiert:

* Migration 0014: dns_settings (single-row) + dns_zones.forward_to.
  Default-Settings sind sinnvoll für die typische LAN-Resolver-Rolle
  (1.1.1.1 + 9.9.9.9 upstream, localnet allow, DNSSEC + qname-min on).
* internal/services/dns: CRUD-Repo für zones, records, settings.
* internal/handlers/dns.go: REST /api/v1/dns/zones, /records, /settings
  mit Auto-Reload nach jeder Mutation.
* internal/unbound/unbound.cfg.tpl + unbound.go: Renderer schreibt
  /etc/unbound/unbound.conf.d/edgeguard.conf direkt (kein Symlink-
  Dance, weil AppArmor unbound nur /etc/unbound erlaubt). Local-zones
  authoritativ aus dns_records; forward-zones per stub-zone; default-
  forwarders catchen alles sonst.
* main.go: dnsRepo + unbound-Reloader injiziert.
* render.go: unbound.New() bekommt Pool.
* postinst:
  - Conf-Datei /etc/unbound/unbound.conf.d/edgeguard.conf wird als
    edgeguard:edgeguard 0644 angelegt damit Renderer schreiben kann.
  - /etc/edgeguard + Service-Subdirs auf 0755 (Squid + Unbound laufen
    NICHT als edgeguard, brauchen Read-Traversal).
  - Sudoers: systemctl reload unbound.service whitelisted.
* Template: chroot:"" (Conf liegt außerhalb /var/lib/unbound default-
  chroot), DNSSEC-Trust-Anchor NICHT setzen (Distro hat schon
  root-auto-trust-anchor-file.conf — sonst doppelter Anchor → start
  failure).
* Frontend /dns: PageHeader + zwei Tabs (Zones + Resolver-Settings).
  Zones-Tab mit Drawer für Records (CRUD pro Zone, A/AAAA/CNAME/TXT/
  MX/SRV/NS/PTR/CAA). Sidebar-Eintrag unter Network.
* i18n DE/EN für dns.* Block.

Verified end-to-end: render → unbound restart → dig @127.0.0.1
example.com → 104.20.23.154 / 172.66.147.243.

Version 1.0.34 (mehrere Iterationen wegen AppArmor + chroot + perms).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-11 06:24:51 +02:00
parent 72269f5b7c
commit e537d70e04
19 changed files with 1416 additions and 23 deletions

View File

@@ -0,0 +1,79 @@
# Generated by edgeguard — do not edit by hand.
# Re-generate via `edgeguard-ctl render-config --only=unbound`.
server:
verbosity: 1
use-syslog: yes
interface-automatic: no
# Kein chroot — unsere Conf-Datei liegt in /etc/edgeguard/unbound/
# und ist außerhalb des Distro-chroot (/var/lib/unbound) nicht
# erreichbar. Distro-Default chrooted; wir deaktivieren das hier
# explicit. Hardening passiert via systemd-Sandboxing der Unit.
chroot: ""
username: "unbound"
{{- range .ListenAddresses}}
interface: {{.}}@{{$.Settings.ListenPort}}
{{- end}}
port: {{.Settings.ListenPort}}
# Access control — wer darf den Resolver benutzen.
{{- range .AccessACLs}}
access-control: {{.}} allow
{{- end}}
access-control: 0.0.0.0/0 refuse
access-control: ::/0 refuse
# Cache + Resilience
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
cache-min-ttl: {{.Settings.CacheMinTTL}}
cache-max-ttl: {{.Settings.CacheMaxTTL}}
msg-cache-size: 64m
rrset-cache-size: 128m
num-threads: 2
# Hardening
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
harden-referral-path: yes
use-caps-for-id: yes
qname-minimisation: {{if .Settings.QNameMinimisation}}yes{{else}}no{{end}}
minimal-responses: yes
aggressive-nsec: yes
{{/* DNSSEC trust-anchor wird vom distro-snippet
/etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf
gesetzt hier keine eigene Zeile, sonst doppelt. Nur die
val-clean-additional-Option setzen wenn DNSSEC aktiv. */}}
{{- if .Settings.DNSSEC}}
val-clean-additional: yes
{{- end}}
# Local zones from operator (zone_type='local')
{{range .LocalZones}}
local-zone: "{{.Name}}." static
{{- range .Records}}
local-data: "{{.Name}}{{if not (hasSuffix .Name $.dot)}}.{{end}} {{.TTL}} IN {{.RecordType}} {{.Value}}"
{{- end}}
{{end}}
# Forward zones from operator (zone_type='forward')
{{range .ForwardZones}}
forward-zone:
name: "{{.Name}}."
{{- range $f := .Forwarders}}
forward-addr: {{$f}}
{{- end}}
{{end}}
# Default upstream forwarders ("." catches everything not local).
forward-zone:
name: "."
{{- range $f := .Upstreams}}
forward-addr: {{$f}}
{{- end}}