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 }