docker / for-mac

Bug reports for Docker Desktop for Mac
https://www.docker.com/products/docker#/mac
2.44k stars 118 forks source link

Understand DNS behavior change to large UDP packets in version 3.6 #6008

Closed aharelick closed 2 years ago

aharelick commented 3 years ago

Apologies for not filling out the template, this is more of a request for information and I'm not exactly sure what the proper expected behavior should be. The change we've noticed is that in version 3.6 and later of Docker for Mac we are now unable to resolve certain domain names. We're using ruby 2.3 and the resolv library library running in an alpine container (ruby:2.3-alpine3.7). However, we can reproduce the issue with debian as well.

The issue only happens when the DNS responses are large. We assume that this has something to do with EDNS support, but there are a ton of issues/tickets floating around on that topic so it'd be helpful to get some more information on what changes were made to help us understand if we should make a bug report here or in the ruby project or if this is just expected behavior. Specifically, this line in the release notes caught our eye:

Fixed a bug where the DNS server would fail after receiving an unexpectedly large datagram.

The actual problem we're encountering is that the Ruby resolv library doesn't support EDNS so it can't handle UDP packet responses that are larger than 512 bytes. It seems like the behavior before Docker for mac 3.6 was that something (possibly the docker dns proxy) would recognize that the response was larger than 512 bytes and would truncate it. Then the DNS request was re-made over TCP. Here is a TCP dump of the DNS request sent in Docker for Mac 3.5.2.

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:00:32.154815 IP 857f49fea0e0.8359 > 192.168.65.5.53: 25651+ SRV? big-dns-result.service.consul. (47)
17:00:32.155170 IP 857f49fea0e0.45413 > 192.168.65.5.53: 16002+ PTR? 5.65.168.192.in-addr.arpa. (43)
17:00:32.285002 IP 192.168.65.5.53 > 857f49fea0e0.45413: 16002 NXDomain 0/0/0 (43)
17:00:32.380087 IP 192.168.65.5.53 > 857f49fea0e0.8359: 25651| 6/0/0 SRV big-dns-result-g9d5l.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-jz72t.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-x2kzx.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-jmx5q.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-ncjf2.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-8bwm2.node.legacy-zone.consul.:31216 1 1 (497)
17:00:32.380678 IP 857f49fea0e0.40532 > 192.168.65.5.53: Flags [S], seq 3141603114, win 64240, options [mss 1460,sackOK,TS val 1160499266 ecr 0,nop,wscale 7], length 0
17:00:32.380749 IP 192.168.65.5.53 > 857f49fea0e0.40532: Flags [S.], seq 4261797615, ack 3141603115, win 65160, options [mss 1460,sackOK,TS val 3995104522 ecr 1160499266,nop,wscale 7], length 0
17:00:32.380762 IP 857f49fea0e0.40532 > 192.168.65.5.53: Flags [.], ack 1, win 502, options [nop,nop,TS val 1160499267 ecr 3995104522], length 0
17:00:32.381174 IP 857f49fea0e0.40532 > 192.168.65.5.53: Flags [P.], seq 1:50, ack 1, win 502, options [nop,nop,TS val 1160499267 ecr 3995104522], length 4927667+ SRV? big-dns-result.service.consul. (47)
17:00:32.381243 IP 192.168.65.5.53 > 857f49fea0e0.40532: Flags [.], ack 50, win 509, options [nop,nop,TS val 3995104523 ecr 1160499267], length 0
17:00:32.384270 IP 192.168.65.5.53 > 857f49fea0e0.40532: Flags [P.], seq 1:725, ack 50, win 509, options [nop,nop,TS val 3995104526 ecr 1160499267], length 72427667 9/0/0 SRV big-dns-result-g9d5l.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-jz72t.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-x2kzx.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-jmx5q.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-ncjf2.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-8bwm2.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-xlnbj.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-2tsn6.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-nlncq.node.legacy-zone.consul.:31216 1 1 (722)
17:00:32.384316 IP 857f49fea0e0.40532 > 192.168.65.5.53: Flags [.], ack 725, win 501, options [nop,nop,TS val 1160499270 ecr 3995104526], length 0
17:00:32.385269 IP 857f49fea0e0.40532 > 192.168.65.5.53: Flags [F.], seq 50, ack 725, win 501, options [nop,nop,TS val 1160499271 ecr 3995104526], length 0
17:00:32.386062 IP 192.168.65.5.53 > 857f49fea0e0.40532: Flags [F.], seq 725, ack 51, win 509, options [nop,nop,TS val 3995104528 ecr 1160499271], length 0
17:00:32.386076 IP 857f49fea0e0.40532 > 192.168.65.5.53: Flags [.], ack 726, win 501, options [nop,nop,TS val 1160499272 ecr 3995104528], length 0

