userxattr.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. //go:build linux
  2. // Forked from https://github.com/containerd/containerd/blob/9ade247b38b5a685244e1391c86ff41ab109556e/snapshots/overlay/check.go
  3. /*
  4. Copyright The containerd Authors.
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. */
  15. package overlayutils
  16. import (
  17. "context"
  18. "fmt"
  19. "os"
  20. "path/filepath"
  21. "github.com/containerd/containerd/mount"
  22. "github.com/containerd/containerd/pkg/userns"
  23. "github.com/containerd/log"
  24. "github.com/docker/docker/pkg/parsers/kernel"
  25. )
  26. // NeedsUserXAttr returns whether overlayfs should be mounted with the "userxattr" mount option.
  27. //
  28. // The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11.
  29. //
  30. // The "userxattr" option is NOT needed for the initial user namespace (aka "the host").
  31. //
  32. // Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount
  33. // the overlayfs in a user namespace without the "userxattr" option.
  34. //
  35. // The corresponding kernel commit: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
  36. // > ovl: user xattr
  37. // >
  38. // > Optionally allow using "user.overlay." namespace instead of "trusted.overlay."
  39. // > ...
  40. // > Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the
  41. // > "user.overlay.redirect" or "user.overlay.metacopy" xattrs.
  42. // > ...
  43. //
  44. // The "userxattr" support is not exposed in "/sys/module/overlay/parameters".
  45. func NeedsUserXAttr(d string) (bool, error) {
  46. if !userns.RunningInUserNS() {
  47. // we are the real root (i.e., the root in the initial user NS),
  48. // so we do never need "userxattr" opt.
  49. return false, nil
  50. }
  51. // Fast path for kernel >= 5.11 .
  52. //
  53. // Keep in mind that distro vendors might be going to backport the patch to older kernels.
  54. // So we can't completely remove the "slow path".
  55. if kernel.CheckKernelVersion(5, 11, 0) {
  56. return true, nil
  57. }
  58. tdRoot := filepath.Join(d, "userxattr-check")
  59. if err := os.RemoveAll(tdRoot); err != nil {
  60. log.G(context.TODO()).WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
  61. }
  62. if err := os.MkdirAll(tdRoot, 0o700); err != nil {
  63. return false, err
  64. }
  65. defer func() {
  66. if err := os.RemoveAll(tdRoot); err != nil {
  67. log.G(context.TODO()).WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
  68. }
  69. }()
  70. td, err := os.MkdirTemp(tdRoot, "")
  71. if err != nil {
  72. return false, err
  73. }
  74. for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
  75. if err := os.Mkdir(filepath.Join(td, dir), 0o755); err != nil {
  76. return false, err
  77. }
  78. }
  79. opts := []string{
  80. fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"), filepath.Join(td, "upper"), filepath.Join(td, "work")),
  81. "userxattr",
  82. }
  83. m := mount.Mount{
  84. Type: "overlay",
  85. Source: "overlay",
  86. Options: opts,
  87. }
  88. dest := filepath.Join(td, "merged")
  89. if err := m.Mount(dest); err != nil {
  90. // Probably the host is running Ubuntu/Debian kernel (< 5.11) with the userns patch but without the userxattr patch.
  91. // Return false without error.
  92. log.G(context.TODO()).WithError(err).Debugf("cannot mount overlay with \"userxattr\", probably the kernel does not support userxattr")
  93. return false, nil
  94. }
  95. if err := mount.UnmountAll(dest, 0); err != nil {
  96. log.G(context.TODO()).WithError(err).Warnf("Failed to unmount check directory %v", dest)
  97. }
  98. return true, nil
  99. }