specconv_linux.go 3.4 KB

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