Merge pull request #41893 from AkihiroSuda/fix-41457

rootless: support --pid=host
This commit is contained in:
Akihiro Suda 2021-04-06 14:30:40 +09:00 committed by GitHub
commit c8ff7305f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 35 deletions

View File

@ -2425,28 +2425,6 @@ func (s *DockerSuite) TestContainerNetworkMode(c *testing.T) {
}
}
func (s *DockerSuite) TestRunModePIDHost(c *testing.T) {
// Not applicable on Windows as uses Unix-specific capabilities
testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
hostPid, err := os.Readlink("/proc/1/ns/pid")
if err != nil {
c.Fatal(err)
}
out, _ := dockerCmd(c, "run", "--pid=host", "busybox", "readlink", "/proc/self/ns/pid")
out = strings.Trim(out, "\n")
if hostPid != out {
c.Fatalf("PID different with --pid=host %s != %s\n", hostPid, out)
}
out, _ = dockerCmd(c, "run", "busybox", "readlink", "/proc/self/ns/pid")
out = strings.Trim(out, "\n")
if hostPid == out {
c.Fatalf("PID should be different without --pid=host %s == %s\n", hostPid, out)
}
}
func (s *DockerSuite) TestRunModeUTSHost(c *testing.T) {
// Not applicable on Windows as uses Unix-specific capabilities
testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)

View File

@ -0,0 +1,37 @@
package container // import "github.com/docker/docker/integration/container"
import (
"context"
"os"
"testing"
"time"
"github.com/docker/docker/integration/internal/container"
"gotest.tools/v3/assert"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
func TestPidHost(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
skip.If(t, testEnv.IsRemoteDaemon())
hostPid, err := os.Readlink("/proc/1/ns/pid")
assert.NilError(t, err)
defer setupTest(t)()
client := testEnv.APIClient()
ctx := context.Background()
cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
c.HostConfig.PidMode = "host"
})
poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
cPid := container.GetContainerNS(ctx, t, client, cID, "pid")
assert.Assert(t, hostPid == cPid)
cID = container.Run(ctx, t, client)
poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
cPid = container.GetContainerNS(ctx, t, client, cID, "pid")
assert.Assert(t, hostPid != cPid)
}

View File

@ -2,7 +2,6 @@ package container // import "github.com/docker/docker/integration/container"
import (
"context"
"strings"
"testing"
"time"
@ -11,20 +10,10 @@ import (
"github.com/docker/docker/integration/internal/requirement"
"github.com/docker/docker/testutil/daemon"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
// Gets the value of the cgroup namespace for pid 1 of a container
func containerCgroupNamespace(ctx context.Context, t *testing.T, client *client.Client, cID string) string {
res, err := container.Exec(ctx, client, cID, []string{"readlink", "/proc/1/ns/cgroup"})
assert.NilError(t, err)
assert.Assert(t, is.Len(res.Stderr(), 0))
assert.Equal(t, 0, res.ExitCode)
return strings.TrimSpace(res.Stdout())
}
// Bring up a daemon with the specified default cgroup namespace mode, and then create a container with the container options
func testRunWithCgroupNs(t *testing.T, daemonNsMode string, containerOpts ...func(*container.TestContainerConfig)) (string, string) {
d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
@ -38,7 +27,7 @@ func testRunWithCgroupNs(t *testing.T, daemonNsMode string, containerOpts ...fun
poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
daemonCgroup := d.CgroupNamespace(t)
containerCgroup := containerCgroupNamespace(ctx, t, client, cID)
containerCgroup := container.GetContainerNS(ctx, t, client, cID, "cgroup")
return containerCgroup, daemonCgroup
}
@ -147,7 +136,7 @@ func TestCgroupNamespacesRunOlderClient(t *testing.T) {
poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
daemonCgroup := d.CgroupNamespace(t)
containerCgroup := containerCgroupNamespace(ctx, t, client, cID)
containerCgroup := container.GetContainerNS(ctx, t, client, cID, "cgroup")
if testEnv.DaemonInfo.CgroupVersion != "2" {
assert.Assert(t, daemonCgroup == containerCgroup)
} else {

View File

@ -0,0 +1,21 @@
package container
import (
"context"
"strings"
"testing"
"github.com/docker/docker/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
// GetContainerNS gets the value of the specified namespace of a container
func GetContainerNS(ctx context.Context, t *testing.T, client client.APIClient, cID, nsName string) string {
t.Helper()
res, err := Exec(ctx, client, cID, []string{"readlink", "/proc/self/ns/" + nsName})
assert.NilError(t, err)
assert.Assert(t, is.Len(res.Stderr(), 0))
assert.Equal(t, 0, res.ExitCode)
return strings.TrimSpace(res.Stdout())
}

View File

@ -2,6 +2,8 @@ package specconv // import "github.com/docker/docker/rootless/specconv"
import (
"io/ioutil"
"os"
"path"
"strconv"
"strings"
@ -12,6 +14,7 @@ import (
// ToRootless converts spec to be compatible with "rootless" runc.
// * Remove non-supported cgroups
// * Fix up OOMScoreAdj
// * Fix up /proc if --pid=host
//
// v2Controllers should be non-nil only if running with v2 and systemd.
func ToRootless(spec *specs.Spec, v2Controllers []string) error {
@ -75,5 +78,62 @@ func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int
if spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj {
*spec.Process.OOMScoreAdj = currentOOMScoreAdj
}
// Fix up /proc if --pid=host
pidHost, err := isPidHost(spec)
if err != nil {
return err
}
if !pidHost {
return nil
}
return bindMountHostProcfs(spec)
}
func isPidHost(spec *specs.Spec) (bool, error) {
for _, ns := range spec.Linux.Namespaces {
if ns.Type == specs.PIDNamespace {
if ns.Path == "" {
return false, nil
}
pidNS, err := os.Readlink(ns.Path)
if err != nil {
return false, err
}
selfPidNS, err := os.Readlink("/proc/self/ns/pid")
if err != nil {
return false, err
}
return pidNS == selfPidNS, nil
}
}
return true, nil
}
func bindMountHostProcfs(spec *specs.Spec) error {
// Replace procfs mount with rbind
// https://github.com/containers/podman/blob/v3.0.0-rc1/pkg/specgen/generate/oci.go#L248-L257
for i, m := range spec.Mounts {
if path.Clean(m.Destination) == "/proc" {
newM := specs.Mount{
Destination: "/proc",
Type: "bind",
Source: "/proc",
Options: []string{"rbind", "nosuid", "noexec", "nodev"},
}
spec.Mounts[i] = newM
}
}
// Remove ReadonlyPaths for /proc/*
newROP := spec.Linux.ReadonlyPaths[:0]
for _, s := range spec.Linux.ReadonlyPaths {
s = path.Clean(s)
if !strings.HasPrefix(s, "/proc/") {
newROP = append(newROP, s)
}
}
spec.Linux.ReadonlyPaths = newROP
return nil
}