You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
NetworkOptimizer correctly credits a firewall policy that blocks TCP/443 to the known public DoH providers. On UniFi gateways, however, a domain-based firewall policy is only enforceable while the gateway's dnsmasq has populated its runtime destination ipsets with current A/AAAA answers.
The audit currently validates policy intent, but not whether the associated runtime ipsets are populated. A structurally correct DoH block can therefore be silently ineffective after a gateway reboot, firewall-policy rebuild, or dnsmasq restart until some client happens to resolve each provider hostname through the gateway's resolver.
This is an enforcement-liveness gap, not a request to change the definition of a healthy DNS posture.
Related: #1062 resolves domain-group policy destinations for the audit. This proposal uses those resolved domains to maintain and verify their runtime enforcement state.
Verified live shape
On UniFi OS 5.1.27, the persistent gateway state stores only hostname-to-ipset mappings:
The gateway generates dnsmasq configuration such as:
ipset=/dns.google/<v4 policy set>,<v6 policy set>
The resolved addresses are held only in runtime hash:net child ipsets. They are not persisted in the firewall policy or dnsForwarder.ipsets configuration.
Before resolution, the DoH block rule was compiled and installed but both IPv4 and IPv6 destination sets had zero entries. After querying gateway-local dnsmasq, the same runtime sets populated:
dig @127.0.0.1 cloudflare-dns.com A +short
dig @127.0.0.1 cloudflare-dns.com AAAA +short
dig @127.0.0.1 dns.google A +short
dig @127.0.0.1 dns.google AAAA +short
dig @127.0.0.1 dns.quad9.net A +short
dig @127.0.0.1 dns.quad9.net AAAA +short
dig @127.0.0.1 doh.opendns.com A +short
dig @127.0.0.1 doh.opendns.com AAAA +short
Observed IPv6 entries were also populated for all four providers.
The live child sets have no configured ipset timeout. I have not demonstrated per-entry expiry/removal. However, the DNS answers themselves have short TTLs, including observed values of 26 seconds for cloudflare-dns.com AAAA, 43 seconds for dns.google AAAA, and 46 seconds for doh.opendns.com A. A reconciliation loop is therefore useful both to restore empty sets after lifecycle events and to discover rotated provider addresses.
Why allow rules are different
For a domain-based allow rule, normal client DNS traffic can populate the gateway ipset when clients use its resolver. Sites with a separate LAN resolver can also use conditional forwarding to the gateway for the relevant domains.
For a domain-based block rule, the client is specifically expected not to contact the prohibited service. It may never resolve dns.google, dns.quad9.net, etc. through either path. The destination ipset can remain empty indefinitely, leaving the block rule effectively meaningless without an audit warning.
Proposed behavior
Add an opt-in Domain Firewall Set Reconciliation feature:
Default the feature to domain-backed block rules only. Do not automatically manage allow-rule destinations in the first version.
Resolve each selected hostname's A and AAAA records through gateway-local dnsmasq (127.0.0.1), not through the NetworkOptimizer host resolver or a public DNS service.
Run once after gateway connection/reconnection, on a configurable interval, and through a manual Warm now action.
Rate-limit lookups and preserve normal gateway ownership of dnsmasq and ipsets. Do not write to ipset directly or hand-edit runtime state.
Verify that the associated IPv4 and IPv6 child ipsets become non-empty after resolution.
Persist/display per-domain status: last attempted/successful warm-up, A/AAAA result, current IPv4/IPv6 entry counts, and error detail.
Emit an actionable warning when a policy is structurally valid but its runtime destination set is empty, for example DNS_DOH_BLOCK_RUNTIME_EMPTY.
Acceptance criteria
A configured TCP/443 DoH block covering Cloudflare, Google, Quad9, and OpenDNS is not considered fully healthy when its mapped runtime destination ipsets are empty.
After gateway dnsmasq or firewall-policy lifecycle events, configured block-only domain destinations are repopulated without waiting for client DNS traffic.
Both A and AAAA lookups are performed and the corresponding IPv4/IPv6 sets are verified.
Reconciliation failures are visible in NetworkOptimizer rather than silently leaving the audit green.
The implementation does not mutate UniFi firewall policy, directly modify ipsets, or introduce resolver loops.
Allow-rule reconciliation remains opt-in or out of scope until stale-address handling is deliberately designed.
Summary
NetworkOptimizer correctly credits a firewall policy that blocks TCP/443 to the known public DoH providers. On UniFi gateways, however, a domain-based firewall policy is only enforceable while the gateway's
dnsmasqhas populated its runtime destination ipsets with current A/AAAA answers.The audit currently validates policy intent, but not whether the associated runtime ipsets are populated. A structurally correct DoH block can therefore be silently ineffective after a gateway reboot, firewall-policy rebuild, or
dnsmasqrestart until some client happens to resolve each provider hostname through the gateway's resolver.This is an enforcement-liveness gap, not a request to change the definition of a healthy DNS posture.
Related: #1062 resolves
domain-grouppolicy destinations for the audit. This proposal uses those resolved domains to maintain and verify their runtime enforcement state.Verified live shape
On UniFi OS 5.1.27, the persistent gateway state stores only hostname-to-ipset mappings:
{ "hosts": [ "cloudflare-dns.com", "dns.google", "dns.quad9.net", "doh.opendns.com" ], "ipsets": [ "<policy destination set>" ] }The gateway generates
dnsmasqconfiguration such as:The resolved addresses are held only in runtime
hash:netchild ipsets. They are not persisted in the firewall policy ordnsForwarder.ipsetsconfiguration.Before resolution, the DoH block rule was compiled and installed but both IPv4 and IPv6 destination sets had zero entries. After querying gateway-local
dnsmasq, the same runtime sets populated:Observed IPv4 entries:
Observed IPv6 entries were also populated for all four providers.
The live child sets have no configured ipset timeout. I have not demonstrated per-entry expiry/removal. However, the DNS answers themselves have short TTLs, including observed values of 26 seconds for
cloudflare-dns.comAAAA, 43 seconds fordns.googleAAAA, and 46 seconds fordoh.opendns.comA. A reconciliation loop is therefore useful both to restore empty sets after lifecycle events and to discover rotated provider addresses.Why allow rules are different
For a domain-based allow rule, normal client DNS traffic can populate the gateway ipset when clients use its resolver. Sites with a separate LAN resolver can also use conditional forwarding to the gateway for the relevant domains.
For a domain-based block rule, the client is specifically expected not to contact the prohibited service. It may never resolve
dns.google,dns.quad9.net, etc. through either path. The destination ipset can remain empty indefinitely, leaving the block rule effectively meaningless without an audit warning.Proposed behavior
Add an opt-in Domain Firewall Set Reconciliation feature:
domain-groupobjects after Security Audit does not resolve UniFi domain-group firewall destinations #1062 support lands.dnsmasq(127.0.0.1), not through the NetworkOptimizer host resolver or a public DNS service.Warm nowaction.dnsmasqand ipsets. Do not write toipsetdirectly or hand-edit runtime state.DNS_DOH_BLOCK_RUNTIME_EMPTY.Acceptance criteria
dnsmasqor firewall-policy lifecycle events, configured block-only domain destinations are repopulated without waiting for client DNS traffic.