3

I have a server on my LAN wich I want to access with a public domain name, but it should be reachable only within my local network.

To do that, I added a CNAME record on my domain (example.org) to point myserver.example.org to myserver.fritz.box.

On my host machine this works flawlessly, but inside a docker container running on the host I can't resolve the CNAME.

Resolving on my host:

host# dig myserver.example.org 

; <<>> DiG 9.16.1-Ubuntu <<>> myserver.example.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49974
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;myserver.example.org.      IN  A

;; ANSWER SECTION:
myserver.example.org.   0   IN  CNAME   myserver.fritz.box.
myserver.fritz.box. 9   IN  A   192.168.178.155

;; Query time: 20 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sa Jul 01 16:54:58 CEST 2023
;; MSG SIZE  rcvd: 116

Resolving inside the container. I tried a blank ubuntu and debian base image and in both I only receive a NXDOMAIN response:

container# dig myserver.example.org 

; <<>> DiG 9.18.16 <<>> myserver.example.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 177
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;myserver.example.org.      IN  A

;; ANSWER SECTION:
myserver.example.org.   0   IN  CNAME   myserver.fritz.box.

;; AUTHORITY SECTION:
box.            1823    IN  SOA ns0.centralnic.net. hostmaster.centralnic.net. 1688220651 900 1800 6048000 3600

;; Query time: 4 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Sat Jul 01 14:57:46 UTC 2023
;; MSG SIZE  rcvd: 149

That also leads to me being unable to ping the server from inside the container:

container# ping myserver.example.org
ping: bad address 'myserver.example.org'

Note that manually doing another query for the local domain name works fine also inside the container.

container# dig myserver.fritz.box

; <<>> DiG 9.18.16 <<>> myserver.fritz.box
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2636
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 1, ADDITIONAL: 3

;; QUESTION SECTION:
;myserver.fritz.box.        IN  A

;; ANSWER SECTION:
myserver.fritz.box. 9   IN  A   192.168.178.155

;; AUTHORITY SECTION:
myserver.fritz.box. 9   IN  NS  fritz.box.

;; ADDITIONAL SECTION:
fritz.box.      9   IN  A   192.168.178.1
fritz.box.      9   IN  AAAA    fd00::cece:1eff:fef4:c4db
fritz.box.      9   IN  AAAA    2a01:c22:d145:6900:cece:1eff:fef4:c4db

;; Query time: 252 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Sat Jul 01 14:58:27 UTC 2023
;; MSG SIZE  rcvd: 158

Why do we have this discrepancy?

Shouldn't docker's DNS server 127.0.0.11 simply forward the request to 127.0.0.53 on the host machine, where everything works fine?


Maybe related

I've noticed the same issue also on my host machine, if I'm explicitly using the DNS server of my router, instead of 127.0.0.53 from resolvectl. I don't understand why this is happening either.

host# dig @fritz.box myserver.example.org 

