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