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:
@@ -14,6 +14,7 @@
|
||||
"vpn": "VPN",
|
||||
"wireguard": "WireGuard",
|
||||
"forwardProxy": "Forward-Proxy",
|
||||
"dns": "DNS",
|
||||
"firewall": "Firewall",
|
||||
"cluster": "Cluster",
|
||||
"settings": "Einstellungen",
|
||||
@@ -399,6 +400,51 @@
|
||||
"wg": "WireGuard"
|
||||
}
|
||||
},
|
||||
"dns": {
|
||||
"title": "DNS (Unbound)",
|
||||
"intro": "Unbound-Resolver auf :53. Lokale Zonen (authoritativ aus DNS-Records) und Forward-Zonen (per stub-zone weiter zu fremden Resolvern). Default-Forwarder für alles andere.",
|
||||
"tabs": { "zones": "Zonen", "settings": "Resolver-Settings" },
|
||||
"zone": {
|
||||
"name": "Zone-Name",
|
||||
"nameExtra": "FQDN ohne führenden/abschließenden Punkt — z.B. internal.netcell-it.de",
|
||||
"type": "Typ",
|
||||
"typeLocal": "local — authoritativ (records hier)",
|
||||
"typeForward": "forward — stub-zone zu fremdem Resolver",
|
||||
"forwardTo": "Upstream-Resolver",
|
||||
"forwardToExtra": "Komma-separierte IP-Liste — z.B. '10.0.0.53, 8.8.8.8'",
|
||||
"description": "Beschreibung",
|
||||
"records": "Records …",
|
||||
"add": "Zone hinzufügen",
|
||||
"edit": "Zone bearbeiten",
|
||||
"deleteConfirm": "Zone {{name}} mit allen Records wirklich löschen?"
|
||||
},
|
||||
"record": {
|
||||
"name": "Name",
|
||||
"nameExtra": "Relativ zur Zone (z.B. 'mailcow') oder FQDN mit abschließendem Punkt.",
|
||||
"type": "Typ",
|
||||
"value": "Wert",
|
||||
"valueExtra": "RDATA in Textform: A → IP, CNAME → FQDN, MX → 'priority host', TXT → 'string'.",
|
||||
"ttl": "TTL (sec)",
|
||||
"drawerTitle": "DNS-Records",
|
||||
"add": "Record hinzufügen",
|
||||
"edit": "Record bearbeiten",
|
||||
"deleteConfirm": "Record {{name}} wirklich löschen?"
|
||||
},
|
||||
"settings": {
|
||||
"intro": "Globale Resolver-Settings. Änderungen hier reloaden Unbound automatisch.",
|
||||
"listenAddresses": "Listen-Adressen",
|
||||
"listenAddressesExtra": "Komma-separiert. Standard 127.0.0.1+::1 — wenn LAN-Clients fragen sollen, z.B. die LAN-Iface-IP zusätzlich (10.10.20.3).",
|
||||
"listenPort": "Port",
|
||||
"upstreamForwards": "Default-Forwarders",
|
||||
"upstreamForwardsExtra": "Wo geht alles hin was nicht lokal ist. Default 1.1.1.1 + 9.9.9.9.",
|
||||
"accessACL": "Access-ACL (CIDRs)",
|
||||
"accessACLExtra": "Wer darf diesen Resolver benutzen.",
|
||||
"dnssec": "DNSSEC validieren",
|
||||
"qnameMin": "QName-Minimisation (privacy)",
|
||||
"cacheMin": "Cache min-TTL",
|
||||
"cacheMax": "Cache max-TTL"
|
||||
}
|
||||
},
|
||||
"fwd": {
|
||||
"title": "Forward-Proxy (Squid)",
|
||||
"intro": "Squid-basierter Forward-Proxy auf :3128. ACLs werden top-down nach Priority ausgewertet — first-match wins. Wenn keine Regel passt, gewinnt der Default: nur localnet (10/8, 172.16/12, 192.168/16) darf raus.",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"vpn": "VPN",
|
||||
"wireguard": "WireGuard",
|
||||
"forwardProxy": "Forward proxy",
|
||||
"dns": "DNS",
|
||||
"firewall": "Firewall",
|
||||
"cluster": "Cluster",
|
||||
"settings": "Settings",
|
||||
@@ -399,6 +400,51 @@
|
||||
"wg": "WireGuard"
|
||||
}
|
||||
},
|
||||
"dns": {
|
||||
"title": "DNS (Unbound)",
|
||||
"intro": "Unbound resolver on :53. Local zones (authoritative from DNS records) and forward zones (stub-zone to remote resolvers). Default forwarders catch everything else.",
|
||||
"tabs": { "zones": "Zones", "settings": "Resolver settings" },
|
||||
"zone": {
|
||||
"name": "Zone name",
|
||||
"nameExtra": "FQDN without leading/trailing dot — e.g. internal.netcell-it.de",
|
||||
"type": "Type",
|
||||
"typeLocal": "local — authoritative (records here)",
|
||||
"typeForward": "forward — stub-zone to remote resolver",
|
||||
"forwardTo": "Upstream resolvers",
|
||||
"forwardToExtra": "Comma-separated IP list — e.g. '10.0.0.53, 8.8.8.8'",
|
||||
"description": "Description",
|
||||
"records": "Records …",
|
||||
"add": "Add zone",
|
||||
"edit": "Edit zone",
|
||||
"deleteConfirm": "Really delete zone {{name}} and all its records?"
|
||||
},
|
||||
"record": {
|
||||
"name": "Name",
|
||||
"nameExtra": "Relative to zone (e.g. 'mailcow') or FQDN with trailing dot.",
|
||||
"type": "Type",
|
||||
"value": "Value",
|
||||
"valueExtra": "RDATA in text form: A → IP, CNAME → FQDN, MX → 'priority host', TXT → 'string'.",
|
||||
"ttl": "TTL (sec)",
|
||||
"drawerTitle": "DNS records",
|
||||
"add": "Add record",
|
||||
"edit": "Edit record",
|
||||
"deleteConfirm": "Really delete record {{name}}?"
|
||||
},
|
||||
"settings": {
|
||||
"intro": "Global resolver settings. Saves reload Unbound automatically.",
|
||||
"listenAddresses": "Listen addresses",
|
||||
"listenAddressesExtra": "Comma-separated. Default 127.0.0.1+::1 — to let LAN clients query, add the LAN iface IP (e.g. 10.10.20.3).",
|
||||
"listenPort": "Port",
|
||||
"upstreamForwards": "Default forwarders",
|
||||
"upstreamForwardsExtra": "Where everything not local goes. Default 1.1.1.1 + 9.9.9.9.",
|
||||
"accessACL": "Access ACL (CIDRs)",
|
||||
"accessACLExtra": "Who is allowed to use this resolver.",
|
||||
"dnssec": "DNSSEC validation",
|
||||
"qnameMin": "QName minimisation (privacy)",
|
||||
"cacheMin": "Cache min-TTL",
|
||||
"cacheMax": "Cache max-TTL"
|
||||
}
|
||||
},
|
||||
"fwd": {
|
||||
"title": "Forward proxy (Squid)",
|
||||
"intro": "Squid-based forward proxy on :3128. ACLs are evaluated top-down by priority — first match wins. If no rule matches, the default permits only localnet (10/8, 172.16/12, 192.168/16).",
|
||||
|
||||
Reference in New Issue
Block a user