It seems that with version 3.6, packets larger than 512 bytes are being allowed and since the resolv library doesn't support EDNS, it's having trouble parsing them. Here's an example of the tcp dump from version 3.6, in this case the packet isn't truncated so the resolver doesn't fallback to TCP:

17:31:45.955789 IP 712d5ebcb40f.48089 > 192.168.65.5.53: 11185+ SRV? big-dns-result.service.consul. (47)
17:31:46.124114 IP 192.168.65.5.53 > 712d5ebcb40f.48089: 11185 9/0/0 SRV big-dns-result-nlncq.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-jz72t.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-ncjf2.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-8bwm2.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-jmx5q.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-x2kzx.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-xlnbj.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-g9d5l.node.legacy-zone.consul.:31216 1 1, SRV big-dns-result-2tsn6.node.legacy-zone.consul.:31216 1 1 (722)

I guess the expected behavior would be that if the dns requester (in this case the ruby library) doesn't support EDNS, then the docker for mac proxy would continue to truncate at 512 bytes, but it's very possible I could be misunderstanding the specifics of EDNS or how the proxy fits into this.

Also, as another note, this problem doesn't happen if we explicitly specify our DNS server in our ruby code instead of using the 192.168.65.5 dns proxy (that then forwards to our DNS server). That is what made us fairly certain it was some interaction with the Docker for mac dns setup. When we specify our DNS server the response is truncated and the ruby library re-requests over TCP similar to the behavior in 3.5.2.

joe0BAB commented 3 years ago

Could you please verify if the problem still persists with the latest version of Docker Desktop?

stephen-turner commented 3 years ago

This might be related to docker/for-win#12018, although we have not yet managed to reproduce that one.

aharelick commented 3 years ago

@joe0BAB, yup I can confirm that I'm seeing the same issue on version 4.1.1.

@stephen-turner, that issue does seem very similar to what I described. Let me see if I can put together a reproduction.

aharelick commented 3 years ago

Alright, I believe I was able to reproduce this with a public domain. I own some random domain freespaceheaters.com so I put a super long TXT record on it that everyone should be able to query and get the same result from.

 ~ dig freespaceheaters.com TXT

; <<>> DiG 9.16.4 <<>> freespaceheaters.com TXT
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32270
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;freespaceheaters.com.      IN  TXT

;; ANSWER SECTION:
freespaceheaters.com.   255 IN  TXT "superlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesup" "erlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperl" "ongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvaluesuperlongtextvalue"
freespaceheaters.com.   1755    IN  TXT "v=spf1 include:spf.efwd.registrar-servers.com ~all"

;; Query time: 7 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Thu Oct 14 12:36:14 PDT 2021
;; MSG SIZE  rcvd: 811

The response is well over 512 bytes. The exact container setup that we're using is ruby:2.3-alpine3.7. So, you can do:

$ docker run --rm -it ruby:2.3-alpine3.7 sh

Then in the shell run irb to get the interactive ruby console.

Then you can run:

require 'resolv' # to load the dns library

# this command should give you the proper output with "superlongtextvaluesuper....", because it specifies the cloudflare dns server and goes around the docker proxy (feel free to replace 1.1.1.1 with 8.8.8.8 or your public DNS resolver of choice)
Resolv::DNS.new(:nameserver => ['1.1.1.1']).getresources "freespaceheaters.com", Resolv::DNS::Resource::IN::TXT

# this command should give you an error as it uses the proxy. I don't think the exception is meaningful, it's just a byproduct of it not being able to parse the response.
Resolv::DNS.new.getresources "freespaceheaters.com", Resolv::DNS::Resource::IN::TXT

Let me know if any of that isn't working as expected.

stephen-turner commented 3 years ago

Thanks for that, @aharelick. I can reproduce your result.

I'm now not convinced this is the same bug as docker/for-win#12018 because I can ping or nslookup freespaceheaters.com from the shell of an alpine container. My current belief is that your bug was probably caused by the same change on our end, but is a different bug, specific to Ruby.

aharelick commented 3 years ago

That sounds right to me, probably a specific bug on the docker side that's having varying impacts on different DNS resolvers depending on what they support and how they handle large packets. Is it fair to consider this a docker bug at this point or is there anything else I can provide?

stephen-turner commented 3 years ago

We have enough to investigate, thank you.

docker-robott commented 2 years ago

Issues go stale after 90 days of inactivity. Mark the issue as fresh with /remove-lifecycle stale comment. Stale issues will be closed after an additional 30 days of inactivity.

Prevent issues from auto-closing with an /lifecycle frozen comment.

If this issue is safe to close now please do so.

Send feedback to Docker Community Slack channels #docker-for-mac or #docker-for-windows. /lifecycle stale

docker-robott commented 2 years ago

Closed issues are locked after 30 days of inactivity. This helps our team focus on active issues.

If you have found a problem that seems similar to this, please open a new issue.

Send feedback to Docker Community Slack channels #docker-for-mac or #docker-for-windows. /lifecycle locked