mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 14:13:58 +08:00
48006d0007
1. Fix function docs. In particular, remove the part
which is not true ("verifies that the user isn't trying to set up any
mounts they don't have the rights to do"), and fix the part that
says "that doesn't resolve to root" (which is no longer true since
commit d8b669400a).
2. Replace fmt.Sscanf (which is slow and does lots of allocations)
with strings.TrimPrefix and strconv.Atoi.
3. Add a benchmark for rootlessEUIDMount. Comparing the old and the new
implementations:
name old time/op new time/op delta
RootlessEUIDMount-4 1.01µs ± 2% 0.16µs ± 1% -84.15% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
RootlessEUIDMount-4 224B ± 0% 80B ± 0% -64.29% (p=0.008 n=5+5)
name old allocs/op new allocs/op delta
RootlessEUIDMount-4 7.00 ± 0% 1.00 ± 0% -85.71% (p=0.008 n=5+5)
Note this code is already tested (in rootless_test.go).
Fixes: d8b669400a
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
91 lines
2.5 KiB
Go
91 lines
2.5 KiB
Go
package validate
|
|
|
|
import (
|
|
"errors"
|
|
"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 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")
|
|
}
|
|
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.
|
|
|
|
for _, mount := range config.Mounts {
|
|
// Check that the options list doesn't contain any uid= or gid= entries
|
|
// that don't resolve to root.
|
|
for _, opt := range strings.Split(mount.Data, ",") {
|
|
if str := strings.TrimPrefix(opt, "uid="); len(str) < len(opt) {
|
|
uid, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
// 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 str := strings.TrimPrefix(opt, "gid="); len(str) < len(opt) {
|
|
gid, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
// Ignore unknown mount options.
|
|
continue
|
|
}
|
|
if !hasIDMapping(gid, config.GidMappings) {
|
|
return errors.New("cannot specify gid= mount options for unmapped gid in rootless containers")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|