mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
*: actually support joining a userns with a new container
Our handling for name space paths with user namespaces has been broken for a long time. In particular, the need to parse /proc/self/*id_map in quite a few places meant that we would treat userns configurations that had a namespace path as if they were a userns configuration without mappings, resulting in errors. The primary issue was down to the id translation helper functions, which could only handle configurations that had explicit mappings. Obviously, when joining a user namespace we need to map the ids but figuring out the correct mapping is non-trivial in comparison. In order to get the mapping, you need to read /proc/<pid>/*id_map of a process inside the userns -- while most userns paths will be of the form /proc/<pid>/ns/user (and we have a fast-path for this case), this is not guaranteed and thus it is necessary to spawn a process inside the container and read its /proc/<pid>/*id_map files in the general case. As Go does not allow us spawn a subprocess into a target userns, we have to use CGo to fork a sub-process which does the setns(2). To be honest, this is a little dodgy in regards to POSIX signal-safety(7) but since we do no allocations and we are executing in the forked context from a Go program (not a C program), it should be okay. The other alternative would be to do an expensive re-exec (a-la nsexec which would make several other bits of runc more complicated), or to use nsenter(1) which might not exist on the system and is less than ideal. Because we need to logically remap users quite a few times in runc (including in "runc init", where joining the namespace is not feasable), we cache the mapping inside the libcontainer config struct. A future patch will make sure that we stop allow invalid user configurations where a mapping is specified as well as a userns path to join. Finally, add an integration test to make sure we don't regress this again. Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -28,25 +29,18 @@ func rootlessEUIDCheck(config *configs.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasIDMapping(id int, mappings []configs.IDMap) bool {
|
||||
for _, m := range mappings {
|
||||
if id >= m.ContainerID && id < m.ContainerID+m.Size {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func rootlessEUIDMappings(config *configs.Config) error {
|
||||
if !config.Namespaces.Contains(configs.NEWUSER) {
|
||||
return errors.New("rootless container requires user namespaces")
|
||||
}
|
||||
|
||||
if len(config.UIDMappings) == 0 {
|
||||
return errors.New("rootless containers requires at least one UID mapping")
|
||||
}
|
||||
if len(config.GIDMappings) == 0 {
|
||||
return errors.New("rootless containers requires at least one GID mapping")
|
||||
// We only require mappings if we are not joining another userns.
|
||||
if path := config.Namespaces.PathOf(configs.NEWUSER); path == "" {
|
||||
if len(config.UIDMappings) == 0 {
|
||||
return errors.New("rootless containers requires at least one UID mapping")
|
||||
}
|
||||
if len(config.GIDMappings) == 0 {
|
||||
return errors.New("rootless containers requires at least one GID mapping")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -68,8 +62,8 @@ func rootlessEUIDMount(config *configs.Config) error {
|
||||
// Ignore unknown mount options.
|
||||
continue
|
||||
}
|
||||
if !hasIDMapping(uid, config.UIDMappings) {
|
||||
return errors.New("cannot specify uid= mount options for unmapped uid in rootless containers")
|
||||
if _, err := config.HostUID(uid); err != nil {
|
||||
return fmt.Errorf("cannot specify uid=%d mount option for rootless container: %w", uid, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +73,8 @@ func rootlessEUIDMount(config *configs.Config) error {
|
||||
// Ignore unknown mount options.
|
||||
continue
|
||||
}
|
||||
if !hasIDMapping(gid, config.GIDMappings) {
|
||||
return errors.New("cannot specify gid= mount options for unmapped gid in rootless containers")
|
||||
if _, err := config.HostGID(gid); err != nil {
|
||||
return fmt.Errorf("cannot specify gid=%d mount option for rootless container: %w", gid, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user