resolvconf_test.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package networking
  2. import (
  3. "context"
  4. "strings"
  5. "testing"
  6. "time"
  7. containertypes "github.com/docker/docker/api/types/container"
  8. networktypes "github.com/docker/docker/api/types/network"
  9. "github.com/docker/docker/integration/internal/container"
  10. "github.com/docker/docker/integration/internal/network"
  11. "github.com/docker/docker/testutil/daemon"
  12. "gotest.tools/v3/assert"
  13. is "gotest.tools/v3/assert/cmp"
  14. "gotest.tools/v3/skip"
  15. )
  16. // Regression test for https://github.com/moby/moby/issues/46968
  17. func TestResolvConfLocalhostIPv6(t *testing.T) {
  18. // No "/etc/resolv.conf" on Windows.
  19. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  20. ctx := setupTest(t)
  21. tmpFileName := network.WriteTempResolvConf(t, "127.0.0.53")
  22. d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName))
  23. d.StartWithBusybox(ctx, t, "--experimental", "--ip6tables")
  24. defer d.Stop(t)
  25. c := d.NewClientT(t)
  26. defer c.Close()
  27. netName := "nnn"
  28. network.CreateNoError(ctx, t, c, netName,
  29. network.WithDriver("bridge"),
  30. network.WithIPv6(),
  31. network.WithIPAM("fd49:b5ef:36d9::/64", "fd49:b5ef:36d9::1"),
  32. )
  33. defer network.RemoveNoError(ctx, t, c, netName)
  34. result := container.RunAttach(ctx, t, c,
  35. container.WithImage("busybox:latest"),
  36. container.WithNetworkMode(netName),
  37. container.WithCmd("cat", "/etc/resolv.conf"),
  38. )
  39. defer c.ContainerRemove(ctx, result.ContainerID, containertypes.RemoveOptions{
  40. Force: true,
  41. })
  42. output := strings.ReplaceAll(result.Stdout.String(), tmpFileName, "RESOLV.CONF")
  43. assert.Check(t, is.Equal(output, `# Generated by Docker Engine.
  44. # This file can be edited; Docker Engine will not make further changes once it
  45. # has been modified.
  46. nameserver 127.0.0.11
  47. options ndots:0
  48. # Based on host file: 'RESOLV.CONF' (internal resolver)
  49. # ExtServers: [host(127.0.0.53)]
  50. # Overrides: []
  51. # Option ndots from: internal
  52. `))
  53. }
  54. // Check that when a container is connected to an internal network, DNS
  55. // requests sent to daemon's internal DNS resolver are not forwarded to
  56. // an upstream resolver listening on a localhost address.
  57. // (Assumes the host does not already have a DNS server on 127.0.0.1.)
  58. func TestInternalNetworkDNS(t *testing.T) {
  59. skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No resolv.conf on Windows")
  60. skip.If(t, testEnv.IsRootless, "Can't use resolver on host in rootless mode")
  61. ctx := setupTest(t)
  62. // Start a DNS server on the loopback interface.
  63. network.StartDaftDNS(t, "127.0.0.1")
  64. // Set up a temp resolv.conf pointing at that DNS server, and a daemon using it.
  65. tmpFileName := network.WriteTempResolvConf(t, "127.0.0.1")
  66. d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName))
  67. d.StartWithBusybox(ctx, t, "--experimental", "--ip6tables")
  68. defer d.Stop(t)
  69. c := d.NewClientT(t)
  70. defer c.Close()
  71. intNetName := "intnet"
  72. network.CreateNoError(ctx, t, c, intNetName,
  73. network.WithDriver("bridge"),
  74. network.WithInternal(),
  75. )
  76. defer network.RemoveNoError(ctx, t, c, intNetName)
  77. extNetName := "extnet"
  78. network.CreateNoError(ctx, t, c, extNetName,
  79. network.WithDriver("bridge"),
  80. )
  81. defer network.RemoveNoError(ctx, t, c, extNetName)
  82. // Create a container, initially with external connectivity.
  83. // Expect the external DNS server to respond to a request from the container.
  84. ctrId := container.Run(ctx, t, c, container.WithNetworkMode(extNetName))
  85. defer c.ContainerRemove(ctx, ctrId, containertypes.RemoveOptions{Force: true})
  86. res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
  87. assert.NilError(t, err)
  88. assert.Check(t, is.Equal(res.ExitCode, 0))
  89. assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr))
  90. // Connect the container to the internal network as well.
  91. // External DNS should still be used.
  92. err = c.NetworkConnect(ctx, intNetName, ctrId, nil)
  93. assert.NilError(t, err)
  94. res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
  95. assert.NilError(t, err)
  96. assert.Check(t, is.Equal(res.ExitCode, 0))
  97. assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr))
  98. // Disconnect from the external network.
  99. // Expect no access to the external DNS.
  100. err = c.NetworkDisconnect(ctx, extNetName, ctrId, true)
  101. assert.NilError(t, err)
  102. res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
  103. assert.NilError(t, err)
  104. assert.Check(t, is.Equal(res.ExitCode, 1))
  105. assert.Check(t, is.Contains(res.Stdout(), "SERVFAIL"))
  106. // Reconnect the external network.
  107. // Check that the external DNS server is used again.
  108. err = c.NetworkConnect(ctx, extNetName, ctrId, nil)
  109. assert.NilError(t, err)
  110. res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
  111. assert.NilError(t, err)
  112. assert.Check(t, is.Equal(res.ExitCode, 0))
  113. assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr))
  114. }
  115. // TestNslookupWindows checks that nslookup gets results from external DNS.
  116. // Regression test for https://github.com/moby/moby/issues/46792
  117. func TestNslookupWindows(t *testing.T) {
  118. skip.If(t, testEnv.DaemonInfo.OSType != "windows")
  119. ctx := setupTest(t)
  120. c := testEnv.APIClient()
  121. attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
  122. defer cancel()
  123. res := container.RunAttach(attachCtx, t, c,
  124. container.WithCmd("nslookup", "docker.com"),
  125. )
  126. defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
  127. assert.Check(t, is.Equal(res.ExitCode, 0))
  128. // Current default is to not-forward requests to external servers, which
  129. // can only be changed in daemon.json using feature flag "windows-dns-proxy".
  130. // So, expect the lookup to fail...
  131. assert.Check(t, is.Contains(res.Stderr.String(), "Server failed"))
  132. // When the default behaviour is changed, nslookup should succeed...
  133. //assert.Check(t, is.Contains(res.Stdout.String(), "Addresses:"))
  134. }
  135. // Check that containers on the default bridge network can use a host's resolver
  136. // running on a loopback interface (via the internal resolver), but the internal
  137. // resolver is not populated with DNS names for containers (no service discovery
  138. // on the legacy/default bridge network).
  139. // (Assumes the host does not already have a DNS server on 127.0.0.1.)
  140. func TestDefaultBridgeDNS(t *testing.T) {
  141. skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No resolv.conf on Windows")
  142. skip.If(t, testEnv.IsRootless, "Can't use resolver on host in rootless mode")
  143. ctx := setupTest(t)
  144. // Start a DNS server on the loopback interface.
  145. server := startDaftDNS(t, "127.0.0.1")
  146. defer server.Shutdown()
  147. // Set up a temp resolv.conf pointing at that DNS server, and a daemon using it.
  148. tmpFileName := writeTempResolvConf(t, "127.0.0.1")
  149. d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName))
  150. d.StartWithBusybox(ctx, t)
  151. defer d.Stop(t)
  152. c := d.NewClientT(t)
  153. defer c.Close()
  154. // Create a container on the default bridge network.
  155. const ctrName = "ctrname"
  156. ctrId := container.Run(ctx, t, c, container.WithName(ctrName))
  157. defer c.ContainerRemove(ctx, ctrId, containertypes.RemoveOptions{Force: true})
  158. // Expect the external DNS server to respond to a request from the container.
  159. res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
  160. assert.NilError(t, err)
  161. assert.Check(t, is.Equal(res.ExitCode, 0))
  162. assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr))
  163. // Expect the external DNS server to respond to a request from the container
  164. // for the container's own name - it won't be recognised as a container name
  165. // because there's no service resolution on the default bridge.
  166. res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", ctrName})
  167. assert.NilError(t, err)
  168. assert.Check(t, is.Equal(res.ExitCode, 0))
  169. assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr))
  170. // Check that inspect output has no DNSNames for the container.
  171. inspect := container.Inspect(ctx, t, c, ctrId)
  172. net, ok := inspect.NetworkSettings.Networks[networktypes.NetworkBridge]
  173. assert.Check(t, ok, "expected to find bridge network in inspect output")
  174. assert.Check(t, is.Nil(net.DNSNames))
  175. }