mirror of
https://github.com/opencontainers/runc.git
synced 2026-04-24 16:39:52 +08:00
89e59902c4
Brought to you by modernize -fix -test ./... Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
88 lines
2.6 KiB
Go
88 lines
2.6 KiB
Go
package validate
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
)
|
|
|
|
// rootlessEUIDCheck makes sure that the config can be applied when runc
|
|
// is being executed as a non-root user (euid != 0) in the current user namespace.
|
|
func rootlessEUIDCheck(config *configs.Config) error {
|
|
if !config.RootlessEUID {
|
|
return nil
|
|
}
|
|
if err := rootlessEUIDMappings(config); err != nil {
|
|
return err
|
|
}
|
|
if err := rootlessEUIDMount(config); err != nil {
|
|
return err
|
|
}
|
|
|
|
// XXX: We currently can't verify the user config at all, because
|
|
// configs.Config doesn't store the user-related configs. So this
|
|
// has to be verified by setupUser() in init_linux.go.
|
|
|
|
return nil
|
|
}
|
|
|
|
func rootlessEUIDMappings(config *configs.Config) error {
|
|
if !config.Namespaces.Contains(configs.NEWUSER) {
|
|
return errors.New("rootless container requires user namespaces")
|
|
}
|
|
// We only require mappings if we are not joining another userns.
|
|
if config.Namespaces.IsPrivate(configs.NEWUSER) {
|
|
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
|
|
}
|
|
|
|
// rootlessEUIDMount verifies that all mounts have valid uid=/gid= options,
|
|
// i.e. their arguments has proper ID mappings.
|
|
func rootlessEUIDMount(config *configs.Config) error {
|
|
// XXX: We could whitelist allowed devices at this point, but I'm not
|
|
// convinced that's a good idea. The kernel is the best arbiter of
|
|
// access control.
|
|
|
|
// Check that the options list doesn't contain any uid= or gid= entries
|
|
// that don't resolve to root.
|
|
for _, mount := range config.Mounts {
|
|
// Look for a common substring; skip further processing
|
|
// if there can't be any uid= or gid= options.
|
|
if !strings.Contains(mount.Data, "id=") {
|
|
continue
|
|
}
|
|
for opt := range strings.SplitSeq(mount.Data, ",") {
|
|
if str, ok := strings.CutPrefix(opt, "uid="); ok {
|
|
uid, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
// Ignore unknown mount options.
|
|
continue
|
|
}
|
|
if _, err := config.HostUID(uid); err != nil {
|
|
return fmt.Errorf("cannot specify %s mount option for rootless container: %w", opt, err)
|
|
}
|
|
} else if str, ok := strings.CutPrefix(opt, "gid="); ok {
|
|
gid, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
// Ignore unknown mount options.
|
|
continue
|
|
}
|
|
if _, err := config.HostGID(gid); err != nil {
|
|
return fmt.Errorf("cannot specify %s mount option for rootless container: %w", opt, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|