Skip to content

Commit

Permalink
Merge pull request #47774 from vvoland/v26.1-47769
Browse files Browse the repository at this point in the history
[26.1 backport] Allow for a read-only "/proc/sys/net".
  • Loading branch information
thaJeztah committed Apr 30, 2024
2 parents 21da192 + 4061808 commit 9a2b531
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 4 deletions.
75 changes: 75 additions & 0 deletions integration/networking/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,78 @@ func TestSetInterfaceSysctl(t *testing.T) {
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})
})
}
}
38 changes: 34 additions & 4 deletions libnetwork/osl/namespace_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,17 +646,47 @@ func setIPv6(nspath, iface string, enable bool) error {
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
}
}()
Expand Down

0 comments on commit 9a2b531

Please sign in to comment.