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>
This commit is contained in:
parent
40cc022c70
commit
b3c5352386
2 changed files with 53 additions and 14 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
return bindMountHostProcfs(spec)
|
||||
|
||||
// Fix up /dev/shm and /dev/mqueue if --ipc=host
|
||||
ipcHost, err := isHostNS(spec, specs.IPCNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue