github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/rootless/specconv/specconv_linux.go (about)

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