github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/rootless/specconv/specconv_linux.go (about)

     1  package specconv // import "github.com/docker/docker/rootless/specconv"
     2  
     3  import (
     4  	"os"
     5  	"path"
     6  	"strconv"
     7  	"strings"
     8  
     9  	specs "github.com/opencontainers/runtime-spec/specs-go"
    10  	"github.com/sirupsen/logrus"
    11  )
    12  
    13  // ToRootless converts spec to be compatible with "rootless" runc.
    14  // * Remove non-supported cgroups
    15  // * Fix up OOMScoreAdj
    16  // * Fix up /proc if --pid=host
    17  //
    18  // v2Controllers should be non-nil only if running with v2 and systemd.
    19  func ToRootless(spec *specs.Spec, v2Controllers []string) error {
    20  	return toRootless(spec, v2Controllers, getCurrentOOMScoreAdj())
    21  }
    22  
    23  func getCurrentOOMScoreAdj() int {
    24  	b, err := os.ReadFile("/proc/self/oom_score_adj")
    25  	if err != nil {
    26  		logrus.WithError(err).Warn("failed to read /proc/self/oom_score_adj")
    27  		return 0
    28  	}
    29  	s := string(b)
    30  	i, err := strconv.Atoi(strings.TrimSpace(s))
    31  	if err != nil {
    32  		logrus.WithError(err).Warnf("failed to parse /proc/self/oom_score_adj (%q)", s)
    33  		return 0
    34  	}
    35  	return i
    36  }
    37  
    38  func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int) error {
    39  	if len(v2Controllers) == 0 {
    40  		// Remove cgroup settings.
    41  		spec.Linux.Resources = nil
    42  		spec.Linux.CgroupsPath = ""
    43  	} else {
    44  		if spec.Linux.Resources != nil {
    45  			m := make(map[string]struct{})
    46  			for _, s := range v2Controllers {
    47  				m[s] = struct{}{}
    48  			}
    49  			// Remove devices: https://github.com/containers/crun/issues/255
    50  			spec.Linux.Resources.Devices = nil
    51  			if _, ok := m["memory"]; !ok {
    52  				spec.Linux.Resources.Memory = nil
    53  			}
    54  			if _, ok := m["cpu"]; !ok {
    55  				spec.Linux.Resources.CPU = nil
    56  			}
    57  			if _, ok := m["cpuset"]; !ok {
    58  				if spec.Linux.Resources.CPU != nil {
    59  					spec.Linux.Resources.CPU.Cpus = ""
    60  					spec.Linux.Resources.CPU.Mems = ""
    61  				}
    62  			}
    63  			if _, ok := m["pids"]; !ok {
    64  				spec.Linux.Resources.Pids = nil
    65  			}
    66  			if _, ok := m["io"]; !ok {
    67  				spec.Linux.Resources.BlockIO = nil
    68  			}
    69  			if _, ok := m["rdma"]; !ok {
    70  				spec.Linux.Resources.Rdma = nil
    71  			}
    72  			spec.Linux.Resources.HugepageLimits = nil
    73  			spec.Linux.Resources.Network = nil
    74  		}
    75  	}
    76  
    77  	if spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj {
    78  		*spec.Process.OOMScoreAdj = currentOOMScoreAdj
    79  	}
    80  
    81  	// Fix up /proc if --pid=host
    82  	pidHost, err := isPidHost(spec)
    83  	if err != nil {
    84  		return err
    85  	}
    86  	if !pidHost {
    87  		return nil
    88  	}
    89  	return bindMountHostProcfs(spec)
    90  }
    91  
    92  func isPidHost(spec *specs.Spec) (bool, error) {
    93  	for _, ns := range spec.Linux.Namespaces {
    94  		if ns.Type == specs.PIDNamespace {
    95  			if ns.Path == "" {
    96  				return false, nil
    97  			}
    98  			pidNS, err := os.Readlink(ns.Path)
    99  			if err != nil {
   100  				return false, err
   101  			}
   102  			selfPidNS, err := os.Readlink("/proc/self/ns/pid")
   103  			if err != nil {
   104  				return false, err
   105  			}
   106  			return pidNS == selfPidNS, nil
   107  		}
   108  	}
   109  	return true, nil
   110  }
   111  
   112  func bindMountHostProcfs(spec *specs.Spec) error {
   113  	// Replace procfs mount with rbind
   114  	// https://github.com/containers/podman/blob/v3.0.0-rc1/pkg/specgen/generate/oci.go#L248-L257
   115  	for i, m := range spec.Mounts {
   116  		if path.Clean(m.Destination) == "/proc" {
   117  			newM := specs.Mount{
   118  				Destination: "/proc",
   119  				Type:        "bind",
   120  				Source:      "/proc",
   121  				Options:     []string{"rbind", "nosuid", "noexec", "nodev"},
   122  			}
   123  			spec.Mounts[i] = newM
   124  		}
   125  	}
   126  
   127  	// Remove ReadonlyPaths for /proc/*
   128  	newROP := spec.Linux.ReadonlyPaths[:0]
   129  	for _, s := range spec.Linux.ReadonlyPaths {
   130  		s = path.Clean(s)
   131  		if !strings.HasPrefix(s, "/proc/") {
   132  			newROP = append(newROP, s)
   133  		}
   134  	}
   135  	spec.Linux.ReadonlyPaths = newROP
   136  
   137  	return nil
   138  }