github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/configs/validate/validator.go (about) 1 package validate 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/opencontainers/runc/libcontainer/configs" 10 "github.com/opencontainers/runc/libcontainer/selinux" 11 ) 12 13 type Validator interface { 14 Validate(*configs.Config) error 15 } 16 17 func New() Validator { 18 return &ConfigValidator{} 19 } 20 21 type ConfigValidator struct { 22 } 23 24 func (v *ConfigValidator) Validate(config *configs.Config) error { 25 if err := v.rootfs(config); err != nil { 26 return err 27 } 28 if err := v.network(config); err != nil { 29 return err 30 } 31 if err := v.hostname(config); err != nil { 32 return err 33 } 34 if err := v.security(config); err != nil { 35 return err 36 } 37 if err := v.usernamespace(config); err != nil { 38 return err 39 } 40 if err := v.sysctl(config); err != nil { 41 return err 42 } 43 return nil 44 } 45 46 // rootfs validates if the rootfs is an absolute path and is not a symlink 47 // to the container's root filesystem. 48 func (v *ConfigValidator) rootfs(config *configs.Config) error { 49 if _, err := os.Stat(config.Rootfs); err != nil { 50 if os.IsNotExist(err) { 51 return fmt.Errorf("rootfs (%s) does not exist", config.Rootfs) 52 } 53 return err 54 } 55 cleaned, err := filepath.Abs(config.Rootfs) 56 if err != nil { 57 return err 58 } 59 if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil { 60 return err 61 } 62 if filepath.Clean(config.Rootfs) != cleaned { 63 return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs) 64 } 65 return nil 66 } 67 68 func (v *ConfigValidator) network(config *configs.Config) error { 69 if !config.Namespaces.Contains(configs.NEWNET) { 70 if len(config.Networks) > 0 || len(config.Routes) > 0 { 71 return fmt.Errorf("unable to apply network settings without a private NET namespace") 72 } 73 } 74 return nil 75 } 76 77 func (v *ConfigValidator) hostname(config *configs.Config) error { 78 if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) { 79 return fmt.Errorf("unable to set hostname without a private UTS namespace") 80 } 81 return nil 82 } 83 84 func (v *ConfigValidator) security(config *configs.Config) error { 85 // restrict sys without mount namespace 86 if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) && 87 !config.Namespaces.Contains(configs.NEWNS) { 88 return fmt.Errorf("unable to restrict sys entries without a private MNT namespace") 89 } 90 if config.ProcessLabel != "" && !selinux.SelinuxEnabled() { 91 return fmt.Errorf("selinux label is specified in config, but selinux is disabled or not supported") 92 } 93 94 return nil 95 } 96 97 func (v *ConfigValidator) usernamespace(config *configs.Config) error { 98 if config.Namespaces.Contains(configs.NEWUSER) { 99 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { 100 return fmt.Errorf("USER namespaces aren't enabled in the kernel") 101 } 102 } else { 103 if config.UidMappings != nil || config.GidMappings != nil { 104 return fmt.Errorf("User namespace mappings specified, but USER namespace isn't enabled in the config") 105 } 106 } 107 return nil 108 } 109 110 // sysctl validates that the specified sysctl keys are valid or not. 111 // /proc/sys isn't completely namespaced and depending on which namespaces 112 // are specified, a subset of sysctls are permitted. 113 func (v *ConfigValidator) sysctl(config *configs.Config) error { 114 validSysctlMap := map[string]bool{ 115 "kernel.msgmax": true, 116 "kernel.msgmnb": true, 117 "kernel.msgmni": true, 118 "kernel.sem": true, 119 "kernel.shmall": true, 120 "kernel.shmmax": true, 121 "kernel.shmmni": true, 122 "kernel.shm_rmid_forced": true, 123 } 124 125 for s := range config.Sysctl { 126 if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") { 127 if config.Namespaces.Contains(configs.NEWIPC) { 128 continue 129 } else { 130 return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s) 131 } 132 } 133 if strings.HasPrefix(s, "net.") { 134 if config.Namespaces.Contains(configs.NEWNET) { 135 if path := config.Namespaces.PathOf(configs.NEWNET); path != "" { 136 if err := checkHostNs(s, path); err != nil { 137 return err 138 } 139 } 140 continue 141 } else { 142 return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", s) 143 } 144 } 145 return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s) 146 } 147 148 return nil 149 } 150 151 // checkHostNs checks whether network sysctl is used in host namespace. 152 func checkHostNs(sysctlConfig string, path string) error { 153 var currentProcessNetns = "/proc/self/ns/net" 154 // readlink on the current processes network namespace 155 destOfCurrentProcess, err := os.Readlink(currentProcessNetns) 156 if err != nil { 157 return fmt.Errorf("read soft link %q error", currentProcessNetns) 158 } 159 // readlink on the path provided in the struct 160 destOfContainer, err := os.Readlink(path) 161 if err != nil { 162 return fmt.Errorf("read soft link %q error", path) 163 } 164 if destOfContainer == destOfCurrentProcess { 165 return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", sysctlConfig) 166 } 167 return nil 168 }