package networking import ( "context" "testing" "time" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/testutil" "github.com/docker/docker/testutil/daemon" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/skip" ) // Check that the '/etc/hosts' file in a container is created according to // whether the container supports IPv6. // Regression test for https://github.com/moby/moby/issues/35954 func TestEtcHostsIpv6(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType == "windows") ctx := setupTest(t) d := daemon.New(t) d.StartWithBusybox(ctx, t, "--ipv6", "--ip6tables", "--experimental", "--fixed-cidr-v6=fdc8:ffe2:d8d7:1234::/64") defer d.Stop(t) c := d.NewClientT(t) defer c.Close() testcases := []struct { name string sysctls map[string]string expIPv6Enabled bool expEtcHosts string }{ { // Create a container with no overrides, on the IPv6-enabled default bridge. // Expect the container to have a working '::1' address, on the assumption // the test host's kernel supports IPv6 - and for its '/etc/hosts' file to // include IPv6 addresses. name: "IPv6 enabled", expIPv6Enabled: true, expEtcHosts: `127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters `, }, { // Create a container in the same network, with IPv6 disabled. Expect '::1' // not to be pingable, and no IPv6 addresses in its '/etc/hosts'. name: "IPv6 disabled", sysctls: map[string]string{"net.ipv6.conf.all.disable_ipv6": "1"}, expIPv6Enabled: false, expEtcHosts: "127.0.0.1\tlocalhost\n", }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { ctx := testutil.StartSpan(ctx, t) ctrId := container.Run(ctx, t, c, container.WithName("etchosts_"+sanitizeCtrName(t.Name())), container.WithImage("busybox:latest"), container.WithCmd("top"), container.WithSysctls(tc.sysctls), ) defer func() { c.ContainerRemove(ctx, ctrId, containertypes.RemoveOptions{Force: true}) }() runCmd := func(ctrId string, cmd []string, expExitCode int) string { t.Helper() execCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() res, err := container.Exec(execCtx, c, ctrId, cmd) assert.Check(t, is.Nil(err)) assert.Check(t, is.Equal(res.ExitCode, expExitCode)) return res.Stdout() } // Check that IPv6 is/isn't enabled, as expected. var expPingExitStatus int if !tc.expIPv6Enabled { expPingExitStatus = 1 } runCmd(ctrId, []string{"ping", "-6", "-c1", "-W3", "::1"}, expPingExitStatus) // Check the contents of /etc/hosts. stdout := runCmd(ctrId, []string{"cat", "/etc/hosts"}, 0) // Append the container's own addresses/name to the expected hosts file content. inspect := container.Inspect(ctx, t, c, ctrId) exp := tc.expEtcHosts + inspect.NetworkSettings.IPAddress + "\t" + inspect.Config.Hostname + "\n" if tc.expIPv6Enabled { exp += inspect.NetworkSettings.GlobalIPv6Address + "\t" + inspect.Config.Hostname + "\n" } assert.Check(t, is.Equal(stdout, exp)) }) } }