summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>2023-01-19 10:33:30 +0900
committerAkihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>2023-01-21 01:00:27 +0900
commitb3c5352386c2bb6c46d0f2232bad9e84e53e8d27 (patch)
tree17190f64d98cf12de5beb3b4203101682977e219
parent40cc022c70471023a0ecfe592a39b5681ea2245b (diff)
downloaddocker-b3c5352386c2bb6c46d0f2232bad9e84e53e8d27.tar.gz
rootless: support `--ipc=host`
Fix issue 44294 Co-authored-by: Sebastiaan van Stijn <github@gone.nl> Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
-rw-r--r--integration/container/ipcmode_linux_test.go10
-rw-r--r--pkg/rootless/specconv/specconv_linux.go57
2 files changed, 53 insertions, 14 deletions
diff --git a/integration/container/ipcmode_linux_test.go b/integration/container/ipcmode_linux_test.go
index 49141ce58e..61d30b3360 100644
--- a/integration/container/ipcmode_linux_test.go
+++ b/integration/container/ipcmode_linux_test.go
@@ -115,7 +115,7 @@ func TestIpcModePrivate(t *testing.T) {
// also exists on the host.
func TestIpcModeShareable(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon)
- skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
+ skip.If(t, testEnv.IsRootless, "no support for --ipc=shareable in rootless")
testIpcNonePrivateShareable(t, "shareable", true, true)
}
@@ -191,7 +191,6 @@ func TestAPIIpcModeShareableAndContainer(t *testing.T) {
func TestAPIIpcModeHost(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon)
skip.If(t, testEnv.IsUserNamespace)
- skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
cfg := containertypes.Config{
Image: "busybox",
@@ -263,7 +262,7 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin
// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
func TestDaemonIpcModeShareable(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon)
- skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
+ skip.If(t, testEnv.IsRootless, "no support for --ipc=shareable in rootless")
testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable")
}
@@ -277,9 +276,9 @@ func TestDaemonIpcModePrivate(t *testing.T) {
// used to check if an IpcMode given in config works as intended
func testDaemonIpcFromConfig(t *testing.T, mode string, mustExist bool) {
- skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
config := `{"default-ipc-mode": "` + mode + `"}`
- file := fs.NewFile(t, "test-daemon-ipc-config", fs.WithContent(config))
+ // WithMode is needed for rootless
+ file := fs.NewFile(t, "test-daemon-ipc-config", fs.WithContent(config), fs.WithMode(0o644))
defer file.Remove()
testDaemonIpcPrivateShareable(t, mustExist, "--config-file", file.Path())
@@ -295,6 +294,7 @@ func TestDaemonIpcModePrivateFromConfig(t *testing.T) {
// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended.
func TestDaemonIpcModeShareableFromConfig(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon)
+ skip.If(t, testEnv.IsRootless, "no support for --ipc=shareable in rootless")
testDaemonIpcFromConfig(t, "shareable", true)
}
diff --git a/pkg/rootless/specconv/specconv_linux.go b/pkg/rootless/specconv/specconv_linux.go
index 16200e22e9..b706b5ca6a 100644
--- a/pkg/rootless/specconv/specconv_linux.go
+++ b/pkg/rootless/specconv/specconv_linux.go
@@ -1,8 +1,10 @@
package specconv // import "github.com/docker/docker/pkg/rootless/specconv"
import (
+ "fmt"
"os"
"path"
+ "path/filepath"
"strconv"
"strings"
@@ -14,6 +16,7 @@ import (
// * Remove non-supported cgroups
// * Fix up OOMScoreAdj
// * Fix up /proc if --pid=host
+// * Fix up /dev/shm and /dev/mqueue if --ipc=host
//
// v2Controllers should be non-nil only if running with v2 and systemd.
func ToRootless(spec *specs.Spec, v2Controllers []string) error {
@@ -79,31 +82,48 @@ func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int
}
// Fix up /proc if --pid=host
- pidHost, err := isPidHost(spec)
+ pidHost, err := isHostNS(spec, specs.PIDNamespace)
if err != nil {
return err
}
- if !pidHost {
- return nil
+ if pidHost {
+ if err := bindMountHostProcfs(spec); err != nil {
+ return err
+ }
+ }
+
+ // Fix up /dev/shm and /dev/mqueue if --ipc=host
+ ipcHost, err := isHostNS(spec, specs.IPCNamespace)
+ if err != nil {
+ return err
}
- return bindMountHostProcfs(spec)
+ if ipcHost {
+ if err := bindMountHostIPC(spec); err != nil {
+ return err
+ }
+ }
+
+ return nil
}
-func isPidHost(spec *specs.Spec) (bool, error) {
+func isHostNS(spec *specs.Spec, nsType specs.LinuxNamespaceType) (bool, error) {
+ if strings.Contains(string(nsType), string(os.PathSeparator)) {
+ return false, fmt.Errorf("unexpected namespace type %q", nsType)
+ }
for _, ns := range spec.Linux.Namespaces {
- if ns.Type == specs.PIDNamespace {
+ if ns.Type == nsType {
if ns.Path == "" {
return false, nil
}
- pidNS, err := os.Readlink(ns.Path)
+ ns, err := os.Readlink(ns.Path)
if err != nil {
return false, err
}
- selfPidNS, err := os.Readlink("/proc/self/ns/pid")
+ selfNS, err := os.Readlink(filepath.Join("/proc/self/ns", string(nsType)))
if err != nil {
return false, err
}
- return pidNS == selfPidNS, nil
+ return ns == selfNS, nil
}
}
return true, nil
@@ -136,3 +156,22 @@ func bindMountHostProcfs(spec *specs.Spec) error {
return nil
}
+
+// withBindMountHostIPC replaces /dev/shm and /dev/mqueue mount with rbind.
+// Required for --ipc=host on rootless.
+//
+// Based on https://github.com/containerd/nerdctl/blob/v1.1.0/cmd/nerdctl/run.go#L836-L860
+func bindMountHostIPC(s *specs.Spec) error {
+ for i, m := range s.Mounts {
+ switch p := path.Clean(m.Destination); p {
+ case "/dev/shm", "/dev/mqueue":
+ s.Mounts[i] = specs.Mount{
+ Destination: p,
+ Type: "bind",
+ Source: p,
+ Options: []string{"rbind", "nosuid", "noexec", "nodev"},
+ }
+ }
+ }
+ return nil
+}