Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CNAME in local-data: is not resolved #976

Open
vzuevsky opened this issue Dec 7, 2023 · 18 comments
Open

CNAME in local-data: is not resolved #976

vzuevsky opened this issue Dec 7, 2023 · 18 comments
Assignees

Comments

@vzuevsky
Copy link

vzuevsky commented Dec 7, 2023

Describe the bug

server:
  local-zone: "fqdn.com." transparent
  local-data: "bb.fqdn.com.      CNAME vv.my.net."

To reproduce
Steps to reproduce the behavior:

dig bb.fqdn.com @localhost
bb.fqdn.com.	3600	IN	CNAME	vv.my.net.

Expected behavior
A record corresponding to vv.my.net.

System:
Debian 4.19
Version 1.9.0
linked libs: libevent 2.1.8-stable (it uses epoll), OpenSSL 1.1.1n 15 Mar 2022
linked modules: dns64 python subnetcache respip validator iterator

@gthess gthess self-assigned this Dec 13, 2023
@gthess
Copy link
Member

gthess commented Dec 13, 2023

Hi @vzuevsky, as per the documentation CNAME is not supported in local zones (https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-local-zone).

If you would like to serve your own content with CNAME support you could use one of stub, forward or auth zone configuration options. The auth zone documentation specifically mentions the CNAME case (https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#authority-zone-options).

@GamerBene19
Copy link

GamerBene19 commented Dec 30, 2024

Hi @vzuevsky, as per the documentation CNAME is not supported in local zones (https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-local-zone).

If you would like to serve your own content with CNAME support you could use one of stub, forward or auth zone configuration options. The auth zone documentation specifically mentions the CNAME case (https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#authority-zone-options).

@gthess, can you (or anyone else) go into more detail (or link to an example perhaps), as to how one would set that up? I am struggling with defining the right auth-zone files, and googling does not bring up anything useful (anymore).

Say I want muh.foo.de to resolve to (the IP of) foo.bar similar to how pihole (which uses dnsmasq iirc) does it, such that this is the reply for a query to muh.foo.de:

;; ANSWER SECTION:
muh.foo.de.   0	IN	CNAME	foo.bar.
foo.bar.      0	IN	A	192.168.20.30

My understanding is that I would need to have these two records somewhere:

foo.bar. A 192.168.20.30
muh.foo.de. CNAME foo.bar.

Would I need a zonefile for .bar,, .foo.de, or both in this case? What would their contents be? Do I still need local-zone and/or local-data options?

Currently I have this setup:
unbound.conf:

server:
    ...

auth-zone:
    name: "foo.de"
    zonefile: "/opt/unbound/etc/unbound/foo.de.zone"

auth-zone:
    name: "bar"
    zonefile: "/opt/unbound/etc/unbound/bar.zone"

foo.de.zone:

$ORIGIN foo.de.
$TTL 3600
muh CNAME foo.bar.

bar.zone:

$ORIGIN bar.
$TTL 3600
foo A 192.168.20.30

With it, I still only get the CNAME (instead of CNAME and A records) in the reply for muh.foo.de.

I have also tried various combinations of for-downstream: no, for-upstream: no, moving the auth-zone definitions to server and reordering auth-zone definitions, all without the desired result.

Any help is greatly appreciated!

@gthess
Copy link
Member

gthess commented Dec 31, 2024

I am guessing you could be missing:

server:
    ...
    domain-insecure: foo.de.
    domain-insecure: bar.
    ...

in the configuration file. To tell the validator that these domains should be treated as insecure now that you are modifying their contents.
Then for the auth-zones you need to specify for-downstream: no, for-upstream: yes.
Order does not matter.

@GamerBene19
Copy link

GamerBene19 commented Dec 31, 2024

Thanks for the quick reply, really appreciate it! 😄

I have added the two options (with values surrounded by quotes) like so:

server:
    domain-insecure: "foo.de."
    domain-insecure: "bar."

and also added for-downstream: no and for-upstream: yes to both auth-zones, but it did not fix my issue.

The only change I saw (which I also saw yesterday) is, that with for-downstream: no enabled on the auth-zone for foo.de, I do get back the SOA record for foo.de like so:

;; QUESTION SECTION:
;muh.foo.de.			IN	A

;; AUTHORITY SECTION:
foo.de.			415	IN	SOA	ns-1and1.ui-dns.de. dnsadmin.ionos.com. 2016020544 28800 7200 604800 600

instead of the CNAME like so:

;; QUESTION SECTION:
;muh.foo.de.			IN	A

;; ANSWER SECTION:
muh.foo.de.		3600	IN	CNAME	foo.bar.

I have also tried moving the auth-zone options to within server, with similar results. Does it matter where they are defined? If so, where is the "right" place for them?

@gthess
Copy link
Member

gthess commented Dec 31, 2024

It does matter where they are defined because the config file can only be parsed one way.
A minimal configuration which works here is:

server:
        domain-insecure: foo.de.
        domain-insecure: bar.
auth-zone:
	name: foo.de.
	zonefile: foo.de.zone
	for-downstream: no
	for-upstream: yes
auth-zone:
	name: bar.
	zonefile: bar.zone
	for-downstream: no
	for-upstream: yes

What happens when you say it doesn't work? What is Unbound logging for such a query (you can bump up verbosity: 4 for more output)? Do you restart Unbound between configuration changes?

@GamerBene19
Copy link

GamerBene19 commented Dec 31, 2024

Looking at your config, I decided to comment out my forward-zone, which looks (or looked) like this

    forward-zone:
        name: "."
        forward-tls-upstream: "yes"
        forward-addr: "9.9.9.9@853#dns.quad9.net"

With it disabled, i get the intended result (see following). I didn't mention the forward-zone, since I thought it was unrelated -- Sorry for that.

;; QUESTION SECTION:
;muh.foo.de.			IN	A

;; ANSWER SECTION:
muh.foo.de.		3448	IN	CNAME	foo.bar.
foo.bar.		3448	IN	A	192.168.20.30

Do you restart Unbound between configuration changes?

I do. I'm using this docker image and am doing a up/down each time i change the config.

What happens when you say it doesn't work?

(Just to avoid confusion: Now talking about previous results with above mentioned forward zone enabled)
I get one of the two answers I pasted above. Either I only get the CNAME, or a SOA record.

What is Unbound logging for such a query (you can bump up verbosity: 4 for more output)?

(Again, talking about with forward-zone enabled)
The log for the first query reads:

unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: SSL connection ip4 192.168.10.204 port 35063 (len 16)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: Reading ssl tcp query of length 51
unbound         | Dec 31 11:40:55 unbound[1:4] query: 192.168.10.204 muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: start
unbound         | Dec 31 11:40:55 unbound[1:4] info: respip operate: query muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: respip: pass to next module
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: respip module exit state is module_wait_module
unbound         | Dec 31 11:40:55 unbound[1:4] debug: validator[module 1] operate: extstate:module_state_initial event:module_event_pass
unbound         | Dec 31 11:40:55 unbound[1:4] info: validator operate: query muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: validator: pass to next module
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: validator module exit state is module_wait_module
unbound         | Dec 31 11:40:55 unbound[1:4] debug: iterator[module 2] operate: extstate:module_state_initial event:module_event_pass
unbound         | Dec 31 11:40:55 unbound[1:4] debug: process_request: new external request event
unbound         | Dec 31 11:40:55 unbound[1:4] debug: iter_handle processing q with state INIT REQUEST STATE
unbound         | Dec 31 11:40:55 unbound[1:4] info: resolving muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: request has dependency depth of 0
unbound         | Dec 31 11:40:55 unbound[1:4] debug: forwarding request
unbound         | Dec 31 11:40:55 unbound[1:4] debug: iter_handle processing q with state QUERY TARGETS STATE
unbound         | Dec 31 11:40:55 unbound[1:4] info: processQueryTargets: muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: processQueryTargets: targetqueries 0, currentqueries 0 sentcount 0
unbound         | Dec 31 11:40:55 unbound[1:4] info: DelegationPoint<.>: 0 names (0 missing), 1 addrs (0 result, 1 avail) parentNS
unbound         | Dec 31 11:40:55 unbound[1:4] debug:   [dns.quad9.net] ip4 9.9.9.9 port 853 (len 16)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: attempt to get extra 3 targets
unbound         | Dec 31 11:40:55 unbound[1:4] debug: rpz: iterator module callback: have_rpz=1
unbound         | Dec 31 11:40:55 unbound[1:4] debug: servselect ip4 9.9.9.9 port 853 (len 16)
unbound         | Dec 31 11:40:55 unbound[1:4] debug:    rtt=82
unbound         | Dec 31 11:40:55 unbound[1:4] debug: selrtt 82
unbound         | Dec 31 11:40:55 unbound[1:4] info: sending query: muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: sending to target: <.> 9.9.9.9#853
unbound         | Dec 31 11:40:55 unbound[1:4] debug: dnssec status: not expected
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: iterator module exit state is module_wait_reply
unbound         | Dec 31 11:40:55 unbound[1:4] info: mesh_run: end 1 recursion states (1 with reply, 0 detached), 1 waiting replies, 1 recursion replies sent, 0 replies dropped, 0 states jostled out
unbound         | Dec 31 11:40:55 unbound[1:4] info: average recursion processing time 0.094521 sec
unbound         | Dec 31 11:40:55 unbound[1:4] info: histogram of recursion processing times
unbound         | Dec 31 11:40:55 unbound[1:4] info: [25%]=0 median[50%]=0 [75%]=0
unbound         | Dec 31 11:40:55 unbound[1:4] info: lower(secs) upper(secs) recursions
unbound         | Dec 31 11:40:55 unbound[1:4] info:    0.065536    0.131072 1
unbound         | Dec 31 11:40:55 unbound[1:4] info: 0RDd mod2 rep muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: cache memory msg=134069 rrset=137084 infra=11385 val=134996 subnet=0
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 80
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: serviced send timer
unbound         | Dec 31 11:40:55 unbound[1:4] debug: qname perturbed to Muh.foo.de.
unbound         | Dec 31 11:40:55 unbound[1:4] debug: the query is using TLS encryption, for dns.quad9.net
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 81 (-1 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point listen_for_rw 81 0
unbound         | Dec 31 11:40:55 unbound[1:4] debug: peer certificate:
unbound         |  Issuer: C=US, O=DigiCert Inc, CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1
unbound         |  Validity
unbound         |  Not Before: Jul 17 00:00:00 2024 GMT
unbound         |  Not After : Jul 16 23:59:59 2025 GMT
unbound         |  Subject: C=CH, ST=Zurich, L=Zurich, O=Quad9, CN=dns.quad9.net
unbound         |  X509v3 extensions:
unbound         |  X509v3 Authority Key Identifier:
unbound         |  8A:23:EB:9E:6B:D7:F9:37:5D:F9:6D:21:39:76:9A:A1:67:DE:10:A8
unbound         |  X509v3 Subject Key Identifier:
unbound         |  E5:7D:41:EC:49:E0:7A:8B:45:57:6B:21:B3:21:BD:CB:3B:19:4F:6E
unbound         |
unbound         | Dec 31 11:40:55 unbound[1:4] debug: SSL connection to dns.quad9.net authenticated ip4 9.9.9.9 port 853 (len 16)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point listen_for_rw 81 1
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 81
unbound         | Dec 31 11:40:55 unbound[1:4] debug: outnettcp cb
unbound         | Dec 31 11:40:55 unbound[1:4] debug: outnet tcp pkt was written event
unbound         | Dec 31 11:40:55 unbound[1:4] debug: outnet tcp writes done, wait
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 81
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 81 (60000 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: Reading ssl tcp query of length 109
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 81
unbound         | Dec 31 11:40:55 unbound[1:4] debug: outnettcp cb
unbound         | Dec 31 11:40:55 unbound[1:4] debug: measured TCP-time at 23 msec
unbound         | Dec 31 11:40:55 unbound[1:4] debug: svcd callbacks start
unbound         | Dec 31 11:40:55 unbound[1:4] debug: good 0x20-ID in reply qname
unbound         | Dec 31 11:40:55 unbound[1:4] debug: worker svcd callback for qstate 0x5a3aa27337f0
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: start
unbound         | Dec 31 11:40:55 unbound[1:4] debug: iterator[module 2] operate: extstate:module_wait_reply event:module_event_reply
unbound         | Dec 31 11:40:55 unbound[1:4] info: iterator operate: query muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: process_response: new external response event
unbound         | Dec 31 11:40:55 unbound[1:4] info: scrub for . NS IN
unbound         | Dec 31 11:40:55 unbound[1:4] info: response for muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] info: reply from <.> 9.9.9.9#853
unbound         | Dec 31 11:40:55 unbound[1:4] info: incoming scrubbed packet: ;; ->>HEADER<<- opcode: QUERY, rcode: NXDOMAIN, id: 0
unbound         | ;; flags: qr rd ra ; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
unbound         | ;; QUESTION SECTION:
unbound         | muh.foo.de.   IN      A
unbound         |
unbound         | ;; ANSWER SECTION:
unbound         |
unbound         | ;; AUTHORITY SECTION:
unbound         | foo.de.       456     IN      SOA     ns-1and1.ui-dns.de. dnsadmin.ionos.com. 2016020544 28800 7200 604800 600
unbound         |
unbound         | ;; ADDITIONAL SECTION:
unbound         | ;; MSG SIZE  rcvd: 98
unbound         |
unbound         | Dec 31 11:40:55 unbound[1:4] debug: iter_handle processing q with state QUERY RESPONSE STATE
unbound         | Dec 31 11:40:55 unbound[1:4] info: query response was NXDOMAIN ANSWER
unbound         | Dec 31 11:40:55 unbound[1:4] debug: iter_handle processing q with state FINISHED RESPONSE STATE
unbound         | Dec 31 11:40:55 unbound[1:4] info: finishing processing for muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: iterator module exit state is module_finished
unbound         | Dec 31 11:40:55 unbound[1:4] debug: validator[module 1] operate: extstate:module_wait_module event:module_event_moddone
unbound         | Dec 31 11:40:55 unbound[1:4] info: validator operate: query muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: validator: nextmodule returned
unbound         | Dec 31 11:40:55 unbound[1:4] debug: val handle processing q with state VAL_INIT_STATE
unbound         | Dec 31 11:40:55 unbound[1:4] debug: validator classification nameerror
unbound         | Dec 31 11:40:55 unbound[1:4] info: no signer, using muh.foo.de. TYPE0 CLASS0
unbound         | Dec 31 11:40:55 unbound[1:4] debug: val handle processing q with state VAL_FINISHED_STATE
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: validator module exit state is module_finished
unbound         | Dec 31 11:40:55 unbound[1:4] info: respip operate: query muh.foo.de. A IN
unbound         | Dec 31 11:40:55 unbound[1:4] debug: mesh_run: respip module exit state is module_finished
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 80
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: query took 0.023915 sec
unbound         | Dec 31 11:40:55 unbound[1:4] reply: 192.168.10.204 muh.foo.de. A IN NXDOMAIN 0.023915 0 109 on tcp 0.0.0.0 853
unbound         | Dec 31 11:40:55 unbound[1:4] info: mesh_run: end 0 recursion states (0 with reply, 0 detached), 0 waiting replies, 2 recursion replies sent, 0 replies dropped, 0 states jostled out
unbound         | Dec 31 11:40:55 unbound[1:4] info: average recursion processing time 0.059218 sec
unbound         | Dec 31 11:40:55 unbound[1:4] info: histogram of recursion processing times
unbound         | Dec 31 11:40:55 unbound[1:4] info: [25%]=0 median[50%]=0 [75%]=0
unbound         | Dec 31 11:40:55 unbound[1:4] info: lower(secs) upper(secs) recursions
unbound         | Dec 31 11:40:55 unbound[1:4] info:    0.016384    0.032768 1
unbound         | Dec 31 11:40:55 unbound[1:4] info:    0.065536    0.131072 1
unbound         | Dec 31 11:40:55 unbound[1:4] debug: cache memory msg=134337 rrset=137378 infra=11385 val=134996 subnet=0
unbound         | Dec 31 11:40:55 unbound[1:4] debug: svcd callbacks end
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 81
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 81 (60000 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point stop listening 80
unbound         | Dec 31 11:40:55 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:40:55 unbound[1:4] debug: tcp channel read side closed 80
unbound         | Dec 31 11:40:55 unbound[1:4] debug: close fd 80
unbound         | Dec 31 11:41:05 unbound[1:4] debug: close fd 81
unbound         | Dec 31 11:41:05 unbound[1:4] debug: outnettcp cb
unbound         | Dec 31 11:41:05 unbound[1:4] debug: outnettcp got tcp error -1

and for subsequent queries it shows

unbound         | Dec 31 11:47:14 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:47:14 unbound[1:4] debug: SSL connection ip4 192.168.10.204 port 41553 (len 16)
unbound         | Dec 31 11:47:14 unbound[1:4] debug: Reading ssl tcp query of length 51
unbound         | Dec 31 11:47:14 unbound[1:4] query: 192.168.10.204 muh.foo.de. A IN
unbound         | Dec 31 11:47:14 unbound[1:4] reply: 192.168.10.204 muh.foo.de. A IN NXDOMAIN 0.000000 1 109 on tcp 0.0.0.0 853
unbound         | Dec 31 11:47:14 unbound[1:4] debug: comm point stop listening 80
unbound         | Dec 31 11:47:14 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:47:14 unbound[1:4] debug: comm point stop listening 80
unbound         | Dec 31 11:47:14 unbound[1:4] debug: comm point start listening 80 (30000 msec)
unbound         | Dec 31 11:47:14 unbound[1:4] debug: tcp channel read side closed 80
unbound         | Dec 31 11:47:14 unbound[1:4] debug: close fd 80

Note that I have other options defined (e.g. I'm using the respip module together with this blocklist in RPZ format have enabled DoT/DoH and have certain harden options enabled). However, I think the culprit is the forward-zone, as "CNAME resolution" works with it disabled. Let me know if you need the whole config though.

So the new question I have is: How can I combine "CNAME resolution" with the forward-zone? Basically what I want to achieve is this:
0. Incoming query for some domain

  1. Look up some domain
  2. If it is a CNAME that was defined locally, resolve that CNAME and return the A record along with it
  3. Otherwise return the result from the upstream DNS Server (Quad9 in my case)

@gthess
Copy link
Member

gthess commented Jan 2, 2025

I see that this currently does not work if you define a less specific forwarder (in this case '.').
I also do not see why it shouldn't work but I can have a closer look later. From a quick first look it seems like a bug.

@gthess
Copy link
Member

gthess commented Jan 14, 2025

@GamerBene19, #1221 should solve your current problem (configured auths and forwards).

Would you be able to build and test from that branch or would you wait for a released version?

@GamerBene19
Copy link

@GamerBene19, #1221 should solve your current problem (configured auths and forwards).

Thanks for the quick fix!

Would you be able to build and test from that branch or would you wait for a released version?

I'm currently using this docker image, but I'll try to modify it's Dockerfile to build your branch. If that doesn't succeed I'll simply wait for the release to test it.

@GamerBene19
Copy link

Building the image was successful and I can confirm that CNAMEs now get resolved as expected. Thanks again for the quick fix!

So for my use case

  1. If it is a CNAME that was defined locally, resolve that CNAME and return the A record along with it

is fulfilled, but unfortunately it looks like

  1. Otherwise return the result from the upstream DNS Server (Quad9 in my case)

does not work. If I query sth-not-defined-locally.foo.de I get back an SERVFAIL response. I assume that is because by defining these auth-zones I become the authoritative server for the respective domains. Is there a way to achieve that third option too?

@GamerBene19
Copy link

I commented too soon...
I just found fallback-enabled and setting it to yes on the foo.de auth-zone seems to do the job.
However, I think unbound is recursive in the fallback case. The documentation only mentions

Unbound falls back to querying the internet as a resolver for this zone when lookups fail.

and I'm not quite sure what "the internet" means in this case. Can you confirm my suspicion? If it is recursive: Is there a way to have it use the defined forward-zone instead?

Thanks again for the help so far, I really appreciate it!

@gthess
Copy link
Member

gthess commented Jan 17, 2025

If something does not exist in your local foo.de. auth zone you should be getting NXDOMAIN, not SERVFAIL. NXDOMAIN terminates resolution and I think the fallback works because you are getting SERVFAIL.

I suppose you are getting a DNSSEC failure probably, you can check the logs if you raise verbosity to 3 or higher. Is foo.de the actual zone you are using or a nameplace for a public zone? Using the following snippet makes locally served and altered zones work:

server:
    domain-insecure: foo.de.

And btw, if you have configured an auth-zone, Unbound is authoritative for that zone with the contents provided.

@GamerBene19
Copy link

GamerBene19 commented Jan 18, 2025

If something does not exist in your local foo.de. auth zone you should be getting NXDOMAIN, not SERVFAIL.

I thought so too and was confused about that. However, with fallback disabled, I get SERVFAIL for domains that do not exist in my local foo.de zone (including queries for foo.de itself`).

I suppose you are getting a DNSSEC failure probably, you can check the logs if you raise verbosity to 3 or higher.

I have defined dns-insecure for the relevant domains (foo.de and .bar) and the logs (see below) don't point towards DNSSEC (as far as I can tell)

Is foo.de the actual zone you are using or a nameplace for a public zone?

It's a placeholder for my actual domain (which is currently registered to use Cloudflare DNS). However I also test with the foo.de zone (which I do not own) and the contents of zone files pasted above and the behavior between my domain and foo.de is identical.

digging muh.foo.de (defined in zonefile) works as expected:

;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43836
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;muh.foo.de.                    IN      A

;; ANSWER SECTION:
muh.foo.de.             3600    IN      CNAME   foo.bar.
foo.bar.                3600    IN      A       192.168.20.30

where the log (with verbosity 3) show:

unbound         | Jan 18 12:05:27 unbound[1:1] query: 172.16.11.1 muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: respip operate: query muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] debug: validator[module 1] operate: extstate:module_state_initial event:module_event_pass
unbound         | Jan 18 12:05:27 unbound[1:1] info: validator operate: query muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] debug: iterator[module 2] operate: extstate:module_state_initial event:module_event_pass
unbound         | Jan 18 12:05:27 unbound[1:1] info: resolving muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: resolving (init part 2):  muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: resolving (init part 3):  muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: processQueryTargets: muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: query response was CNAME
unbound         | Jan 18 12:05:27 unbound[1:1] info: resolving muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: resolving (init part 2):  muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: resolving (init part 3):  muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: processQueryTargets: muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: query response was ANSWER
unbound         | Jan 18 12:05:27 unbound[1:1] info: finishing processing for muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] debug: validator[module 1] operate: extstate:module_wait_module event:module_event_moddone
unbound         | Jan 18 12:05:27 unbound[1:1] info: validator operate: query muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] info: respip operate: query muh.foo.de. A IN
unbound         | Jan 18 12:05:27 unbound[1:1] reply: 172.16.11.1 muh.foo.de. A IN NOERROR 0.000000 0 76 on tcp 0.0.0.0 853
unbound         | Jan 18 12:05:27 unbound[1:1] debug: cache memory msg=132741 rrset=132670 infra=10816 val=132464 subnet=0

but digging for not-defined.foo.de fails with SERVFAIL:

;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 9647
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;not-defined.foo.de.            IN      A

and the logs show

unbound         | Jan 18 12:08:21 unbound[1:4] query: 172.16.11.1 not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] info: respip operate: query not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] debug: validator[module 1] operate: extstate:module_state_initial event:module_event_pass
unbound         | Jan 18 12:08:21 unbound[1:4] info: validator operate: query not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] debug: iterator[module 2] operate: extstate:module_state_initial event:module_event_pass
unbound         | Jan 18 12:08:21 unbound[1:4] info: resolving not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] info: resolving (init part 2):  not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] info: resolving (init part 3):  not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] info: processQueryTargets: not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] debug: return error response SERVFAIL
unbound         | Jan 18 12:08:21 unbound[1:4] debug: validator[module 1] operate: extstate:module_wait_module event:module_event_moddone
unbound         | Jan 18 12:08:21 unbound[1:4] info: validator operate: query not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] info: respip operate: query not-defined.foo.de. A IN
unbound         | Jan 18 12:08:21 unbound[1:4] error: SERVFAIL <not-defined.foo.de. A IN>: auth zone lookup failed, fallback is off
unbound         | Jan 18 12:08:21 unbound[1:4] reply: 172.16.11.1 not-defined.foo.de. A IN SERVFAIL 0.000000 0 47 on tcp 0.0.0.0 853
unbound         | Jan 18 12:08:21 unbound[1:4] debug: cache memory msg=135789 rrset=140935 infra=11385 val=135544 subnet=0

@gthess
Copy link
Member

gthess commented Jan 20, 2025

Which Unbound version is that? You can check with unbound -V.

@GamerBene19
Copy link

Which Unbound version is that? You can check with unbound -V.

Still the version compiled from your branch. It shows Version 1.22.1

@gthess
Copy link
Member

gthess commented Jan 20, 2025

Maybe you are missing the SOA record for the zone?

@GamerBene19
Copy link

Maybe you are missing the SOA record for the zone?

I did indeed not have one. After a bit of googling how it has to look, I now have one and get NXDOMAIN for domains that are not in my zonefile. Which, as far as my understanding goes, is what an authoritative server should answer.

However, as outlined above, ideally I'd like unbound to (try to) resolve domains that are not defined in locally within it's zonefile.
Basically I want to use unbound as a sort of "override" to achieve something along the lines of

if domain is defined locally:
   return fully resolved CNAME <= this part now works
else
   return response from upstream server <= this part doesn't work
end

I suspect the "else case" is not something that auth-zone is designed for, correct? Is something like that possible with unbound?

If you prefer, we can also close this issue and open a new one for the "else case", since I think the actual issue here is solved.

Again, thanks for all the help so far, really appreciate it!

@gthess
Copy link
Member

gthess commented Jan 24, 2025

The auth zone cannot do what you describe, it is supposed to be the actual zone contents.
local-data can do the "if not local, fallback to resolution" but they cannot do the cname case.
Actually they can but only if the zone is marked as redirect which doesn't help in your case.

Another approach would be to try and (ab)use RPZ for your filtering needs.
You could have a zone with something like the following contents:

$ORIGIN my-rpz.
@ SOA my-rpz. me.my-rpz. 1 2 3 4 5

; local data for a zone, note the missing trailing dot on the qname
foo.bar A 0.0.0.0

; local data for a zone, note the missing trailing dot on the qname
muh.foo.de CNAME foo.bar.
;not-defined.foo.de will escape to the network.

The caveat is that you cannot use the RPZ problematic types as zone data (https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-dns-rpz-00#autoid-9).

Then you can configure it in Unbound like:

server:
    module-config: "respip validator iterator"

rpz:
    name: my-rpz.
    zonefile: <above zonefile>

Let me know if that solves your situation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants