github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+incompatible/integration/networking/resolvconf_test.go (about) 1 package networking 2 3 import ( 4 "net" 5 "os" 6 "strings" 7 "testing" 8 9 containertypes "github.com/docker/docker/api/types/container" 10 "github.com/docker/docker/integration/internal/container" 11 "github.com/docker/docker/integration/internal/network" 12 "github.com/docker/docker/testutil/daemon" 13 "github.com/miekg/dns" 14 "gotest.tools/v3/assert" 15 is "gotest.tools/v3/assert/cmp" 16 "gotest.tools/v3/skip" 17 ) 18 19 // writeTempResolvConf writes a resolv.conf that only contains a single 20 // nameserver line, with address addr. 21 // It returns the name of the temp file. 22 func writeTempResolvConf(t *testing.T, addr string) string { 23 t.Helper() 24 // Not using t.TempDir() here because in rootless mode, while the temporary 25 // directory gets mode 0777, it's a subdir of an 0700 directory owned by root. 26 // So, it's not accessible by the daemon. 27 f, err := os.CreateTemp("", "resolv.conf") 28 assert.NilError(t, err) 29 t.Cleanup(func() { os.Remove(f.Name()) }) 30 err = f.Chmod(0644) 31 assert.NilError(t, err) 32 f.Write([]byte("nameserver " + addr + "\n")) 33 return f.Name() 34 } 35 36 // Regression test for https://github.com/moby/moby/issues/46968 37 func TestResolvConfLocalhostIPv6(t *testing.T) { 38 // No "/etc/resolv.conf" on Windows. 39 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 40 41 ctx := setupTest(t) 42 43 tmpFileName := writeTempResolvConf(t, "127.0.0.53") 44 45 d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName)) 46 d.StartWithBusybox(ctx, t, "--experimental", "--ip6tables") 47 defer d.Stop(t) 48 49 c := d.NewClientT(t) 50 defer c.Close() 51 52 netName := "nnn" 53 network.CreateNoError(ctx, t, c, netName, 54 network.WithDriver("bridge"), 55 network.WithIPv6(), 56 network.WithIPAM("fd49:b5ef:36d9::/64", "fd49:b5ef:36d9::1"), 57 ) 58 defer network.RemoveNoError(ctx, t, c, netName) 59 60 result := container.RunAttach(ctx, t, c, 61 container.WithImage("busybox:latest"), 62 container.WithNetworkMode(netName), 63 container.WithCmd("cat", "/etc/resolv.conf"), 64 ) 65 defer c.ContainerRemove(ctx, result.ContainerID, containertypes.RemoveOptions{ 66 Force: true, 67 }) 68 69 output := strings.ReplaceAll(result.Stdout.String(), tmpFileName, "RESOLV.CONF") 70 assert.Check(t, is.Equal(output, `# Generated by Docker Engine. 71 # This file can be edited; Docker Engine will not make further changes once it 72 # has been modified. 73 74 nameserver 127.0.0.11 75 options ndots:0 76 77 # Based on host file: 'RESOLV.CONF' (internal resolver) 78 # ExtServers: [host(127.0.0.53)] 79 # Overrides: [] 80 # Option ndots from: internal 81 `)) 82 } 83 84 const dnsRespAddr = "10.11.12.13" 85 86 // startDaftDNS starts and returns a really, really daft DNS server that only 87 // responds to type-A requests, and always with address dnsRespAddr. 88 func startDaftDNS(t *testing.T, addr string) *dns.Server { 89 serveDNS := func(w dns.ResponseWriter, query *dns.Msg) { 90 if query.Question[0].Qtype == dns.TypeA { 91 resp := &dns.Msg{} 92 resp.SetReply(query) 93 answer := &dns.A{ 94 Hdr: dns.RR_Header{ 95 Name: query.Question[0].Name, 96 Rrtype: dns.TypeA, 97 Class: dns.ClassINET, 98 Ttl: 600, 99 }, 100 } 101 answer.A = net.ParseIP(dnsRespAddr) 102 resp.Answer = append(resp.Answer, answer) 103 _ = w.WriteMsg(resp) 104 } 105 } 106 107 conn, err := net.ListenUDP("udp", &net.UDPAddr{ 108 IP: net.ParseIP(addr), 109 Port: 53, 110 }) 111 assert.NilError(t, err) 112 113 server := &dns.Server{Handler: dns.HandlerFunc(serveDNS), PacketConn: conn} 114 go func() { 115 _ = server.ActivateAndServe() 116 }() 117 118 return server 119 } 120 121 // Check that when a container is connected to an internal network, DNS 122 // requests sent to daemon's internal DNS resolver are not forwarded to 123 // an upstream resolver listening on a localhost address. 124 // (Assumes the host does not already have a DNS server on 127.0.0.1.) 125 func TestInternalNetworkDNS(t *testing.T) { 126 skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No resolv.conf on Windows") 127 skip.If(t, testEnv.IsRootless, "Can't use resolver on host in rootless mode") 128 ctx := setupTest(t) 129 130 // Start a DNS server on the loopback interface. 131 server := startDaftDNS(t, "127.0.0.1") 132 defer server.Shutdown() 133 134 // Set up a temp resolv.conf pointing at that DNS server, and a daemon using it. 135 tmpFileName := writeTempResolvConf(t, "127.0.0.1") 136 d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName)) 137 d.StartWithBusybox(ctx, t, "--experimental", "--ip6tables") 138 defer d.Stop(t) 139 140 c := d.NewClientT(t) 141 defer c.Close() 142 143 intNetName := "intnet" 144 network.CreateNoError(ctx, t, c, intNetName, 145 network.WithDriver("bridge"), 146 network.WithInternal(), 147 ) 148 defer network.RemoveNoError(ctx, t, c, intNetName) 149 150 extNetName := "extnet" 151 network.CreateNoError(ctx, t, c, extNetName, 152 network.WithDriver("bridge"), 153 ) 154 defer network.RemoveNoError(ctx, t, c, extNetName) 155 156 // Create a container, initially with external connectivity. 157 // Expect the external DNS server to respond to a request from the container. 158 ctrId := container.Run(ctx, t, c, container.WithNetworkMode(extNetName)) 159 defer c.ContainerRemove(ctx, ctrId, containertypes.RemoveOptions{Force: true}) 160 res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) 161 assert.NilError(t, err) 162 assert.Check(t, is.Equal(res.ExitCode, 0)) 163 assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr)) 164 165 // Connect the container to the internal network as well. 166 // External DNS should still be used. 167 err = c.NetworkConnect(ctx, intNetName, ctrId, nil) 168 assert.NilError(t, err) 169 res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) 170 assert.NilError(t, err) 171 assert.Check(t, is.Equal(res.ExitCode, 0)) 172 assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr)) 173 174 // Disconnect from the external network. 175 // Expect no access to the external DNS. 176 err = c.NetworkDisconnect(ctx, extNetName, ctrId, true) 177 assert.NilError(t, err) 178 res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) 179 assert.NilError(t, err) 180 assert.Check(t, is.Equal(res.ExitCode, 1)) 181 assert.Check(t, is.Contains(res.Stdout(), "SERVFAIL")) 182 183 // Reconnect the external network. 184 // Check that the external DNS server is used again. 185 err = c.NetworkConnect(ctx, extNetName, ctrId, nil) 186 assert.NilError(t, err) 187 res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) 188 assert.NilError(t, err) 189 assert.Check(t, is.Equal(res.ExitCode, 0)) 190 assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr)) 191 }