1package validate 2 3import ( 4 "errors" 5 "fmt" 6 "strings" 7 8 "github.com/opencontainers/runc/libcontainer/configs" 9) 10 11// rootlessEUID makes sure that the config can be applied when runc 12// is being executed as a non-root user (euid != 0) in the current user namespace. 13func (v *ConfigValidator) rootlessEUID(config *configs.Config) error { 14 if err := rootlessEUIDMappings(config); err != nil { 15 return err 16 } 17 if err := rootlessEUIDMount(config); err != nil { 18 return err 19 } 20 21 // XXX: We currently can't verify the user config at all, because 22 // configs.Config doesn't store the user-related configs. So this 23 // has to be verified by setupUser() in init_linux.go. 24 25 return nil 26} 27 28func hasIDMapping(id int, mappings []configs.IDMap) bool { 29 for _, m := range mappings { 30 if id >= m.ContainerID && id < m.ContainerID+m.Size { 31 return true 32 } 33 } 34 return false 35} 36 37func rootlessEUIDMappings(config *configs.Config) error { 38 if !config.Namespaces.Contains(configs.NEWUSER) { 39 return errors.New("rootless container requires user namespaces") 40 } 41 42 if len(config.UidMappings) == 0 { 43 return errors.New("rootless containers requires at least one UID mapping") 44 } 45 if len(config.GidMappings) == 0 { 46 return errors.New("rootless containers requires at least one GID mapping") 47 } 48 return nil 49} 50 51// mount verifies that the user isn't trying to set up any mounts they don't have 52// the rights to do. In addition, it makes sure that no mount has a `uid=` or 53// `gid=` option that doesn't resolve to root. 54func rootlessEUIDMount(config *configs.Config) error { 55 // XXX: We could whitelist allowed devices at this point, but I'm not 56 // convinced that's a good idea. The kernel is the best arbiter of 57 // access control. 58 59 for _, mount := range config.Mounts { 60 // Check that the options list doesn't contain any uid= or gid= entries 61 // that don't resolve to root. 62 for _, opt := range strings.Split(mount.Data, ",") { 63 if strings.HasPrefix(opt, "uid=") { 64 var uid int 65 n, err := fmt.Sscanf(opt, "uid=%d", &uid) 66 if n != 1 || err != nil { 67 // Ignore unknown mount options. 68 continue 69 } 70 if !hasIDMapping(uid, config.UidMappings) { 71 return errors.New("cannot specify uid= mount options for unmapped uid in rootless containers") 72 } 73 } 74 75 if strings.HasPrefix(opt, "gid=") { 76 var gid int 77 n, err := fmt.Sscanf(opt, "gid=%d", &gid) 78 if n != 1 || err != nil { 79 // Ignore unknown mount options. 80 continue 81 } 82 if !hasIDMapping(gid, config.GidMappings) { 83 return errors.New("cannot specify gid= mount options for unmapped gid in rootless containers") 84 } 85 } 86 } 87 } 88 89 return nil 90} 91