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