github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/parent/warn.go (about) 1 package parent 2 3 import ( 4 "errors" 5 "os" 6 "strconv" 7 "strings" 8 9 "github.com/moby/sys/mountinfo" 10 "github.com/sirupsen/logrus" 11 "golang.org/x/sys/unix" 12 ) 13 14 func warnPropagation(propagation string) { 15 mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter("/")) 16 if err != nil || len(mounts) < 1 { 17 logrus.WithError(err).Warn("Failed to parse mountinfo") 18 return 19 } 20 root := mounts[0] 21 // 1. When running on a "sane" host, root.Optional is like "shared:1". ("shared" in findmnt(8) output) 22 // 2. When running inside a container, root.Optional is like "master:363". ("private, slave" in findmnt(8) output) 23 // 24 // Setting non-private propagation is supported for 1, unsupported for 2. 25 if !strings.Contains(propagation, "private") && !strings.Contains(root.Optional, "shared") { 26 logrus.Warnf("The host root filesystem is mounted as %q. Setting child propagation to %q is not supported.", 27 root.Optional, propagation) 28 } 29 } 30 31 // warnSysctl verifies /proc/sys/kernel/unprivileged_userns_clone and /proc/sys/user/max_user_namespaces 32 func warnSysctl() { 33 uuc, err := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") 34 // The file exists only on distros with the "add sysctl to disallow unprivileged CLONE_NEWUSER by default" patch. 35 // (e.g. Debian and Arch) 36 if err == nil { 37 s := strings.TrimSpace(string(uuc)) 38 i, err := strconv.ParseInt(s, 10, 64) 39 if err != nil { 40 logrus.WithError(err).Warnf("Failed to parse /proc/sys/kernel/unprivileged_userns_clone (%q)", s) 41 } else if i == 0 { 42 logrus.Warn("/proc/sys/kernel/unprivileged_userns_clone needs to be set to 1.") 43 } 44 } 45 46 mun, err := os.ReadFile("/proc/sys/user/max_user_namespaces") 47 if err == nil { 48 s := strings.TrimSpace(string(mun)) 49 i, err := strconv.ParseInt(strings.TrimSpace(string(mun)), 10, 64) 50 if err != nil { 51 logrus.WithError(err).Warnf("Failed to parse /proc/sys/user/max_user_namespaces (%q)", s) 52 } else if i == 0 { 53 logrus.Warn("/proc/sys/user/max_user_namespaces needs to be set to non-zero.") 54 } else { 55 threshold := int64(1024) 56 if i < threshold { 57 logrus.Warnf("/proc/sys/user/max_user_namespaces=%d may be low. Consider setting to >= %d.", i, threshold) 58 } 59 } 60 } 61 } 62 63 func warnOnChildStartFailure(childStartErr error) { 64 if errors.Is(childStartErr, unix.EACCES) { 65 // apparmor_restrict_unprivileged_userns is available since Ubuntu 23.10. 66 // Enabled by default since Ubuntu 24.04. 67 // https://github.com/containerd/nerdctl/issues/2847 68 b, err := os.ReadFile("/proc/sys/kernel/apparmor_restrict_unprivileged_userns") 69 if err == nil { 70 s := strings.TrimSpace(string(b)) 71 i, err := strconv.ParseInt(s, 10, 64) 72 if err != nil { 73 logrus.WithError(err).Warnf("Failed to parse /proc/sys/kernel/apparmor_restrict_unprivileged_userns (%q)", s) 74 } else if i == 1 { 75 logrus.WithError(childStartErr).Warnf("This error might have happened because /proc/sys/kernel/apparmor_restrict_unprivileged_userns is set to 1") 76 selfExe, err := os.Executable() 77 if err != nil { 78 selfExe = "/usr/local/bin/rootlesskit" 79 logrus.WithError(err).Warnf("Failed to detect the path of the rootlesskit binary, assuming it to be %q", selfExe) 80 } 81 profileName := strings.ReplaceAll(strings.TrimPrefix(selfExe, "/"), "/", ".") 82 const tmpl = ` 83 84 ########## BEGIN ########## 85 cat <<EOT | sudo tee "/etc/apparmor.d/%s" 86 # ref: https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces 87 abi <abi/4.0>, 88 include <tunables/global> 89 90 %s flags=(unconfined) { 91 userns, 92 93 # Site-specific additions and overrides. See local/README for details. 94 include if exists <local/%s> 95 } 96 EOT 97 sudo systemctl restart apparmor.service 98 ########## END ########## 99 ` 100 logrus.Warnf("Hint: try running the following commands:\n"+tmpl+"\n", profileName, selfExe, profileName) 101 } 102 } 103 } 104 }