Open bedge opened 2 years ago
Unbound does not inspect the local zone data for recursively looked up CNAME targets. If you want that to be changed, there are some options. The respip module can trigger on the response IP address and it also works for recursively looked up names after a CNAME, and that could be something if you want to trigger that by IP address instead of by name. By name is also possible, the RPZ zones are inspected also for recursively looked up content. In the man page 'Response Policy Zone Options', there it also explains the syntax by which the specific name can get an override, to another IP address, or to block it. Then load the respip module in module-config and the rpz zone with that configuration.
Thanks @wcawijngaards, the pointers are much appreciated. So is your patience with the uneducated (me). Reading the docs I feel I'm looking for a much simpler case than for what rpz is targeting.
Here's the config I added for rpz:
module-config: "respip validator iterator". <-- I just added respip
rpz:
name: "rpz.example.com" <-- I'm assuming I need a prefix on `example.com`?
zonefile: /var/unbound/rpz.example.com
primary: 10.1.1.1 <-- this is the host running unbound.
rpz-log: yes
rpz-log-name: rpz.example.com
I'm having trouble constructing the minimum zonefile for rpz, eg: rpz.example.com
.
Are there any examples for this use case? ie: falling back to a local lookup for when a DNS query results in a CNAME?
Ideally, I want unbound to lookup the returned CNAME locally if the returned CNAME matches *.sample.com
, but if that's not viable, I could construct a zone file with a set of A records to provide the IPs. Just looking for some pointers on the format.
I already have local-data
statements in my unbound cfg, which are able to resolve the CNAMEs coming back from the stub-zone, eg:
# Static host entries
include: /var/unbound/host_entries.conf
and, in /var/unbound/host_entries.conf
:
local-data-ptr: "10.1.1.100 host.sample.com"
local-data: "host.sample.com. A 10.1.1.100"
Can I reference these, or must they be defined anew in the rpz zonefile?
The rpz file only needs a line like host.sample.com A 10.1.1.100
for the A records. For the PTR records, you could put in the PTR records for the reverse, in reverse notation. Note that it is without a trailing dot '.' after the sample.com so it gets extended by the rpz zone name.
Backing up a level of abstraction: the ideal solution would be one that causes Unbound to first check the local zone, then the stub zone, for both initial lookups and any recursion. Is there a way to write (and perhaps an example of) and RPZ zonefile that would deliver that behavior generically for a given domain and any children, rather than having to programmatically generate a zonefile explicitly specifying each host?
I think the RPZ syntax may support the wildcard syntax, *.sample.com A 192.0.2.1
RPZ is also applied, I believe for incoming traffic, not just when recursing, so the local zone is not needed, it checks the RPZ zone.
We'll test the wildcard syntax and report back.
But I think we have to keep the local zone, as that's where pfSense inserts the host entries for the DHCP leases; the RPZ is just the mechanism through which we can have CNAMEs from an authoritative server recurse to that local zone.
I think the RPZ syntax may support the wildcard syntax,
*.sample.com A 192.0.2.1
While I am after a solution that recurses the lookup locally when a *.sample.com
CNAME is returned, I can't specify an A record as it will differ based on the CNAME returned.
eg:
There's host1.sample.com
and host1.sample.com
that are known locally, and both have CNAMEs in the stub zone, eg:
alias1.sample.com
= host1.sample.com
alias2.sample.com
= host2.sample.com
So, a wildcard QNAME can't specify a single A record, but rather needs to indicate that the unbound resolve the returned CNAME locally.
The path being something like: (forgive my terminology assassination)
request: alias1.sample.com
-> stub zone resolves alias1.sample.com
-> returns CNAME: host1.sample.com
-> unbound recurses with a local lookup because the stub zone response matches a QNAME trigger in the rpz: *.sample.com.sample.com
-> some as yet undiscovered action that looks up the returned CNAME, eg:
-> host1.sample.com
-> 10.1.1.100
No, it is not that the RPZ causes it to perform local zone lookup. With the RPZ it can do an RPZ lookup. The A records can be specified in the RPZ zone. For the individual hosts.
OK, gotcha. Then this is not the appropriate solution, unless I figure something else out to dump the internal resolver state to a zone file. Which may not be the worst thing, but that feels like a band-aid fix.
Is there some other mechanism that does perform a local lookup of a CNAME returned from a stub zone?
If you do not like this. Then I do not know. You could maybe write a module in code, with dynlib, or in python, with pythonmod, and load that and make it look into the local zone information for such CNAMEs.
Or run two unbound instances, and one sends to another. That increases the configuration flexibility.
Understood. I can script the creation of an RPZ zone file containing all my local CNAMEs resolved to A records. Just feels like this should not be necessary given unbound's "recursive" claims.
In pseudocode:
If returned lookup is a CNAME AND domain == sample.com. {
lookup the returned CNAME
}
IOW: Why must I "hand feed" unbound with an RPZ zone file containing the CNAMES it already knows, given the "recursive" claim?
Honestly, feels like we're still missing something and I'm too much of a noob to know what it is.
Regardless, appreciate all the suggestions and engagement, thanks.
But you say the CNAME can be retrieved from the stub upstream? If so, it need not be in the RPZ file. But the A record that you want to have it resolve it, that is not the A record that would otherwise get loaded, that A record is then what is put in the RPZ file. And then unbound uses that instead of the information it would otherwise retrieve.
I get that it's not the simplest config. Apologies if I'm exacerbating the problem with poor descriptions.
But you say the CNAME can be retrieved from the stub upstream? If so, it need not be in the RPZ file.
No, the stub zone dereferences one CNAME into another CNAME and still returns a CNAME, that the localhost can resolve: (from https://github.com/NLnetLabs/unbound/issues/747#issuecomment-1240116373) The stub zone does this:
alias1.sample.com = host1.sample.com
alias2.sample.com = host2.sample.com
The localhost running unbound has this:
local-data-ptr: "10.1.1.100 host1.sample.com"
local-data: "host1.sample.com. A 10.1.1.100"
in host_entries.conf
, as populated by the DHCP server reserved MAC mappings. This is why the A records cannot exist in the stub zone DNS server.
So the unbound host is capable of resolving host1.sample.com
to an IP already, but it doesn't.
Given unbound gets a CNAME that it can resolve locally back from the stub zone, it should then map this to an A record. Rather it just returns the CNAME without attempting to resolve via the DHCP IP mappings.
Maybe a better way to phrase this is - How do I get unbound to consult the DHCP server mapped MAC->IP reservations?
So it turns out we all overlooked a longer-standing issue from #132, and this is actually a deliberate "feature" of Unbound. The resolver has a baked-in check for CNAMEs referencing the local zone, and stops chaining lookups at the last CNAME in the original lookup zone. Ultimately, the cross-domain CNAMEing we've been trying to do appears to be impossible with the current version of Unbound.
my unbound version is 1.16.0
# unbound -V
Version 1.16.0
Configure line: --build=x86_64-redhat-linux-gnu --host=x86_64-redhat-linux-gnu --program-prefix= --disable-dependency-tracking --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include --libdir=/usr/lib64 --libexecdir=/usr/libexec --localstatedir=/var --sharedstatedir=/var/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-conf-file=/var/unbound/unbound.conf --disable-rpath
Linked libs: mini-event internal (it uses select), OpenSSL 1.0.2k-fips 26 Jan 2017
Linked modules: dns64 respip validator iterator
BSD licensed, see LICENSE in source package for details.
Report bugs to unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues
Here is my configuration:
unbound.conf
server:
module-config: "respip validator iterator"
.... old lines...
rpz:
name: rpz.localhost
zonefile: /etc/unbound/rpz.localhost
... old lines...
$ORIGIN rpz.localhost.
www.google.com IN CNAME www.facebook.com.
3. test result
www.google.com is an alias for www.facebook.com. www.facebook.com has address 128.242.245.180
www.facebook.com has address 128.242.245.180
www.oracle.com is an alias for ds-www.oracle.com.edgekey.net. ds-www.oracle.com.edgekey.net is an alias for e2581.dscx.akamaiedge.net. e2581.dscx.akamaiedge.net has address 23.43.230.57
We can see www.google.com is overrided to facebook, other domain names are not impacted.
Post here as a reference, not sure if it can help others.
Unbound does not chain lookups for CNAMEs from configured stub or forwarding zones.
Example: Local zone: example.com; contains numerous A records injected by DHCP server.
Stub zone in
unbound.conf
referencing hosts that exist in the local zone. ie:
ad_domain_controller.example.com
contains:Observed behavior: A DNS lookup against Unbound for
alias.example.com
returns the CNAME (retrieved from stub server) tohost.example.com
but does not return an A record or IP address (even though that record exists in the local zone) unless that A record exists in the stub DNS server.Expected behavior: Unbound first checks local zone and cache, then queries stub zone if no match found. Unbound receives CNAME from stub server, then initiates a new lookup starting with the local zone, returning the local-zone A record. Final result of the lookup should be the IP addr
10.1.1.100
rather than the CNAMEalias.example.com
Context: running Unbound 1.13.2 on pfSense. Reviewed release notes through current version and none seem relevant to this issue. I have an AD domain controller (which is an authoritative DNS server) which I'm using to host some CNAME records in my local domain. I don't want to use the AD DHCP server, or its DNS server exclusively, because I run pfBlocker and want its logs to show me which host made each blocked request.