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  }