mirror of
https://github.com/containers/podman.git
synced 2025-05-20 00:27:03 +08:00

Because /etc/hosts is shared for all containers with a shared network namespace you should not be able to add hosts from a joined container. Only the primary netns container can set the hosts. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
202 lines
7.3 KiB
Go
202 lines
7.3 KiB
Go
package specgen
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/containers/podman/v4/libpod/define"
|
|
"github.com/containers/podman/v4/pkg/rootless"
|
|
"github.com/containers/podman/v4/pkg/util"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
// ErrInvalidSpecConfig describes an error that the given SpecGenerator is invalid
|
|
ErrInvalidSpecConfig = errors.New("invalid configuration")
|
|
// SystemDValues describes the only values that SystemD can be
|
|
SystemDValues = []string{"true", "false", "always"}
|
|
// SdNotifyModeValues describes the only values that SdNotifyMode can be
|
|
SdNotifyModeValues = []string{define.SdNotifyModeContainer, define.SdNotifyModeConmon, define.SdNotifyModeIgnore}
|
|
// ImageVolumeModeValues describes the only values that ImageVolumeMode can be
|
|
ImageVolumeModeValues = []string{"ignore", "tmpfs", "anonymous"}
|
|
)
|
|
|
|
func exclusiveOptions(opt1, opt2 string) error {
|
|
return errors.Errorf("%s and %s are mutually exclusive options", opt1, opt2)
|
|
}
|
|
|
|
// Validate verifies that the given SpecGenerator is valid and satisfies required
|
|
// input for creating a container.
|
|
func (s *SpecGenerator) Validate() error {
|
|
// Containers being added to a pod cannot have certain network attributes
|
|
// associated with them because those should be on the infra container.
|
|
if len(s.Pod) > 0 && s.NetNS.NSMode == FromPod {
|
|
if len(s.Networks) > 0 {
|
|
return errors.Wrap(define.ErrNetworkOnPodContainer, "networks must be defined when the pod is created")
|
|
}
|
|
if len(s.PortMappings) > 0 || s.PublishExposedPorts {
|
|
return errors.Wrap(define.ErrNetworkOnPodContainer, "published or exposed ports must be defined when the pod is created")
|
|
}
|
|
if len(s.HostAdd) > 0 {
|
|
return errors.Wrap(define.ErrNetworkOnPodContainer, "extra host entries must be specified on the pod")
|
|
}
|
|
}
|
|
|
|
if s.NetNS.IsContainer() && len(s.HostAdd) > 0 {
|
|
return errors.Wrap(ErrInvalidSpecConfig, "cannot set extra host entries when the container is joined to another containers network namespace")
|
|
}
|
|
|
|
//
|
|
// ContainerBasicConfig
|
|
//
|
|
// Rootfs and Image cannot both populated
|
|
if len(s.ContainerStorageConfig.Image) > 0 && len(s.ContainerStorageConfig.Rootfs) > 0 {
|
|
return errors.Wrap(ErrInvalidSpecConfig, "both image and rootfs cannot be simultaneously")
|
|
}
|
|
// Cannot set hostname and utsns
|
|
if len(s.ContainerBasicConfig.Hostname) > 0 && !s.ContainerBasicConfig.UtsNS.IsPrivate() {
|
|
if s.ContainerBasicConfig.UtsNS.IsPod() {
|
|
return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when joining the pod UTS namespace")
|
|
}
|
|
return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when running in the host UTS namespace")
|
|
}
|
|
// systemd values must be true, false, or always
|
|
if len(s.ContainerBasicConfig.Systemd) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.Systemd), SystemDValues) {
|
|
return errors.Wrapf(ErrInvalidSpecConfig, "--systemd values must be one of %q", strings.Join(SystemDValues, ", "))
|
|
}
|
|
// sdnotify values must be container, conmon, or ignore
|
|
if len(s.ContainerBasicConfig.SdNotifyMode) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.SdNotifyMode), SdNotifyModeValues) {
|
|
return errors.Wrapf(ErrInvalidSpecConfig, "--sdnotify values must be one of %q", strings.Join(SdNotifyModeValues, ", "))
|
|
}
|
|
|
|
//
|
|
// ContainerStorageConfig
|
|
//
|
|
// rootfs and image cannot both be set
|
|
if len(s.ContainerStorageConfig.Image) > 0 && len(s.ContainerStorageConfig.Rootfs) > 0 {
|
|
return exclusiveOptions("rootfs", "image")
|
|
}
|
|
// imagevolumemode must be one of ignore, tmpfs, or anonymous if given
|
|
if len(s.ContainerStorageConfig.ImageVolumeMode) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerStorageConfig.ImageVolumeMode), ImageVolumeModeValues) {
|
|
return errors.Errorf("invalid ImageVolumeMode %q, value must be one of %s",
|
|
s.ContainerStorageConfig.ImageVolumeMode, strings.Join(ImageVolumeModeValues, ","))
|
|
}
|
|
// shmsize conflicts with IPC namespace
|
|
if s.ContainerStorageConfig.ShmSize != nil && (s.ContainerStorageConfig.IpcNS.IsHost() || s.ContainerStorageConfig.IpcNS.IsNone()) {
|
|
return errors.Errorf("cannot set shmsize when running in the %s IPC Namespace", s.ContainerStorageConfig.IpcNS)
|
|
}
|
|
|
|
//
|
|
// ContainerSecurityConfig
|
|
//
|
|
// userns and idmappings conflict
|
|
if s.UserNS.IsPrivate() && s.IDMappings == nil {
|
|
return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace")
|
|
}
|
|
|
|
//
|
|
// ContainerCgroupConfig
|
|
//
|
|
//
|
|
// None for now
|
|
|
|
//
|
|
// ContainerNetworkConfig
|
|
//
|
|
// useimageresolveconf conflicts with dnsserver, dnssearch, dnsoption
|
|
if s.UseImageResolvConf {
|
|
if len(s.DNSServers) > 0 {
|
|
return exclusiveOptions("UseImageResolvConf", "DNSServer")
|
|
}
|
|
if len(s.DNSSearch) > 0 {
|
|
return exclusiveOptions("UseImageResolvConf", "DNSSearch")
|
|
}
|
|
if len(s.DNSOptions) > 0 {
|
|
return exclusiveOptions("UseImageResolvConf", "DNSOption")
|
|
}
|
|
}
|
|
// UseImageHosts and HostAdd are exclusive
|
|
if s.UseImageHosts && len(s.HostAdd) > 0 {
|
|
return exclusiveOptions("UseImageHosts", "HostAdd")
|
|
}
|
|
|
|
// TODO the specgen does not appear to handle this? Should it
|
|
//switch config.Cgroup.Cgroups {
|
|
//case "disabled":
|
|
// if addedResources {
|
|
// return errors.New("cannot specify resource limits when cgroups are disabled is specified")
|
|
// }
|
|
// configSpec.Linux.Resources = &spec.LinuxResources{}
|
|
//case "enabled", "no-conmon", "":
|
|
// // Do nothing
|
|
//default:
|
|
// return errors.New("unrecognized option for cgroups; supported are 'default', 'disabled', 'no-conmon'")
|
|
//}
|
|
invalidUlimitFormatError := errors.New("invalid default ulimit definition must be form of type=soft:hard")
|
|
//set ulimits if not rootless
|
|
if len(s.ContainerResourceConfig.Rlimits) < 1 && !rootless.IsRootless() {
|
|
// Containers common defines this as something like nproc=4194304:4194304
|
|
tmpnproc := containerConfig.Ulimits()
|
|
var posixLimits []specs.POSIXRlimit
|
|
for _, limit := range tmpnproc {
|
|
limitSplit := strings.SplitN(limit, "=", 2)
|
|
if len(limitSplit) < 2 {
|
|
return errors.Wrapf(invalidUlimitFormatError, "missing = in %s", limit)
|
|
}
|
|
valueSplit := strings.SplitN(limitSplit[1], ":", 2)
|
|
if len(valueSplit) < 2 {
|
|
return errors.Wrapf(invalidUlimitFormatError, "missing : in %s", limit)
|
|
}
|
|
hard, err := strconv.Atoi(valueSplit[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
soft, err := strconv.Atoi(valueSplit[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
posixLimit := specs.POSIXRlimit{
|
|
Type: limitSplit[0],
|
|
Hard: uint64(hard),
|
|
Soft: uint64(soft),
|
|
}
|
|
posixLimits = append(posixLimits, posixLimit)
|
|
}
|
|
s.ContainerResourceConfig.Rlimits = posixLimits
|
|
}
|
|
// Namespaces
|
|
if err := s.UtsNS.validate(); err != nil {
|
|
return err
|
|
}
|
|
if err := validateIPCNS(&s.IpcNS); err != nil {
|
|
return err
|
|
}
|
|
if err := s.PidNS.validate(); err != nil {
|
|
return err
|
|
}
|
|
if err := s.CgroupNS.validate(); err != nil {
|
|
return err
|
|
}
|
|
if err := validateUserNS(&s.UserNS); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set defaults if network info is not provided
|
|
if s.NetNS.NSMode == "" {
|
|
s.NetNS.NSMode = Bridge
|
|
if rootless.IsRootless() {
|
|
s.NetNS.NSMode = Slirp
|
|
}
|
|
}
|
|
if err := validateNetNS(&s.NetNS); err != nil {
|
|
return err
|
|
}
|
|
if s.NetNS.NSMode != Bridge && len(s.Networks) > 0 {
|
|
// Note that we also get the ip and mac in the networks map
|
|
return errors.New("Networks and static ip/mac address can only be used with Bridge mode networking")
|
|
}
|
|
|
|
return nil
|
|
}
|