Files
moby/integration/networking/resolvconf_test.go
Austin Vazquez e9f28e2a41 client: refactor NetworkConnect, NetworkDisconnect, NetworkRemove
Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-29 13:02:01 +01:00

215 lines
7.2 KiB
Go

package networking
import (
"context"
"net/netip"
"os"
"path"
"strings"
"testing"
"time"
"github.com/moby/moby/api/types/mount"
"github.com/moby/moby/client"
"github.com/moby/moby/v2/integration/internal/container"
"github.com/moby/moby/v2/integration/internal/network"
"github.com/moby/moby/v2/internal/testutil/daemon"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
// Regression test for https://github.com/moby/moby/issues/46968
func TestResolvConfLocalhostIPv6(t *testing.T) {
// No "/etc/resolv.conf" on Windows.
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
ctx := setupTest(t)
d := daemon.New(t, daemon.WithResolvConf(network.GenResolvConf("127.0.0.53")))
d.StartWithBusybox(ctx, t)
defer d.Stop(t)
c := d.NewClientT(t)
defer c.Close()
netName := "nnn"
network.CreateNoError(ctx, t, c, netName,
network.WithDriver("bridge"),
network.WithIPv6(),
network.WithIPAM("fd49:b5ef:36d9::/64", "fd49:b5ef:36d9::1"),
)
defer network.RemoveNoError(ctx, t, c, netName)
result := container.RunAttach(ctx, t, c,
container.WithImage("busybox:latest"),
container.WithNetworkMode(netName),
container.WithCmd("cat", "/etc/resolv.conf"),
)
defer c.ContainerRemove(ctx, result.ContainerID, client.ContainerRemoveOptions{
Force: true,
})
output := strings.ReplaceAll(result.Stdout.String(), d.ResolvConfPathOverride, "RESOLV.CONF")
assert.Check(t, is.Equal(output, `# Generated by Docker Engine.
# This file can be edited; Docker Engine will not make further changes once it
# has been modified.
nameserver 127.0.0.11
options ndots:0
# Based on host file: 'RESOLV.CONF' (internal resolver)
# ExtServers: [host(127.0.0.53)]
# Overrides: []
# Option ndots from: internal
`))
}
// 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.
// (Assumes the host does not already have a DNS server on 127.0.0.1.)
func TestInternalNetworkDNS(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No resolv.conf on Windows")
skip.If(t, testEnv.IsRootless, "Can't use resolver on host in rootless mode")
ctx := setupTest(t)
// Start a DNS server on the loopback interface.
network.StartDaftDNS(t, "127.0.0.1")
// Set up a temp resolv.conf pointing at that DNS server, and a daemon using it.
d := daemon.New(t, daemon.WithResolvConf(network.GenResolvConf("127.0.0.1")))
d.StartWithBusybox(ctx, t)
defer d.Stop(t)
c := d.NewClientT(t)
defer c.Close()
intNetName := "intnet"
network.CreateNoError(ctx, t, c, intNetName,
network.WithDriver("bridge"),
network.WithInternal(),
)
defer network.RemoveNoError(ctx, t, c, intNetName)
extNetName := "extnet"
network.CreateNoError(ctx, t, c, extNetName,
network.WithDriver("bridge"),
)
defer network.RemoveNoError(ctx, t, c, extNetName)
// Create a container, initially with external connectivity.
// Expect the external DNS server to respond to a request from the container.
ctrId := container.Run(ctx, t, c, container.WithNetworkMode(extNetName))
defer c.ContainerRemove(ctx, ctrId, client.ContainerRemoveOptions{Force: true})
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(), network.DNSRespAddr))
// Connect the container to the internal network as well.
// External DNS should still be used.
_, err = c.NetworkConnect(ctx, intNetName, client.NetworkConnectOptions{
Container: ctrId,
})
assert.NilError(t, err)
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(), network.DNSRespAddr))
// Disconnect from the external network.
// Expect no access to the external DNS.
_, err = c.NetworkDisconnect(ctx, extNetName, client.NetworkDisconnectOptions{Container: ctrId, Force: true})
assert.NilError(t, err)
res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
assert.NilError(t, err)
assert.Check(t, is.Equal(res.ExitCode, 1))
assert.Check(t, is.Contains(res.Stdout(), "SERVFAIL"))
// Reconnect the external network.
// Check that the external DNS server is used again.
_, err = c.NetworkConnect(ctx, extNetName, client.NetworkConnectOptions{
Container: ctrId,
})
assert.NilError(t, err)
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(), network.DNSRespAddr))
}
// Check that '--dns' can be used to name a server inside a '--internal' network.
// Regression test for https://github.com/moby/moby/issues/47822
func TestInternalNetworkLocalDNS(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No internal networks on Windows")
skip.If(t, testEnv.IsRootless, "Can't write an accessible dnsd.conf in rootless mode")
ctx := setupTest(t)
d := daemon.New(t)
d.StartWithBusybox(ctx, t)
defer d.Stop(t)
c := d.NewClientT(t)
defer c.Close()
intNetName := "intnet"
network.CreateNoError(ctx, t, c, intNetName,
network.WithDriver("bridge"),
network.WithInternal(),
)
defer network.RemoveNoError(ctx, t, c, intNetName)
// Write a config file for busybox's dnsd.
td := t.TempDir()
fname := path.Join(td, "dnsd.conf")
err := os.WriteFile(fname, []byte("foo.example 192.0.2.42\n"), 0o644)
assert.NilError(t, err)
// Start a DNS server on the internal network.
serverId := container.Run(ctx, t, c,
container.WithNetworkMode(intNetName),
container.WithMount(mount.Mount{
Type: mount.TypeBind,
Source: fname,
Target: "/etc/dnsd.conf",
}),
container.WithCmd("dnsd"),
)
defer c.ContainerRemove(ctx, serverId, client.ContainerRemoveOptions{Force: true})
// Get the DNS server's address.
inspect := container.Inspect(ctx, t, c, serverId)
serverIP := inspect.NetworkSettings.Networks[intNetName].IPAddress
// Query the internal network's DNS server (via the daemon's internal DNS server).
res := container.RunAttach(ctx, t, c,
container.WithNetworkMode(intNetName),
container.WithDNS([]netip.Addr{serverIP}),
container.WithCmd("nslookup", "-type=A", "foo.example"),
)
defer c.ContainerRemove(ctx, res.ContainerID, client.ContainerRemoveOptions{Force: true})
assert.Check(t, is.Contains(res.Stdout.String(), "192.0.2.42"))
}
// TestNslookupWindows checks that nslookup gets results from external DNS.
// Regression test for https://github.com/moby/moby/issues/46792
func TestNslookupWindows(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType != "windows")
ctx := setupTest(t)
c := testEnv.APIClient()
attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
res := container.RunAttach(attachCtx, t, c,
container.WithCmd("nslookup", "docker.com"),
)
defer c.ContainerRemove(ctx, res.ContainerID, client.ContainerRemoveOptions{Force: true})
assert.Check(t, is.Equal(res.ExitCode, 0))
// Current default is to forward requests to external servers, which
// can only be changed in daemon.json using feature flag "windows-dns-proxy".
assert.Check(t, is.Contains(res.Stdout.String(), "Addresses:"))
}