torez233
torez233

Reputation: 441

Question on how DNS works within the Docker container

I’m trying to have a basic understanding of how DNS actually work within the container. I did some research online but find limited info on this subject (e.g. only a very brief description of DNS on their networking overview page https://docs.docker.com/network/#dns-services)

Question 1: is my following understanding correct?

To resolve a particular DNS query (whose domain is a public domain, e.g. dig www.google.com) within the docker container, packets will flow through the following components in order: container local DSN resolver, docker DNS server on the host, host DNS server

In particular:

Question 2: why the local DNS resolver is listening on some non-default ports?

Within the container (container: ubuntu:22.10, host: ubuntu-22.10), when I list the ports currently in use. I got the following result:

$ cat /etc/resolv.conf 
nameserver 127.0.0.11
$ netstat -tulpn 
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.11:35801        0.0.0.0:*               LISTEN      -                   
udp        0      0 127.0.0.11:58113        0.0.0.0:*                           - 

It looks like the DNS resolver is not listening on port 53 but instead on some random ports > 1024.

However, when I capture the DNS query/responses, I notice that the DNS response is sent back from port 53

$ tcpdump -i lo port 53 or port 58113 and udp &
$ dig +short www.google.com
03:08:55.215419 IP localhost.47258 > localhost.58113: UDP, length 55
03:08:55.216852 IP localhost.53 > localhost.47258: 41084 1/0/1 A 142.251.46.228 (59)
03:08:55.307792 IP localhost.50668 > localhost.58113: UDP, length 52
03:08:55.308478 IP localhost.53 > localhost.50668: 17391*$ 1/0/1 PTR localhost. (75)

This looks interesting. For the DNS query, the packets are sent from port 47258 (picked up by dig) to port 58113 (the port DNS resolver is listening to). However, for the response, the packets are sent from port 53 to port 47258. I can understand that this is perfectly allowed but I’m wondering why docker creates this asymmetry? Why can’t the local DNS resolver just listens on the classic port 53 for any query?

I can understand that such setup is perfectly allowed but I’m wondering why docker has to create such asymmetry in the first place? Why can’t the local DNS resolver just listens on the classic port 53 for any query?

I know this is a long question. Really appreciate any help

Upvotes: 6

Views: 2544

Answers (3)

Animesh
Animesh

Reputation: 486

Answer to Q1

Your understanding of how DNS within Docker containers works is mostly correct, but there a few clarifications:

  • Container's local DNS resolver forwards queries to the Docker DNS server on the host. The resolvectl configuration in every docker container is 127.0.0.11:53 as the DNS server. The docker daemon runs its own DNS server that is exposed on all containers. As an example, let's say it is exposed on 127.0.0.11:41552. Now, docker configures iptables SNAT and DNAT rules to map port 53 to the port docker daemon is exposed in.

  • The application first makes a query to the DNS server specified in the resolvectl config. The DNS query made to 127.0.0.11:53 is intercepted by iptables, and the port is changed to reflect the actual DNS server port. Docker daemon gets the query and resolves it if it is container name/id. If not, then it is an external query so it uses the DNS resolver of the host to resolve the query and then sends a reply back to the application.

Answer to Q2

The reason the container's local DNS resolver listens on a non-default port (in your case, 58113) is due to a security measure implemented by Docker. By default, Docker doesn't allow containers to bind to privileged ports (below 1024) unless the container is running in privileged mode or with specific capabilities granted. What you observed (resolver listening on a non-default port but responding from port 53) is a result of the following:

  • The container's DNS resolver listens on a non-privileged port (58113 in your case) for incoming DNS queries from the container. When the resolver needs to send the response back to the client (in this case, the dig command), it uses a source port in the privileged range (53) to mimic the behavior of a regular DNS server.

This is intentional and is not just specific to Docker. Many other containerization platforms like DevPods, DevContainers, GitLab also implement similar security measures to prevent containers from binding to privileged ports without explicit permission.

Upvotes: 1

Shahed
Shahed

Reputation: 1905

Question 1: How DNS Works in Docker Containers

When you ask Docker container to resolve a domain name like www.google.com, here's what happens:

  1. Container's Local DNS Resolver: Think of this as a middleman. It doesn't resolve the DNS query itself but forwards it to Docker's built-in DNS server. This built-in server is accessible at 127.0.0.11.

  2. Docker DNS Server: This server runs on the host machine and handles DNS requests from all containers. It’s smart enough to resolve names of other containers on the same Docker network. For public domains, it forwards the request to the host machine’s DNS server.

  3. Host DNS Server: This is the usual DNS resolver you have on your host system (like what the computer uses to access the internet). It handles queries forwarded by Docker's DNS server for domains like google.com.

Question 2: Why Non-default Ports for Local DNS Resolver?

You noticed that your container's DNS resolver doesn't use the standard port 53. Instead, it uses random high-numbered ports (like 58113).

To:

  • Avoiding Conflicts: Docker avoids port conflicts within the container and between containers. If every service tried to use port 53, there would be clashes.

  • Internal Operations: Docker’s DNS resolver inside the container listens on these high ports to manage DNS queries internally. When you make a DNS request, it hits this high-numbered port.

  • Response Handling: The actual DNS response comes back from port 53, which is the standard DNS port. This might look weird, but it's normal for network operations.

Upvotes: 3

Themoonisacheese
Themoonisacheese

Reputation: 282

According to various online sources i could find:

Question 1:

Yes, your understanding is correct, although the docker daemon may be configured to forward DNS requests to servers that aren't the same as the hosts. additionally, to be perfectly clear, "the hosts DNS server" is not running on the host, typically. It simply refers to the server in the hosts /etc/resolv.conf file.

Question 2:

In docker containers, 127.0.0.11 is the address of the DNS proxy. The proxy apparently listens on a lot of (all?) ports at that adress and will act like a DNS server on all of these ports, probably for performance reasons (I suspect the implementation simply does not care about the port number and sends all packets to that adress directly in the DNS resolver). The setup in which it responds from port 53 is a bit odd, but as you've said yourself, if there is no firewall in between then this is a perfectly valid way of using UDP.
As to why, I suspect that the docker ecosystem embarks an off-the-shelf DNS server solution like bind9 (probably not exactly bind9 but you get the point). These servers are typically extremely well optimized, and such optimizations may include always responding from port 53 to save a few cycles. The docker devs could have changed it, but it works fine and this is far from the oddest thing in the ecosystem.

Upvotes: 0

Related Questions