Fix local-lan-zone.conf: add server: wrapper + correct local-data syntax

Two bugs in the Unbound config file generation:

1. Missing server: wrapper
   OPNsense includes /var/unbound/etc/*.conf at the top level of
   unbound.conf (via include: or include-toplevel:).  Server-level
   directives like local-zone: and local-data: must sit inside a
   server: {} block — without it they are outside any section and
   either silently ignored or rejected by unbound-checkconf.
   forward-zone: is a top-level section so forward_to_ctrld.conf
   correctly has no wrapper.

   Consequence: the original 'local-zone: "lan." static' without a
   server: wrapper was never actually applied, meaning the .lan leak
   prevention was not working.

2. No local-data records
   Even with a correct zone declaration, every .lan name not listed
   as local-data gets NXDOMAIN from the static zone.  The previous
   commit added the local-data records; this commit gives them valid
   syntax inside the server: block.

Generated file now looks like:
  server:
      local-zone: "lan." static

      local-data: "switch.mgmt.lan. A <mgmt_ip>"
      local-data: "management.lan. A <mgmt_ip>"
      local-data: "pbx.lan. A 192.168.50.10"
      ...

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
This commit is contained in:
Claude
2026-03-27 11:06:33 +00:00
parent da1d629e18
commit 91eaeffd9c
+16 -5
View File
@@ -2857,22 +2857,33 @@ def _build_unbound_lan_zone_conf(entries: list, mgmt_ip: str = "192.168.99.50")
"""
Build the full local-lan-zone.conf for Unbound.
OPNsense includes /var/unbound/etc/*.conf at the TOP LEVEL of unbound.conf
(either via include: or include-toplevel:). This means server-level
directives (local-zone:, local-data:) must be wrapped in a server: block.
Without the wrapper they land outside any section and are silently ignored
or cause unbound-checkconf to error. forward-zone: is a top-level section
and needs no wrapper — that's why forward_to_ctrld.conf works without one.
Declares 'lan.' as a static zone (so .lan never leaks to ControlD) and
adds local-data A records for every entry in local-hostnames.json plus
the two built-in management aliases. Without these local-data lines every
.lan name that isn't listed gets NXDOMAIN — including pbx.lan and any
the two built-in management aliases. Without local-data entries every
.lan name not explicitly listed gets NXDOMAIN — including pbx.lan and any
other custom hostname the user defined.
"""
lines = ['local-zone: "lan." static', ""]
lines = [
"server:",
' local-zone: "lan." static',
"",
]
# Management PC aliases — always present
for alias in ("switch.mgmt.lan", "management.lan"):
lines.append(f'local-data: "{alias}. A {mgmt_ip}"')
lines.append(f' local-data: "{alias}. A {mgmt_ip}"')
# User-defined entries from local-hostnames.json
for e in entries:
name = e.get("name", "").strip().rstrip(".")
ip = e.get("ip", "").strip()
if name and ip:
lines.append(f'local-data: "{name}. A {ip}"')
lines.append(f' local-data: "{name}. A {ip}"')
return "\n".join(lines) + "\n"