From 660b9962e4d2d4ad68898b00571a43127b84f6c3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Aug 2021 20:43:30 +0200 Subject: [PATCH] daemon.WithCommonOptions() fix detection of user-namespaces Commit dae652e2e5e47d99c8febd5bc81df0a3269beb74 added support for non-privileged containers to use ICMP_PROTO (used for `ping`). This option cannot be set for containers that have user-namespaces enabled. However, the detection looks to be incorrect; HostConfig.UsernsMode was added in 6993e891d10c760d22e0ea3d455f13858cd0de46 / ee2183881b0273ff1707501e71798a61018f50f0, and the property only has meaning if the daemon is running with user namespaces enabled. In other situations, the property has no meaning. As a result of the above, the sysctl would only be set for containers running with UsernsMode=host on a daemon running with user-namespaces enabled. This patch adds a check if the daemon has user-namespaces enabled (RemappedRoot having a non-empty value), or if the daemon is running inside a user namespace (e.g. rootless mode) to fix the detection. Signed-off-by: Sebastiaan van Stijn (cherry picked from commit a826ca3aefbd4d29344d851723731e3809f2a4ad) --- The cherry-pick was almost clean but `userns.RunningInUserNS()` -> `sys.RunningInUserNS()`. Fix docker/buildx issue 561 --- Signed-off-by: Akihiro Suda --- daemon/oci_linux.go | 3 ++- daemon/oci_linux_test.go | 16 ++++++++++++-- integration/container/run_linux_test.go | 29 +++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 948690fe9afec..3d34dcb31df99 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -767,7 +767,8 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts { // joining an existing namespace, only if we create a new net namespace. if c.HostConfig.NetworkMode.IsPrivate() { // We cannot set up ping socket support in a user namespace - if !c.HostConfig.UsernsMode.IsPrivate() && sysctlExists("net.ipv4.ping_group_range") { + userNS := daemon.configStore.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate() + if !userNS && !sys.RunningInUserNS() && sysctlExists("net.ipv4.ping_group_range") { // allow unprivileged ICMP echo sockets without CAP_NET_RAW s.Linux.Sysctl["net.ipv4.ping_group_range"] = "0 2147483647" } diff --git a/daemon/oci_linux_test.go b/daemon/oci_linux_test.go index 890875af8740c..32854f92152e8 100644 --- a/daemon/oci_linux_test.go +++ b/daemon/oci_linux_test.go @@ -120,7 +120,6 @@ func TestSysctlOverride(t *testing.T) { HostConfig: &containertypes.HostConfig{ NetworkMode: "bridge", Sysctls: map[string]string{}, - UsernsMode: "host", }, } d := setupFakeDaemon(t, c) @@ -148,6 +147,20 @@ func TestSysctlOverride(t *testing.T) { assert.Equal(t, s.Hostname, "foobar") assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"]) assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"]) + + // Ensure the ping_group_range is not set on a daemon with user-namespaces enabled + d.configStore.RemappedRoot = "dummy:dummy" + s, err = d.createSpec(c) + assert.NilError(t, err) + _, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"] + assert.Assert(t, !ok) + + // Ensure the ping_group_range is set on a container in "host" userns mode + // on a daemon with user-namespaces enabled + c.HostConfig.UsernsMode = "host" + s, err = d.createSpec(c) + assert.NilError(t, err) + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647") } // TestSysctlOverrideHost ensures that any implicit network sysctls are not set @@ -159,7 +172,6 @@ func TestSysctlOverrideHost(t *testing.T) { HostConfig: &containertypes.HostConfig{ NetworkMode: "host", Sysctls: map[string]string{}, - UsernsMode: "host", }, } d := setupFakeDaemon(t, c) diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index 9bff9b5a8ee59..a8e47e9f9e56c 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -99,3 +99,32 @@ func TestHostnameDnsResolution(t *testing.T) { assert.Check(t, is.Equal("", res.Stderr())) assert.Equal(t, 0, res.ExitCode) } + +func TestUnprivilegedPortsAndPing(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting net.ipv4.ping_group_range and net.ipv4.ip_unprivileged_port_start") + + defer setupTest(t)() + client := testEnv.APIClient() + ctx := context.Background() + + cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) { + c.Config.User = "1000:1000" + }) + + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + + // Check net.ipv4.ping_group_range. + res, err := container.Exec(ctx, client, cID, []string{"cat", "/proc/sys/net/ipv4/ping_group_range"}) + assert.NilError(t, err) + assert.Assert(t, is.Len(res.Stderr(), 0)) + assert.Equal(t, 0, res.ExitCode) + assert.Equal(t, `0 2147483647`, strings.TrimSpace(res.Stdout())) + + // Check net.ipv4.ip_unprivileged_port_start. + res, err = container.Exec(ctx, client, cID, []string{"cat", "/proc/sys/net/ipv4/ip_unprivileged_port_start"}) + assert.NilError(t, err) + assert.Assert(t, is.Len(res.Stderr(), 0)) + assert.Equal(t, 0, res.ExitCode) + assert.Equal(t, "0", strings.TrimSpace(res.Stdout())) +}