From 8cdcc4f3c7bfd31c3c4664787c5794e34aa75546 Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Thu, 4 Apr 2024 09:19:45 +0100 Subject: [PATCH] Move dummy DNS server to integration/internal/network Signed-off-by: Rob Murray --- integration/internal/network/dns.go | 66 +++++++++++++++++++++ integration/networking/resolvconf_test.go | 70 ++--------------------- 2 files changed, 72 insertions(+), 64 deletions(-) create mode 100644 integration/internal/network/dns.go diff --git a/integration/internal/network/dns.go b/integration/internal/network/dns.go new file mode 100644 index 0000000000..ab87b2b8b8 --- /dev/null +++ b/integration/internal/network/dns.go @@ -0,0 +1,66 @@ +package network + +import ( + "net" + "os" + "testing" + + "github.com/miekg/dns" + "gotest.tools/v3/assert" +) + +const DNSRespAddr = "10.11.12.13" + +// WriteTempResolvConf writes a resolv.conf that only contains a single +// nameserver line, with address addr. +// It returns the name of the temp file. The temp file will be deleted +// automatically by a t.Cleanup(). +func WriteTempResolvConf(t *testing.T, addr string) string { + t.Helper() + // Not using t.TempDir() here because in rootless mode, while the temporary + // directory gets mode 0777, it's a subdir of an 0700 directory owned by root. + // So, it's not accessible by the daemon. + f, err := os.CreateTemp("", "resolv.conf") + assert.NilError(t, err) + t.Cleanup(func() { os.Remove(f.Name()) }) + err = f.Chmod(0644) + assert.NilError(t, err) + f.Write([]byte("nameserver " + addr + "\n")) + return f.Name() +} + +// StartDaftDNS starts and returns a really, really daft DNS server that only +// responds to type-A requests, and always with address dnsRespAddr. +// The DNS server will be stopped automatically by a t.Cleanup(). +func StartDaftDNS(t *testing.T, addr string) { + serveDNS := func(w dns.ResponseWriter, query *dns.Msg) { + if query.Question[0].Qtype == dns.TypeA { + resp := &dns.Msg{} + resp.SetReply(query) + answer := &dns.A{ + Hdr: dns.RR_Header{ + Name: query.Question[0].Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 600, + }, + } + answer.A = net.ParseIP(DNSRespAddr) + resp.Answer = append(resp.Answer, answer) + _ = w.WriteMsg(resp) + } + } + + conn, err := net.ListenUDP("udp", &net.UDPAddr{ + IP: net.ParseIP(addr), + Port: 53, + }) + assert.NilError(t, err) + + server := &dns.Server{Handler: dns.HandlerFunc(serveDNS), PacketConn: conn} + go func() { + _ = server.ActivateAndServe() + }() + + t.Cleanup(func() { server.Shutdown() }) +} diff --git a/integration/networking/resolvconf_test.go b/integration/networking/resolvconf_test.go index f776d7bd42..f489c6a7f4 100644 --- a/integration/networking/resolvconf_test.go +++ b/integration/networking/resolvconf_test.go @@ -1,8 +1,6 @@ package networking import ( - "net" - "os" "strings" "testing" @@ -10,29 +8,11 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/testutil/daemon" - "github.com/miekg/dns" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/skip" ) -// writeTempResolvConf writes a resolv.conf that only contains a single -// nameserver line, with address addr. -// It returns the name of the temp file. -func writeTempResolvConf(t *testing.T, addr string) string { - t.Helper() - // Not using t.TempDir() here because in rootless mode, while the temporary - // directory gets mode 0777, it's a subdir of an 0700 directory owned by root. - // So, it's not accessible by the daemon. - f, err := os.CreateTemp("", "resolv.conf") - assert.NilError(t, err) - t.Cleanup(func() { os.Remove(f.Name()) }) - err = f.Chmod(0644) - assert.NilError(t, err) - f.Write([]byte("nameserver " + addr + "\n")) - return f.Name() -} - // Regression test for https://github.com/moby/moby/issues/46968 func TestResolvConfLocalhostIPv6(t *testing.T) { // No "/etc/resolv.conf" on Windows. @@ -40,7 +20,7 @@ func TestResolvConfLocalhostIPv6(t *testing.T) { ctx := setupTest(t) - tmpFileName := writeTempResolvConf(t, "127.0.0.53") + tmpFileName := network.WriteTempResolvConf(t, "127.0.0.53") d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName)) d.StartWithBusybox(ctx, t, "--experimental", "--ip6tables") @@ -81,43 +61,6 @@ options ndots:0 `)) } -const dnsRespAddr = "10.11.12.13" - -// startDaftDNS starts and returns a really, really daft DNS server that only -// responds to type-A requests, and always with address dnsRespAddr. -func startDaftDNS(t *testing.T, addr string) *dns.Server { - serveDNS := func(w dns.ResponseWriter, query *dns.Msg) { - if query.Question[0].Qtype == dns.TypeA { - resp := &dns.Msg{} - resp.SetReply(query) - answer := &dns.A{ - Hdr: dns.RR_Header{ - Name: query.Question[0].Name, - Rrtype: dns.TypeA, - Class: dns.ClassINET, - Ttl: 600, - }, - } - answer.A = net.ParseIP(dnsRespAddr) - resp.Answer = append(resp.Answer, answer) - _ = w.WriteMsg(resp) - } - } - - conn, err := net.ListenUDP("udp", &net.UDPAddr{ - IP: net.ParseIP(addr), - Port: 53, - }) - assert.NilError(t, err) - - server := &dns.Server{Handler: dns.HandlerFunc(serveDNS), PacketConn: conn} - go func() { - _ = server.ActivateAndServe() - }() - - return server -} - // Check that when a container is connected to an internal network, DNS // requests sent to daemon's internal DNS resolver are not forwarded to // an upstream resolver listening on a localhost address. @@ -128,11 +71,10 @@ func TestInternalNetworkDNS(t *testing.T) { ctx := setupTest(t) // Start a DNS server on the loopback interface. - server := startDaftDNS(t, "127.0.0.1") - defer server.Shutdown() + network.StartDaftDNS(t, "127.0.0.1") // Set up a temp resolv.conf pointing at that DNS server, and a daemon using it. - tmpFileName := writeTempResolvConf(t, "127.0.0.1") + tmpFileName := network.WriteTempResolvConf(t, "127.0.0.1") d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName)) d.StartWithBusybox(ctx, t, "--experimental", "--ip6tables") defer d.Stop(t) @@ -160,7 +102,7 @@ func TestInternalNetworkDNS(t *testing.T) { res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) assert.NilError(t, err) assert.Check(t, is.Equal(res.ExitCode, 0)) - assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr)) + assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr)) // Connect the container to the internal network as well. // External DNS should still be used. @@ -169,7 +111,7 @@ func TestInternalNetworkDNS(t *testing.T) { res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) assert.NilError(t, err) assert.Check(t, is.Equal(res.ExitCode, 0)) - assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr)) + assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr)) // Disconnect from the external network. // Expect no access to the external DNS. @@ -187,5 +129,5 @@ func TestInternalNetworkDNS(t *testing.T) { res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) assert.NilError(t, err) assert.Check(t, is.Equal(res.ExitCode, 0)) - assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr)) + assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr)) }