Clarification on source-based policy matching in Unbound:Blocklists

Started by OPNenthu, March 03, 2026, 10:23:09 AM

Previous topic - Next topic
Question: if a more specific policy (by source address) matches a query in Unbound, and that query is not blocked by any filter on that policy, does the filtering end there?  Or does it cascade through each of the less specific matching policies until it's either blocked or resolved by the final (i.e. 'default') policy?

I couldn't find the answer in the relevant section of the docs, but it says this:

QuoteThe algorithm selects the most specific subnet when domains overlap across subnet sizes.

... which I might have interpreted as it stops on the first matched policy, going from most to least specific.

I set up a test like so:

You cannot view this attachment.

(Note: the original 'default' policy is disabled and replaced with the 'clients-all' default policy.)

The idea was that local queries from OPNsense itself (source 127.0.0.1 or ::1) would resolve unimpeded so that firewall Host aliases wouldn't break.  All other traffic, including dynamic IPv6 sources for which I can't statically enter into the policy, would go to the one with the block lists.

The localhost matchers both work fine.  Since requests and replies are both local, ::1 works here (unlike in F/W and DNAT rules).

The "issue" is that localhost queries are still falling through to the 'clients-all' policy and getting filtered, so I think it's designed to cascade but am not certain.

(I know, I can solve this with IPv4-only addressing for DNS and specific policies per IPv4 net.  Just messing around to learn.  I also want localhost requests forwarded externally via DoT through Unbound, same as the others.)

If they are meant to cascade, is there a way to make the policies work like pf 'quick' rules, on first match?  Alternatively, can anyone think of a creative hack to make this scheme work as intended and still support dynamic prefixes? :)
N5105 | 8/250GB | 4xi226-V | Community

Quote from: OPNenthu on March 03, 2026, 10:23:09 AMIf they are meant to cascade, is there a way to make the policies work like pf 'quick' rules, on first match?  Alternatively, can anyone think of a creative hack to make this scheme work as intended and still support dynamic prefixes? :)


I am looking for the same info.... It would be great if the Blocklists rules worked like firewall, the first match wins. Also would be great if dynamic prefixes could be used as source lists.

You will need to add a subnet to your clients-all policy.
For example, 192.168.1.0/24 (and your IPv6 subnet on a separate cloned policy) ; Now that list will apply to everything on that subnet and your localhost policies will work to not block from localhost.
If you leave it as blank, it will be evaluated regardless on top of your other policies.  You essentially saying this policy is for EVERYTHING by leaving it blank.

For what it is worth, I think it should work the way you assumed it would, but it does not. :)

Quote from: gspannu on March 03, 2026, 02:26:46 PMIt would be great if the Blocklists rules worked like firewall, the first match wins.
Yeah, if right now it's "first block wins" then an option to switch over to "first source match wins" would be useful in some cases.  Still waiting on confirmation though because the way I read the documentation implies that it should already be matching on the most specific source.  There might just be a bug?

Quote from: falken on March 03, 2026, 03:07:32 PMYou will need to add a subnet to your clients-all policy.
For example, 192.168.1.0/24 (and your IPv6 subnet on a separate cloned policy) ; Now that list will apply to everything on that subnet and your localhost policies will work to not block from localhost.

With static prefixes it would be trivial.

One thing I considered, but it felt a bit contrived, was having a policy for each portion of the client IP space:

127.0.0.1localhost-ipv4
::1localhost-ipv6
192.168.0.0/16192-168-net
172.16.0.0/12172-16-net
10.0.0.0/810-net
fe80::/10ipv6-LL
fc00::/7ipv6-ULA
2000::/3ipv6-GUA

There are others we could add in there like IPv4 autoconf ranges, if desired, but the key with this setup is that there can be no 'default' policy in order for it to work. 
N5105 | 8/250GB | 4xi226-V | Community

The DNS requests from your local LAN should always come from the LL or ULA address, which should be static prefixes.  Does each portion need separate blocks?  2 policies should it otherwise, you can add multiple subnets to each policy, you just can't mix v4 and v6 right now.

Quote from: falken on March 03, 2026, 08:34:34 PMThe DNS requests from your local LAN should always come from the LL or ULA address, which should be static prefixes.
I don't distribute ULAs, but if I did, what stops the clients from using their GUA to reach the DNS at its ULA address via routing?  From what I notice on my network the client's GUA source address is always logged in Unbound, not the LL.

Quote from: falken on March 03, 2026, 08:34:34 PMDoes each portion need separate blocks?  2 policies should it otherwise, you can add multiple subnets to each policy, you just can't mix v4 and v6 right now.
Yes because in addition to not mixing IPv4 and IPv6 within a policy, there is also a rule that source networks should have the same size.  So you can't put for example /16 and /8 networks together in one policy.  It doesn't pass input validation.
N5105 | 8/250GB | 4xi226-V | Community

The documentation indicates "the algorithm selects the most specific subnet when domains overlap across subnet sizes."  This is true, if you have 10.0.0.0/24 and 10.0.0.5 for example, it will only match on 10.0.0.5 and not 10.0.0.0.  The main issue there is no default that says "if nothing applies, use this list".  Leaving it blank applies to all policies.  So 10.0.0.5 and the blank one would both apply, but not 10.0.0.0/24.
I can't find anything official on how it should handle "blank" subnets, but the method right now is it will parse any lists that are blank in addition to any other policy it did match on.  I agree if nothing else, it should be a feature request.

As far as forcing the route though the GUA to get there, you would have needed to add a firewall rule to allow that behavior, otherwise it wouldn't route. DNS is also not a security feature. They can also just type the IP in directly, add it their local hosts file,  or many other various methods

Quote from: falken on March 03, 2026, 09:28:24 PMI can't find anything official on how it should handle "blank" subnets, but the method right now is it will parse any lists that are blank in addition to any other policy it did match on.  I agree if nothing else, it should be a feature request.

Ok, so it doesn't really cascade.  If I understood you, it matches the policy with the most specific source and then, having failed to match a blocklist item there, it falls immediately to the 'default' policy.

QuoteAs far as forcing the route though the GUA to get there, you would have needed to add a firewall rule to allow that behavior, otherwise it wouldn't route. DNS is also not a security feature. They can also just type the IP in directly, add it their local hosts file,  or many other various methods

Right, let me clarify my question.

If I do configure ULAs they would be in addition to GUAs because I don't want to have to use NAT with IPv6.  What I recall is that clients choose GUAs before ULAs and in most cases the ULA address won't ever be used.

If Unbound is listening on a ULA, then does the client automatically choose its ULA as well to reach Unbound?  Or does it still try the GUA route?

I don't think I'd be able to control it from the client side (?), so I would have to assume that any client could try to reach Unbound from any of its configured IPs.  In that case I again have the problem that dynamic prefix GUAs would need to be handled by a default (catch-all) policy in Unbound which would break the 'localhost' policies.

I guess I could use firewall blocks to enforce it, but not sure I like that.  It seems like it would cause delays.
N5105 | 8/250GB | 4xi226-V | Community