github.com/moby/docker@v26.1.3+incompatible/integration/networking/resolvconf_test.go (about)

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