123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- package validate
- import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "github.com/opencontainers/runc/libcontainer/configs"
- )
- type Validator interface {
- Validate(*configs.Config) error
- }
- func New() Validator {
- return &ConfigValidator{}
- }
- type ConfigValidator struct {
- }
- func (v *ConfigValidator) Validate(config *configs.Config) error {
- if err := v.rootfs(config); err != nil {
- return err
- }
- if err := v.network(config); err != nil {
- return err
- }
- if err := v.hostname(config); err != nil {
- return err
- }
- if err := v.security(config); err != nil {
- return err
- }
- if err := v.usernamespace(config); err != nil {
- return err
- }
- if err := v.sysctl(config); err != nil {
- return err
- }
- return nil
- }
- // rootfs validates if the rootfs is an absolute path and is not a symlink
- // to the container's root filesystem.
- func (v *ConfigValidator) rootfs(config *configs.Config) error {
- cleaned, err := filepath.Abs(config.Rootfs)
- if err != nil {
- return err
- }
- if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil {
- return err
- }
- if filepath.Clean(config.Rootfs) != cleaned {
- return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs)
- }
- return nil
- }
- func (v *ConfigValidator) network(config *configs.Config) error {
- if !config.Namespaces.Contains(configs.NEWNET) {
- if len(config.Networks) > 0 || len(config.Routes) > 0 {
- return fmt.Errorf("unable to apply network settings without a private NET namespace")
- }
- }
- return nil
- }
- func (v *ConfigValidator) hostname(config *configs.Config) error {
- if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) {
- return fmt.Errorf("unable to set hostname without a private UTS namespace")
- }
- return nil
- }
- func (v *ConfigValidator) security(config *configs.Config) error {
- // restrict sys without mount namespace
- if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) &&
- !config.Namespaces.Contains(configs.NEWNS) {
- return fmt.Errorf("unable to restrict sys entries without a private MNT namespace")
- }
- return nil
- }
- func (v *ConfigValidator) usernamespace(config *configs.Config) error {
- if config.Namespaces.Contains(configs.NEWUSER) {
- if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
- return fmt.Errorf("USER namespaces aren't enabled in the kernel")
- }
- } else {
- if config.UidMappings != nil || config.GidMappings != nil {
- return fmt.Errorf("User namespace mappings specified, but USER namespace isn't enabled in the config")
- }
- }
- return nil
- }
- // sysctl validates that the specified sysctl keys are valid or not.
- // /proc/sys isn't completely namespaced and depending on which namespaces
- // are specified, a subset of sysctls are permitted.
- func (v *ConfigValidator) sysctl(config *configs.Config) error {
- validSysctlMap := map[string]bool{
- "kernel.msgmax": true,
- "kernel.msgmnb": true,
- "kernel.msgmni": true,
- "kernel.sem": true,
- "kernel.shmall": true,
- "kernel.shmmax": true,
- "kernel.shmmni": true,
- "kernel.shm_rmid_forced": true,
- }
- for s := range config.Sysctl {
- if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") {
- if config.Namespaces.Contains(configs.NEWIPC) {
- continue
- } else {
- return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s)
- }
- }
- if strings.HasPrefix(s, "net.") {
- if config.Namespaces.Contains(configs.NEWNET) {
- continue
- } else {
- return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", s)
- }
- }
- return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
- }
- return nil
- }
|