Merge pull request #47775 from vvoland/v26.1-47771
[26.1 backport] Option to avoid deleting the kernel_ll address from bridges.
diff --git a/integration/networking/bridge_test.go b/integration/networking/bridge_test.go
index 9188669..cb2abfa 100644
--- a/integration/networking/bridge_test.go
+++ b/integration/networking/bridge_test.go
@@ -753,3 +753,78 @@
stdout := runRes.Stdout.String()
assert.Check(t, is.Contains(stdout, scName))
}
+
+// With a read-only "/proc/sys/net" filesystem (simulated using env var
+// DOCKER_TEST_RO_DISABLE_IPV6), check that if IPv6 can't be disabled on a
+// container interface, container creation fails - unless the error is ignored by
+// setting env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1.
+// Regression test for https://github.com/moby/moby/issues/47751
+func TestReadOnlySlashProc(t *testing.T) {
+ skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+
+ ctx := setupTest(t)
+
+ testcases := []struct {
+ name string
+ daemonEnv []string
+ expErr string
+ }{
+ {
+ name: "Normality",
+ },
+ {
+ name: "Read only no workaround",
+ daemonEnv: []string{
+ "DOCKER_TEST_RO_DISABLE_IPV6=1",
+ },
+ expErr: "failed to disable IPv6 on container's interface eth0, set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore this error",
+ },
+ {
+ name: "Read only with workaround",
+ daemonEnv: []string{
+ "DOCKER_TEST_RO_DISABLE_IPV6=1",
+ "DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1",
+ },
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := testutil.StartSpan(ctx, t)
+
+ d := daemon.New(t, daemon.WithEnvVars(tc.daemonEnv...))
+ d.StartWithBusybox(ctx, t)
+ defer d.Stop(t)
+ c := d.NewClientT(t)
+
+ const net4Name = "testnet4"
+ network.CreateNoError(ctx, t, c, net4Name)
+ defer network.RemoveNoError(ctx, t, c, net4Name)
+ id4 := container.Create(ctx, t, c,
+ container.WithNetworkMode(net4Name),
+ container.WithCmd("ls"),
+ )
+ defer c.ContainerRemove(ctx, id4, containertypes.RemoveOptions{Force: true})
+ err := c.ContainerStart(ctx, id4, containertypes.StartOptions{})
+ if tc.expErr == "" {
+ assert.Check(t, err)
+ } else {
+ assert.Check(t, is.ErrorContains(err, tc.expErr))
+ }
+
+ // It should always be possible to create a container on an IPv6 network (IPv6
+ // doesn't need to be disabled on the interface).
+ const net6Name = "testnet6"
+ network.CreateNoError(ctx, t, c, net6Name,
+ network.WithIPv6(),
+ network.WithIPAM("fd5c:15e3:0b62:5395::/64", "fd5c:15e3:0b62:5395::1"),
+ )
+ defer network.RemoveNoError(ctx, t, c, net6Name)
+ id6 := container.Run(ctx, t, c,
+ container.WithNetworkMode(net6Name),
+ container.WithCmd("ls"),
+ )
+ defer c.ContainerRemove(ctx, id6, containertypes.RemoveOptions{Force: true})
+ })
+ }
+}
diff --git a/libnetwork/osl/namespace_linux.go b/libnetwork/osl/namespace_linux.go
index 8a41f6a..70dbc9d 100644
--- a/libnetwork/osl/namespace_linux.go
+++ b/libnetwork/osl/namespace_linux.go
@@ -646,17 +646,47 @@
value = '0'
}
- if _, err := os.Stat(path); err != nil {
+ if curVal, err := os.ReadFile(path); err != nil {
if os.IsNotExist(err) {
- log.G(context.TODO()).WithError(err).Warn("Cannot configure IPv6 forwarding on container interface. Has IPv6 been disabled in this node's kernel?")
+ if enable {
+ log.G(context.TODO()).WithError(err).Warn("Cannot enable IPv6 on container interface. Has IPv6 been disabled in this node's kernel?")
+ } else {
+ log.G(context.TODO()).WithError(err).Debug("Not disabling IPv6 on container interface. Has IPv6 been disabled in this node's kernel?")
+ }
return
}
errCh <- err
return
+ } else if len(curVal) > 0 && curVal[0] == value {
+ // Nothing to do, the setting is already correct.
+ return
}
- if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil {
- errCh <- fmt.Errorf("failed to %s IPv6 forwarding for container's interface %s: %w", action, iface, err)
+ if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil || os.Getenv("DOCKER_TEST_RO_DISABLE_IPV6") != "" {
+ logger := log.G(context.TODO()).WithFields(log.Fields{
+ "error": err,
+ "interface": iface,
+ })
+ if enable {
+ // The user asked for IPv6 on the interface, and we can't give it to them.
+ // But, in line with the IsNotExist case above, just log.
+ logger.Warn("Cannot enable IPv6 on container interface, continuing.")
+ } else if os.Getenv("DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE") == "1" {
+ // TODO(robmry) - remove this escape hatch for https://github.com/moby/moby/issues/47751
+ // If the "/proc" file exists but isn't writable, we can't disable IPv6, which is
+ // https://github.com/moby/moby/security/advisories/GHSA-x84c-p2g9-rqv9 ... so,
+ // the user is required to override the error (or configure IPv6, or disable IPv6
+ // by default in the OS, or make the "/proc" file writable). Once it's possible
+ // to enable IPv6 without having to configure IPAM etc, the env var should be
+ // removed. Then the user will have to explicitly enable IPv6 if it can't be
+ // disabled on the interface.
+ logger.Info("Cannot disable IPv6 on container interface but DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1, continuing.")
+ } else {
+ logger.Error("Cannot disable IPv6 on container interface. Set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore.")
+ errCh <- fmt.Errorf(
+ "failed to %s IPv6 on container's interface %s, set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore this error",
+ action, iface)
+ }
return
}
}()