; <<>> DiG 9.16.1-Ubuntu <<>> @fritz.box myserver.example.org
; (3 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 13570
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;myserver.example.org.      IN  A

;; ANSWER SECTION:
myserver.example.org.   0   IN  CNAME   myserver.fritz.box.

;; AUTHORITY SECTION:
box.            1641    IN  SOA ns0.centralnic.net. hostmaster.centralnic.net. 1688220651 900 1800 6048000 3600

;; Query time: 8 msec
;; SERVER: 2a01:c22:d145:6900:cece:1eff:fef4:c4db#53(2a01:c22:d145:6900:cece:1eff:fef4:c4db)
;; WHEN: Sa Jul 01 17:00:48 CEST 2023
;; MSG SIZE  rcvd: 149

Simon Lenz
  • 319
  • 1
  • 3
  • 9
  • What are the time to live values in your zone file? – xenoson Jul 02 '23 at 12:23
  • @xenoson 300 is the TTL for the new records I've created. I just checked (~24 hours) after the changes to the DNS records and the problem is still as described above. – Simon Lenz Jul 02 '23 at 12:39
  • can your docker even resolve the local host normally? or does it have a different DNS that overrides this myserver.fritz.box with something else? – LPChip Jul 02 '23 at 12:41
  • @LPChip, `ping myserver.fritz.box` works fine from inside the docker container. – Simon Lenz Jul 02 '23 at 12:51
  • Can your docker reach the internet though? I mean, at all? – LPChip Jul 02 '23 at 13:16
  • @LPChip, Yes, I can resolve/reach servers on the internet just fine. And I can resolve/reach servers in the LAN just fine (via their local zeroconf domain). The only thing that does not work is reaching a server in the LAN via the public domain. I assume that there is some problem moving from DNS to the local zeroconf network domains. – Simon Lenz Jul 02 '23 at 13:32
  • Does this work on other devices? This could just be a NAT hairpinning issue where the router does not expect incoming traffic when it happens at the exact same time as outgoing traffic and just ignores it. – LPChip Jul 02 '23 at 14:52
  • @LPChip, I can resolve `myserver.example.org` on all devices in my network. But I can't resolve it inside docker containers. – Simon Lenz Jul 02 '23 at 15:22
  • Set up your own *iterative* resolver in your local network and it'll work. This is a good point to learn about the various DNS "servers": authoritative, iterative and recursive. – Daniel B Jul 02 '23 at 21:01

1 Answers1

3

The fritzbox uses fritz.box as a local domain.

If you ask the resolver of your fritzbox you get this answer:

;; AUTHORITY SECTION:
myserver.fritz.box. 9   IN  NS  fritz.box.

Meaning the fritzbox considers itself in authoritiy of the .box domain.

But as we can see from the other resolver answers, if we really talk to a public DNS on the internet we get this response:

;; AUTHORITY SECTION:
box.            1641    IN  SOA ns0.centralnic.net. hostmaster.centralnic.net. 1688220651 900 1800 6048000 3600

That is because .box is a valid top level domain!

Using # dig myserver.example.org asks your local resolver. Usually nowadays the local resolver is systemd-resolved on recent linux. It is a caching resolver, you can check this with resolvectl statistics and the cache can be emptied with resolvectl flush-caches. A cached answer was my fist guess but it is actually works with an empty cache. That means it asks for myserver.fritz.box separately again after it encounters it, otherwise it would not resolve. It gets an answer because it asks the fritzbox for the .box domain.

As can be seen with resolvectl status there is a DNS Domain: fritz.box set. It probably got that setting via DHCP but it can be set manually by editing /etc/systemd/resolved.conf or on the commandline. See man resolvectl and man systemd.network. There is also a setting for docker. Usually that search domains setting means put that domain on blank hostnames that lack a domain part. Probably the systemd-resolved behaves in a special way for its configured search domains.

Using dig @fritz.box myserver.example.org ask the nameserver @fritz.box directly, bypassing the local cache and settings configured for the local OS resolver. Obviously the fritzbox DNS resolver only asks the DNS on the internet and does not resolve its local domains in a second step.

I come to this conclusion because the output of @fritz.box is the same as for example from @1.1.1.1 or any other public DNS server.

As soon as real DNS servers are involved they try to resolve myserver.fritz.box in the public DNS and fail.

As there is no way to configure the DNS name of the fritzbox in the web interface the only two real solutions are:

  • to file a bug report with AVM, they should use a non existing top level domain, .local is there for a reason

  • or use another device for lan DNS that allows configuration of details.

xenoson
  • 502
  • 2
  • 5
  • Thanks for the edits. I'd like to understand where exactly the local systemd resolver gets the correct IP from, and why the fritzbox resolver has no special handling for its own search domain. Still, I accepted your answer. You already helped immensely. – Simon Lenz Jul 02 '23 at 18:23
  • 1
    Curiously `.box` is a valid TLD but there's no real domains in it. This TLD has been going through a consultation process for about 10 years at this time, source https://icannwiki.org/.box So resolving `anything.box` to public DNS fails. I would have guessed someone would have registered "fritz.box" for phishing/antiphishing purposes. – Criggie Jul 02 '23 at 23:48
  • 1
    @SimonLenz This boils down to the question of who is responsible for doing the recursive queries. The nameserver usually is doing the recursions on behalf of the client and the client gets the final result. Obviously `systemd-resolved` acts like a recursive resolver due to the fact that it ask again although it got no result from public DNS. This is kind of a workaround that fixes the situation at hand. Why the fritzbox behaves the way it does and actually breaks the situation it created itself? That question we have to ask AVM. – xenoson Jul 03 '23 at 11:42
  • To conclude the topic here: As a workaround, I'm simply using now `myserver A ` instead of `myserver CNAME myserver.fritz.box`. This works for my case. Still, I'd prefer to use the CNAME approach from above. – Simon Lenz Jul 03 '23 at 17:43
  • @SimonLenz Be aware of [DNS rebinding attacks](https://en.wikipedia.org/wiki/DNS_rebinding). The fritzbox itself has a whitelist for such hostnames with private IPs if you want to access its web gui. It checks the http header but does not filter DNS. A DNS server somewhere else could filter out private IPs, so your initial idea was better. – xenoson Jul 04 '23 at 18:28