diff --git a/pkg/unshare/unshare_linux.go b/pkg/unshare/unshare_linux.go index c86390bd38..8793b4a8b2 100644 --- a/pkg/unshare/unshare_linux.go +++ b/pkg/unshare/unshare_linux.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" "os/exec" "os/signal" @@ -387,10 +388,42 @@ const ( UsernsEnvName = "_CONTAINERS_USERNS_CONFIGURED" ) +// hasFullUsersMappings checks whether the current user namespace has all the IDs mapped. +// current user namespace +func hasFullUsersMappings() (bool, error) { + content, err := ioutil.ReadFile("/proc/self/uid_map") + if err != nil { + return false, err + } + // Both runc and crun check whether the current process is in a user namespace + // by looking up 4294967295 in /proc/self/uid_map. If the mappings would be + // copied as they are, the check in the OCI runtimes would fail. So just split + // it in two different ranges. + return bytes.Contains(content, []byte("4294967295")), nil +} + // IsRootless tells us if we are running in rootless mode func IsRootless() bool { isRootlessOnce.Do(func() { isRootless = getRootlessUID() != 0 || getenv(UsernsEnvName) != "" + if !isRootless { + hasCapSysAdmin, err := HasCapSysAdmin() + if err != nil { + logrus.Warnf("Failed to read CAP_SYS_ADMIN presence for the current process") + } + if err == nil && !hasCapSysAdmin { + isRootless = true + } + } + if !isRootless { + hasMappings, err := hasFullUsersMappings() + if err != nil { + logrus.Warnf("Failed to read current user namespace mappings") + } + if err == nil && !hasMappings { + isRootless = true + } + } }) return isRootless }