2023-01-03 12:08:40 +00:00
|
|
|
package specconv // import "github.com/docker/docker/pkg/rootless/specconv"
|
2018-10-15 07:52:53 +00:00
|
|
|
|
|
|
|
import (
|
2023-06-23 00:33:17 +00:00
|
|
|
"context"
|
2023-01-19 01:33:30 +00:00
|
|
|
"fmt"
|
2021-01-19 05:12:43 +00:00
|
|
|
"os"
|
|
|
|
"path"
|
2023-01-19 01:33:30 +00:00
|
|
|
"path/filepath"
|
2018-10-15 07:52:53 +00:00
|
|
|
"strconv"
|
2021-03-23 09:11:35 +00:00
|
|
|
"strings"
|
2018-10-15 07:52:53 +00:00
|
|
|
|
2023-09-13 15:41:45 +00:00
|
|
|
"github.com/containerd/log"
|
2019-08-05 14:37:47 +00:00
|
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
2018-10-15 07:52:53 +00:00
|
|
|
)
|
|
|
|
|
2023-09-29 11:31:22 +00:00
|
|
|
// ToRootfulInRootless is used for "rootful-in-rootless" dind;
|
|
|
|
// the daemon is running in UserNS but has no access to RootlessKit API socket, host filesystem, etc.
|
|
|
|
//
|
|
|
|
// This fuction does:
|
|
|
|
// * Fix up OOMScoreAdj (needed since systemd v250: https://github.com/moby/moby/issues/46563)
|
|
|
|
func ToRootfulInRootless(spec *specs.Spec) {
|
|
|
|
if spec.Process == nil || spec.Process.OOMScoreAdj == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if currentOOMScoreAdj := getCurrentOOMScoreAdj(); *spec.Process.OOMScoreAdj < currentOOMScoreAdj {
|
|
|
|
*spec.Process.OOMScoreAdj = currentOOMScoreAdj
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 07:52:53 +00:00
|
|
|
// ToRootless converts spec to be compatible with "rootless" runc.
|
2020-02-10 05:37:22 +00:00
|
|
|
// * Remove non-supported cgroups
|
2018-10-15 07:52:53 +00:00
|
|
|
// * Fix up OOMScoreAdj
|
2021-01-19 05:12:43 +00:00
|
|
|
// * Fix up /proc if --pid=host
|
2023-01-19 01:33:30 +00:00
|
|
|
// * Fix up /dev/shm and /dev/mqueue if --ipc=host
|
2020-02-10 05:37:22 +00:00
|
|
|
//
|
|
|
|
// v2Controllers should be non-nil only if running with v2 and systemd.
|
|
|
|
func ToRootless(spec *specs.Spec, v2Controllers []string) error {
|
|
|
|
return toRootless(spec, v2Controllers, getCurrentOOMScoreAdj())
|
2018-10-15 07:52:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getCurrentOOMScoreAdj() int {
|
2021-08-24 10:10:50 +00:00
|
|
|
b, err := os.ReadFile("/proc/self/oom_score_adj")
|
2018-10-15 07:52:53 +00:00
|
|
|
if err != nil {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).WithError(err).Warn("failed to read /proc/self/oom_score_adj")
|
2018-10-15 07:52:53 +00:00
|
|
|
return 0
|
|
|
|
}
|
2021-03-23 09:11:35 +00:00
|
|
|
s := string(b)
|
|
|
|
i, err := strconv.Atoi(strings.TrimSpace(s))
|
2018-10-15 07:52:53 +00:00
|
|
|
if err != nil {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).WithError(err).Warnf("failed to parse /proc/self/oom_score_adj (%q)", s)
|
2018-10-15 07:52:53 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2020-02-10 05:37:22 +00:00
|
|
|
func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int) error {
|
|
|
|
if len(v2Controllers) == 0 {
|
2023-06-06 16:57:38 +00:00
|
|
|
if spec.Linux != nil {
|
|
|
|
// Remove cgroup settings.
|
|
|
|
spec.Linux.Resources = nil
|
|
|
|
spec.Linux.CgroupsPath = ""
|
|
|
|
}
|
2020-02-10 05:37:22 +00:00
|
|
|
} else {
|
2023-06-06 16:57:38 +00:00
|
|
|
if spec.Linux != nil && spec.Linux.Resources != nil {
|
2020-02-10 05:37:22 +00:00
|
|
|
m := make(map[string]struct{})
|
|
|
|
for _, s := range v2Controllers {
|
|
|
|
m[s] = struct{}{}
|
|
|
|
}
|
|
|
|
// Remove devices: https://github.com/containers/crun/issues/255
|
|
|
|
spec.Linux.Resources.Devices = nil
|
|
|
|
if _, ok := m["memory"]; !ok {
|
|
|
|
spec.Linux.Resources.Memory = nil
|
|
|
|
}
|
|
|
|
if _, ok := m["cpu"]; !ok {
|
|
|
|
spec.Linux.Resources.CPU = nil
|
|
|
|
}
|
|
|
|
if _, ok := m["cpuset"]; !ok {
|
|
|
|
if spec.Linux.Resources.CPU != nil {
|
|
|
|
spec.Linux.Resources.CPU.Cpus = ""
|
|
|
|
spec.Linux.Resources.CPU.Mems = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if _, ok := m["pids"]; !ok {
|
|
|
|
spec.Linux.Resources.Pids = nil
|
|
|
|
}
|
|
|
|
if _, ok := m["io"]; !ok {
|
|
|
|
spec.Linux.Resources.BlockIO = nil
|
|
|
|
}
|
|
|
|
if _, ok := m["rdma"]; !ok {
|
|
|
|
spec.Linux.Resources.Rdma = nil
|
|
|
|
}
|
|
|
|
spec.Linux.Resources.HugepageLimits = nil
|
|
|
|
spec.Linux.Resources.Network = nil
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 07:52:53 +00:00
|
|
|
|
2023-06-06 16:57:38 +00:00
|
|
|
if spec.Process != nil && spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj {
|
2018-10-15 07:52:53 +00:00
|
|
|
*spec.Process.OOMScoreAdj = currentOOMScoreAdj
|
|
|
|
}
|
2021-01-19 05:12:43 +00:00
|
|
|
|
|
|
|
// Fix up /proc if --pid=host
|
2023-01-19 01:33:30 +00:00
|
|
|
pidHost, err := isHostNS(spec, specs.PIDNamespace)
|
2021-01-19 05:12:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-01-19 01:33:30 +00:00
|
|
|
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
|
2021-01-19 05:12:43 +00:00
|
|
|
}
|
2023-01-19 01:33:30 +00:00
|
|
|
if ipcHost {
|
|
|
|
if err := bindMountHostIPC(spec); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2021-01-19 05:12:43 +00:00
|
|
|
}
|
|
|
|
|
2023-01-19 01:33:30 +00:00
|
|
|
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)
|
|
|
|
}
|
2023-06-06 16:57:38 +00:00
|
|
|
if spec.Linux == nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
2021-01-19 05:12:43 +00:00
|
|
|
for _, ns := range spec.Linux.Namespaces {
|
2023-01-19 01:33:30 +00:00
|
|
|
if ns.Type == nsType {
|
2021-01-19 05:12:43 +00:00
|
|
|
if ns.Path == "" {
|
|
|
|
return false, nil
|
|
|
|
}
|
2023-01-19 01:33:30 +00:00
|
|
|
ns, err := os.Readlink(ns.Path)
|
2021-01-19 05:12:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2023-01-19 01:33:30 +00:00
|
|
|
selfNS, err := os.Readlink(filepath.Join("/proc/self/ns", string(nsType)))
|
2021-01-19 05:12:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2023-01-19 01:33:30 +00:00
|
|
|
return ns == selfNS, nil
|
2021-01-19 05:12:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-06 16:57:38 +00:00
|
|
|
if spec.Linux != nil {
|
|
|
|
// 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)
|
|
|
|
}
|
2021-01-19 05:12:43 +00:00
|
|
|
}
|
2023-06-06 16:57:38 +00:00
|
|
|
spec.Linux.ReadonlyPaths = newROP
|
2021-01-19 05:12:43 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 07:52:53 +00:00
|
|
|
return nil
|
|
|
|
}
|
2023-01-19 01:33:30 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|