github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/container_validate.go (about)

     1  package specgen
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  
     7  	"github.com/containers/podman/v2/libpod/define"
     8  	"github.com/containers/podman/v2/pkg/rootless"
     9  	"github.com/containers/podman/v2/pkg/util"
    10  	"github.com/opencontainers/runtime-spec/specs-go"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  var (
    15  	// ErrInvalidSpecConfig describes an error that the given SpecGenerator is invalid
    16  	ErrInvalidSpecConfig = errors.New("invalid configuration")
    17  	// SystemDValues describes the only values that SystemD can be
    18  	SystemDValues = []string{"true", "false", "always"}
    19  	// SdNotifyModeValues describes the only values that SdNotifyMode can be
    20  	SdNotifyModeValues = []string{define.SdNotifyModeContainer, define.SdNotifyModeConmon, define.SdNotifyModeIgnore}
    21  	// ImageVolumeModeValues describes the only values that ImageVolumeMode can be
    22  	ImageVolumeModeValues = []string{"ignore", "tmpfs", "anonymous"}
    23  )
    24  
    25  func exclusiveOptions(opt1, opt2 string) error {
    26  	return errors.Errorf("%s and %s are mutually exclusive options", opt1, opt2)
    27  }
    28  
    29  // Validate verifies that the given SpecGenerator is valid and satisfies required
    30  // input for creating a container.
    31  func (s *SpecGenerator) Validate() error {
    32  
    33  	if rootless.IsRootless() {
    34  		if s.StaticIP != nil || s.StaticIPv6 != nil {
    35  			return ErrNoStaticIPRootless
    36  		}
    37  		if s.StaticMAC != nil {
    38  			return ErrNoStaticMACRootless
    39  		}
    40  	}
    41  
    42  	// Containers being added to a pod cannot have certain network attributes
    43  	// associated with them because those should be on the infra container.
    44  	if len(s.Pod) > 0 && s.NetNS.NSMode == FromPod {
    45  		if s.StaticIP != nil || s.StaticIPv6 != nil {
    46  			return errors.Wrap(define.ErrNetworkOnPodContainer, "static ip addresses must be defined when the pod is created")
    47  		}
    48  		if s.StaticMAC != nil {
    49  			return errors.Wrap(define.ErrNetworkOnPodContainer, "MAC addresses must be defined when the pod is created")
    50  		}
    51  		if len(s.CNINetworks) > 0 {
    52  			return errors.Wrap(define.ErrNetworkOnPodContainer, "networks must be defined when the pod is created")
    53  		}
    54  		if len(s.PortMappings) > 0 || s.PublishExposedPorts {
    55  			return errors.Wrap(define.ErrNetworkOnPodContainer, "published or exposed ports must be defined when the pod is created")
    56  		}
    57  	}
    58  
    59  	//
    60  	// ContainerBasicConfig
    61  	//
    62  	// Rootfs and Image cannot both populated
    63  	if len(s.ContainerStorageConfig.Image) > 0 && len(s.ContainerStorageConfig.Rootfs) > 0 {
    64  		return errors.Wrap(ErrInvalidSpecConfig, "both image and rootfs cannot be simultaneously")
    65  	}
    66  	// Cannot set hostname and utsns
    67  	if len(s.ContainerBasicConfig.Hostname) > 0 && !s.ContainerBasicConfig.UtsNS.IsPrivate() {
    68  		if s.ContainerBasicConfig.UtsNS.IsPod() {
    69  			return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when joining the pod UTS namespace")
    70  		}
    71  		return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when running in the host UTS namespace")
    72  	}
    73  	// systemd values must be true, false, or always
    74  	if len(s.ContainerBasicConfig.Systemd) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.Systemd), SystemDValues) {
    75  		return errors.Wrapf(ErrInvalidSpecConfig, "--systemd values must be one of %q", strings.Join(SystemDValues, ", "))
    76  	}
    77  	// sdnotify values must be container, conmon, or ignore
    78  	if len(s.ContainerBasicConfig.SdNotifyMode) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.SdNotifyMode), SdNotifyModeValues) {
    79  		return errors.Wrapf(ErrInvalidSpecConfig, "--sdnotify values must be one of %q", strings.Join(SdNotifyModeValues, ", "))
    80  	}
    81  
    82  	//
    83  	// ContainerStorageConfig
    84  	//
    85  	// rootfs and image cannot both be set
    86  	if len(s.ContainerStorageConfig.Image) > 0 && len(s.ContainerStorageConfig.Rootfs) > 0 {
    87  		return exclusiveOptions("rootfs", "image")
    88  	}
    89  	// imagevolumemode must be one of ignore, tmpfs, or anonymous if given
    90  	if len(s.ContainerStorageConfig.ImageVolumeMode) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerStorageConfig.ImageVolumeMode), ImageVolumeModeValues) {
    91  		return errors.Errorf("invalid ImageVolumeMode %q, value must be one of %s",
    92  			s.ContainerStorageConfig.ImageVolumeMode, strings.Join(ImageVolumeModeValues, ","))
    93  	}
    94  	// shmsize conflicts with IPC namespace
    95  	if s.ContainerStorageConfig.ShmSize != nil && !s.ContainerStorageConfig.IpcNS.IsPrivate() {
    96  		return errors.New("cannot set shmsize when running in the host IPC Namespace")
    97  	}
    98  
    99  	//
   100  	// ContainerSecurityConfig
   101  	//
   102  	// capadd and privileged are exclusive
   103  	if len(s.CapAdd) > 0 && s.Privileged {
   104  		return exclusiveOptions("CapAdd", "privileged")
   105  	}
   106  	// userns and idmappings conflict
   107  	if s.UserNS.IsPrivate() && s.IDMappings == nil {
   108  		return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace")
   109  	}
   110  
   111  	//
   112  	// ContainerCgroupConfig
   113  	//
   114  	//
   115  	// None for now
   116  
   117  	//
   118  	// ContainerNetworkConfig
   119  	//
   120  	// useimageresolveconf conflicts with dnsserver, dnssearch, dnsoption
   121  	if s.UseImageResolvConf {
   122  		if len(s.DNSServers) > 0 {
   123  			return exclusiveOptions("UseImageResolvConf", "DNSServer")
   124  		}
   125  		if len(s.DNSSearch) > 0 {
   126  			return exclusiveOptions("UseImageResolvConf", "DNSSearch")
   127  		}
   128  		if len(s.DNSOptions) > 0 {
   129  			return exclusiveOptions("UseImageResolvConf", "DNSOption")
   130  		}
   131  	}
   132  	// UseImageHosts and HostAdd are exclusive
   133  	if s.UseImageHosts && len(s.HostAdd) > 0 {
   134  		return exclusiveOptions("UseImageHosts", "HostAdd")
   135  	}
   136  
   137  	// TODO the specgen does not appear to handle this?  Should it
   138  	//switch config.Cgroup.Cgroups {
   139  	//case "disabled":
   140  	//	if addedResources {
   141  	//		return errors.New("cannot specify resource limits when cgroups are disabled is specified")
   142  	//	}
   143  	//	configSpec.Linux.Resources = &spec.LinuxResources{}
   144  	//case "enabled", "no-conmon", "":
   145  	//	// Do nothing
   146  	//default:
   147  	//	return errors.New("unrecognized option for cgroups; supported are 'default', 'disabled', 'no-conmon'")
   148  	//}
   149  	invalidUlimitFormatError := errors.New("invalid default ulimit definition must be form of type=soft:hard")
   150  	//set ulimits if not rootless
   151  	if len(s.ContainerResourceConfig.Rlimits) < 1 && !rootless.IsRootless() {
   152  		// Containers common defines this as something like nproc=4194304:4194304
   153  		tmpnproc := containerConfig.Ulimits()
   154  		var posixLimits []specs.POSIXRlimit
   155  		for _, limit := range tmpnproc {
   156  			limitSplit := strings.SplitN(limit, "=", 2)
   157  			if len(limitSplit) < 2 {
   158  				return errors.Wrapf(invalidUlimitFormatError, "missing = in %s", limit)
   159  			}
   160  			valueSplit := strings.SplitN(limitSplit[1], ":", 2)
   161  			if len(valueSplit) < 2 {
   162  				return errors.Wrapf(invalidUlimitFormatError, "missing : in %s", limit)
   163  			}
   164  			hard, err := strconv.Atoi(valueSplit[0])
   165  			if err != nil {
   166  				return err
   167  			}
   168  			soft, err := strconv.Atoi(valueSplit[1])
   169  			if err != nil {
   170  				return err
   171  			}
   172  			posixLimit := specs.POSIXRlimit{
   173  				Type: limitSplit[0],
   174  				Hard: uint64(hard),
   175  				Soft: uint64(soft),
   176  			}
   177  			posixLimits = append(posixLimits, posixLimit)
   178  		}
   179  		s.ContainerResourceConfig.Rlimits = posixLimits
   180  	}
   181  	// Namespaces
   182  	if err := s.UtsNS.validate(); err != nil {
   183  		return err
   184  	}
   185  	if err := s.IpcNS.validate(); err != nil {
   186  		return err
   187  	}
   188  	if err := s.PidNS.validate(); err != nil {
   189  		return err
   190  	}
   191  	if err := s.CgroupNS.validate(); err != nil {
   192  		return err
   193  	}
   194  	if err := validateUserNS(&s.UserNS); err != nil {
   195  		return err
   196  	}
   197  
   198  	// Set defaults if network info is not provided
   199  	if s.NetNS.NSMode == "" {
   200  		s.NetNS.NSMode = Bridge
   201  		if rootless.IsRootless() {
   202  			s.NetNS.NSMode = Slirp
   203  		}
   204  	}
   205  	if err := validateNetNS(&s.NetNS); err != nil {
   206  		return err
   207  	}
   208  	return nil
   209  }