diff options
Diffstat (limited to 'pkg/specgen/generate')
22 files changed, 945 insertions, 618 deletions
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 8cfac924b..c18b5654a 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "os" + "strconv" "strings" "time" @@ -17,9 +18,8 @@ import ( envLib "github.com/containers/podman/v4/pkg/env" "github.com/containers/podman/v4/pkg/signal" "github.com/containers/podman/v4/pkg/specgen" - spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/openshift/imagebuilder" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) (*libimage.Image, string, *libimage.ImageData, error) { @@ -115,7 +115,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // Get Default Environment from containers.conf defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnvEx(s.EnvHost, s.HTTPProxy)) if err != nil { - return nil, fmt.Errorf("error parsing fields in containers.conf: %w", err) + return nil, fmt.Errorf("parsing fields in containers.conf: %w", err) } var envs map[string]string @@ -130,6 +130,17 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat defaultEnvs = envLib.Join(envLib.DefaultEnvVariables(), envLib.Join(defaultEnvs, envs)) } + for _, e := range s.EnvMerge { + processedWord, err := imagebuilder.ProcessWord(e, envLib.Slice(defaultEnvs)) + if err != nil { + return nil, fmt.Errorf("unable to process variables for --env-merge %s: %w", e, err) + } + splitWord := strings.Split(processedWord, "=") + if _, ok := defaultEnvs[splitWord[0]]; ok { + defaultEnvs[splitWord[0]] = splitWord[1] + } + } + for _, e := range s.UnsetEnv { delete(defaultEnvs, e) } @@ -139,10 +150,8 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } // First transform the os env into a map. We need it for the labels later in // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return nil, fmt.Errorf("error parsing host environment variables: %w", err) - } + osEnv := envLib.Map(os.Environ()) + // Caller Specified defaults if s.EnvHost { defaultEnvs = envLib.Join(defaultEnvs, osEnv) @@ -191,16 +200,24 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // - "container" denotes the container should join the VM of the SandboxID // (the infra container) if len(s.Pod) > 0 { - annotations[ann.SandboxID] = s.Pod + p, err := r.LookupPod(s.Pod) + if err != nil { + return nil, err + } + sandboxID := p.ID() + if p.HasInfraContainer() { + infra, err := p.InfraContainer() + if err != nil { + return nil, err + } + sandboxID = infra.ID() + } + annotations[ann.SandboxID] = sandboxID annotations[ann.ContainerType] = ann.ContainerTypeContainer // Check if this is an init-ctr and if so, check if // the pod is running. we do not want to add init-ctrs to // a running pod because it creates confusion for us. if len(s.InitContainerType) > 0 { - p, err := r.LookupPod(s.Pod) - if err != nil { - return nil, err - } containerStatuses, err := p.Status() if err != nil { return nil, err @@ -254,19 +271,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } // If caller did not specify Pids Limits load default - if s.ResourceLimits == nil || s.ResourceLimits.Pids == nil { - if s.CgroupsMode != "disabled" { - limit := rtc.PidsLimit() - if limit != 0 { - if s.ResourceLimits == nil { - s.ResourceLimits = &spec.LinuxResources{} - } - s.ResourceLimits.Pids = &spec.LinuxPids{ - Limit: limit, - } - } - } - } + s.InitResourceLimits(rtc) if s.LogConfiguration == nil { s.LogConfiguration = &specgen.LogConfig{} @@ -338,9 +343,21 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s conf.Systemd = tmpSystemd conf.Mounts = tmpMounts - if conf.Spec != nil && conf.Spec.Linux != nil && conf.Spec.Linux.Resources != nil { - if specg.ResourceLimits == nil { - specg.ResourceLimits = conf.Spec.Linux.Resources + if conf.Spec != nil { + if conf.Spec.Linux != nil && conf.Spec.Linux.Resources != nil { + if specg.ResourceLimits == nil { + specg.ResourceLimits = conf.Spec.Linux.Resources + } + } + if conf.Spec.Process != nil && conf.Spec.Process.Env != nil { + env := make(map[string]string) + for _, entry := range conf.Spec.Process.Env { + split := strings.Split(entry, "=") + if len(split) == 2 { + env[split[0]] = split[1] + } + } + specg.Env = env } } @@ -487,71 +504,40 @@ func mapSecurityConfig(c *libpod.ContainerConfig, s *specgen.SpecGenerator) { s.HostUsers = c.HostUsers } -// FinishThrottleDevices takes the temporary representation of the throttle -// devices in the specgen and looks up the major and major minors. it then -// sets the throttle devices proper in the specgen -func FinishThrottleDevices(s *specgen.SpecGenerator) error { - if s.ResourceLimits == nil { - s.ResourceLimits = &spec.LinuxResources{} - } - if bps := s.ThrottleReadBpsDevice; len(bps) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range bps { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) - } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO) +// Check name looks for existing containers/pods with the same name, and modifies the given string until a new name is found +func CheckName(rt *libpod.Runtime, n string, kind bool) string { + switch { + case strings.Contains(n, "-clone"): + ind := strings.Index(n, "-clone") + 6 + num, err := strconv.Atoi(n[ind:]) + if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here. + if kind { + _, err = rt.LookupContainer(n + "1") + } else { + _, err = rt.LookupPod(n + "1") } - s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v) - } - } - if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range bps { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) - } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) - } - } - if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range iops { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) + + if err != nil { + n += "1" + break } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) - } - } - if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} + } else { + n = n[0:ind] } - for k, v := range iops { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) + err = nil + count := num + for err == nil { + count++ + tempN := n + strconv.Itoa(count) + if kind { + _, err = rt.LookupContainer(tempN) + } else { + _, err = rt.LookupPod(tempN) } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) } + n += strconv.Itoa(count) + default: + n += "-clone" } - return nil + return n } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 8334d386f..6ef5ca79c 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "path/filepath" "strings" @@ -35,7 +36,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if s.Pod != "" { pod, err = rt.LookupPod(s.Pod) if err != nil { - return nil, nil, nil, fmt.Errorf("error retrieving pod %s: %w", s.Pod, err) + return nil, nil, nil, fmt.Errorf("retrieving pod %s: %w", s.Pod, err) } if pod.HasInfraContainer() { infra, err = pod.InfraContainer() @@ -55,7 +56,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } } - if err := FinishThrottleDevices(s); err != nil { + if err := specgen.FinishThrottleDevices(s); err != nil { return nil, nil, nil, err } @@ -343,7 +344,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l if s.StopSignal == nil { stopSignal, err := util.ParseSignal("RTMIN+3") if err != nil { - return nil, fmt.Errorf("error parsing systemd signal: %w", err) + return nil, fmt.Errorf("parsing systemd signal: %w", err) } s.StopSignal = &stopSignal } @@ -352,7 +353,13 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l } if len(s.SdNotifyMode) > 0 { options = append(options, libpod.WithSdNotifyMode(s.SdNotifyMode)) + if s.SdNotifyMode != define.SdNotifyModeIgnore { + if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { + options = append(options, libpod.WithSdNotifySocket(notify)) + } + } } + if pod != nil { logrus.Debugf("adding container to pod %s", pod.Name()) options = append(options, rt.WithPod(pod)) @@ -380,9 +387,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l var vols []*libpod.ContainerNamedVolume for _, v := range volumes { vols = append(vols, &libpod.ContainerNamedVolume{ - Name: v.Name, - Dest: v.Dest, - Options: v.Options, + Name: v.Name, + Dest: v.Dest, + Options: v.Options, + IsAnonymous: v.IsAnonymous, }) } options = append(options, libpod.WithNamedVolumes(vols)) @@ -507,6 +515,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l logrus.Debugf("New container has a health check") } + if s.ContainerHealthCheckConfig.HealthCheckOnFailureAction != define.HealthCheckOnFailureActionNone { + options = append(options, libpod.WithHealthCheckOnFailureAction(s.ContainerHealthCheckConfig.HealthCheckOnFailureAction)) + } + if len(s.Secrets) != 0 { manager, err := rt.SecretsManager() if err != nil { diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e9abf419b..7d85fd2f3 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -7,6 +7,7 @@ import ( "fmt" "math" "net" + "os" "regexp" "runtime" "strconv" @@ -26,6 +27,7 @@ import ( "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/api/resource" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/specgen/generate" + systemdDefine "github.com/containers/podman/v4/pkg/systemd/define" "github.com/containers/podman/v4/pkg/util" "github.com/docker/docker/pkg/system" "github.com/docker/go-units" @@ -205,12 +207,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener s.SeccompProfilePath = opts.SeccompPaths.FindForContainer(opts.Container.Name) s.ResourceLimits = &spec.LinuxResources{} - milliCPU, err := quantityToInt64(opts.Container.Resources.Limits.Cpu()) - if err != nil { - return nil, fmt.Errorf("failed to set CPU quota: %w", err) - } + milliCPU := opts.Container.Resources.Limits.Cpu().MilliValue() if milliCPU > 0 { - period, quota := util.CoresToPeriodAndQuota(float64(milliCPU)) + period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000) s.ResourceLimits.CPU = &spec.LinuxCPU{ Quota: "a, Period: &period, @@ -357,8 +356,11 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // a selinux mount option exists for it for k, v := range opts.Annotations { // Make sure the z/Z option is not already there (from editing the YAML) - if strings.Replace(k, define.BindMountPrefix, "", 1) == volumeSource.Source && !cutil.StringInSlice("z", options) && !cutil.StringInSlice("Z", options) { - options = append(options, v) + if k == define.BindMountPrefix { + lastIndex := strings.LastIndex(v, ":") + if v[:lastIndex] == volumeSource.Source && !cutil.StringInSlice("z", options) && !cutil.StringInSlice("Z", options) { + options = append(options, v[lastIndex+1:]) + } } } mount := spec.Mount{ @@ -406,8 +408,15 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Name: volumeSource.Source, Options: options, } - s.Volumes = append(s.Volumes, &secretVolume) + case KubeVolumeTypeEmptyDir: + emptyDirVolume := specgen.NamedVolume{ + Dest: volume.MountPath, + Name: volumeSource.Source, + Options: options, + IsAnonymous: true, + } + s.Volumes = append(s.Volumes, &emptyDirVolume) default: return nil, errors.New("unsupported volume source type") } @@ -435,6 +444,12 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } } + // Make sure the container runs in a systemd unit which is + // stored as a label at container creation. + if unit := os.Getenv(systemdDefine.EnvVariable); unit != "" { + s.Labels[systemdDefine.EnvVariable] = unit + } + return s, nil } diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go index 470c0c39c..ec0dc4bcd 100644 --- a/pkg/specgen/generate/kube/play_test.go +++ b/pkg/specgen/generate/kube/play_test.go @@ -24,11 +24,15 @@ func createSecrets(t *testing.T, d string) *secrets.SecretsManager { "path": d, } + storeOpts := secrets.StoreOptions{ + DriverOpts: driverOpts, + } + for _, s := range k8sSecrets { data, err := json.Marshal(s.Data) assert.NoError(t, err) - _, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, driverOpts, nil) + _, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, storeOpts) assert.NoError(t, err) } diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index c12adadd8..2d8085020 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -32,6 +32,7 @@ const ( KubeVolumeTypeBlockDevice KubeVolumeTypeCharDevice KubeVolumeTypeSecret + KubeVolumeTypeEmptyDir ) //nolint:revive @@ -62,13 +63,13 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } // Label a newly created volume if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, fmt.Errorf("error giving %s a label: %w", hostPath.Path, err) + return nil, fmt.Errorf("giving %s a label: %w", hostPath.Path, err) } case v1.HostPathFileOrCreate: if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) if err != nil { - return nil, fmt.Errorf("error creating HostPath: %w", err) + return nil, fmt.Errorf("creating HostPath: %w", err) } if err := f.Close(); err != nil { logrus.Warnf("Error in closing newly created HostPath file: %v", err) @@ -76,12 +77,12 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } // unconditionally label a newly created volume if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, fmt.Errorf("error giving %s a label: %w", hostPath.Path, err) + return nil, fmt.Errorf("giving %s a label: %w", hostPath.Path, err) } case v1.HostPathSocket: st, err := os.Stat(hostPath.Path) if err != nil { - return nil, fmt.Errorf("error checking HostPathSocket: %w", err) + return nil, fmt.Errorf("checking HostPathSocket: %w", err) } if st.Mode()&os.ModeSocket != os.ModeSocket { return nil, fmt.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) @@ -89,7 +90,7 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) case v1.HostPathBlockDev: dev, err := os.Stat(hostPath.Path) if err != nil { - return nil, fmt.Errorf("error checking HostPathBlockDevice: %w", err) + return nil, fmt.Errorf("checking HostPathBlockDevice: %w", err) } if dev.Mode()&os.ModeCharDevice == os.ModeCharDevice { return nil, fmt.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) @@ -101,7 +102,7 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) case v1.HostPathCharDev: dev, err := os.Stat(hostPath.Path) if err != nil { - return nil, fmt.Errorf("error checking HostPathCharDevice: %w", err) + return nil, fmt.Errorf("checking HostPathCharDevice: %w", err) } if dev.Mode()&os.ModeCharDevice != os.ModeCharDevice { return nil, fmt.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) @@ -121,7 +122,7 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { - return nil, fmt.Errorf("error in parsing HostPath in YAML: %w", err) + return nil, fmt.Errorf("in parsing HostPath in YAML: %w", err) } return &KubeVolume{ @@ -219,8 +220,13 @@ func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, config return kv, nil } +// Create a kubeVolume for an emptyDir volume +func VolumeFromEmptyDir(emptyDirVolumeSource *v1.EmptyDirVolumeSource, name string) (*KubeVolume, error) { + return &KubeVolume{Type: KubeVolumeTypeEmptyDir, Source: name}, nil +} + // Create a KubeVolume from one of the supported VolumeSource -func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager) (*KubeVolume, error) { +func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, volName string) (*KubeVolume, error) { switch { case volumeSource.HostPath != nil: return VolumeFromHostPath(volumeSource.HostPath) @@ -230,8 +236,10 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, s return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps) case volumeSource.Secret != nil: return VolumeFromSecret(volumeSource.Secret, secretsManager) + case volumeSource.EmptyDir != nil: + return VolumeFromEmptyDir(volumeSource.EmptyDir, volName) default: - return nil, errors.New("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") + return nil, errors.New("HostPath, ConfigMap, EmptyDir, and PersistentVolumeClaim are currently the only supported VolumeSource") } } @@ -240,7 +248,7 @@ func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secre volumes := make(map[string]*KubeVolume) for _, specVolume := range specVolumes { - volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager) + volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager, specVolume.Name) if err != nil { return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err) } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index f0d4e9153..9497894f3 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -3,7 +3,6 @@ package generate import ( "errors" "fmt" - "os" "strings" "github.com/containers/common/libimage" @@ -11,11 +10,11 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/namespaces" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" "github.com/sirupsen/logrus" ) @@ -113,12 +112,12 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if err != nil { // This is likely to be of the fatal kind (pod was // removed) so hard fail - return nil, fmt.Errorf("error looking up pod %s infra container: %w", pod.ID(), err) + return nil, fmt.Errorf("looking up pod %s infra container: %w", pod.ID(), err) } if infraID != "" { ctr, err := rt.GetContainer(infraID) if err != nil { - return nil, fmt.Errorf("error retrieving pod %s infra container %s: %w", pod.ID(), infraID, err) + return nil, fmt.Errorf("retrieving pod %s infra container %s: %w", pod.ID(), infraID, err) } infraCtr = ctr } @@ -136,7 +135,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: pidCtr, err := rt.LookupContainer(s.PidNS.Value) if err != nil { - return nil, fmt.Errorf("error looking up container to share pid namespace with: %w", err) + return nil, fmt.Errorf("looking up container to share pid namespace with: %w", err) } toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr)) } @@ -155,7 +154,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: ipcCtr, err := rt.LookupContainer(s.IpcNS.Value) if err != nil { - return nil, fmt.Errorf("error looking up container to share ipc namespace with: %w", err) + return nil, fmt.Errorf("looking up container to share ipc namespace with: %w", err) } if ipcCtr.ConfigNoCopy().NoShmShare { return nil, fmt.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID()) @@ -187,7 +186,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { - return nil, fmt.Errorf("error looking up container to share uts namespace with: %w", err) + return nil, fmt.Errorf("looking up container to share uts namespace with: %w", err) } toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr)) } @@ -198,12 +197,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if !rootless.IsRootless() { return nil, errors.New("keep-id is only supported in rootless mode") } - toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) + opts, err := namespaces.UsernsMode(s.UserNS.String()).GetKeepIDOptions() + if err != nil { + return nil, err + } + if opts.UID == nil && opts.GID == nil { + toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) + } // If user is not overridden, set user in the container // to user running Podman. if s.User == "" { - _, uid, gid, err := util.GetKeepIDMapping() + _, uid, gid, err := util.GetKeepIDMapping(opts) if err != nil { return nil, err } @@ -222,7 +227,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: userCtr, err := rt.LookupContainer(s.UserNS.Value) if err != nil { - return nil, fmt.Errorf("error looking up container to share user namespace with: %w", err) + return nil, fmt.Errorf("looking up container to share user namespace with: %w", err) } toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) } @@ -254,7 +259,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value) if err != nil { - return nil, fmt.Errorf("error looking up container to share cgroup namespace with: %w", err) + return nil, fmt.Errorf("looking up container to share cgroup namespace with: %w", err) } toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr)) } @@ -282,7 +287,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: netCtr, err := rt.LookupContainer(s.NetNS.Value) if err != nil { - return nil, fmt.Errorf("error looking up container to share net namespace with: %w", err) + return nil, fmt.Errorf("looking up container to share net namespace with: %w", err) } toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) case specgen.Slirp: @@ -357,153 +362,6 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. return toReturn, nil } -func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { - // PID - switch s.PidNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.PidNS.Value); err != nil { - return fmt.Errorf("cannot find specified PID namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil { - return err - } - } - - // IPC - switch s.IpcNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.IpcNS.Value); err != nil { - return fmt.Errorf("cannot find specified IPC namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil { - return err - } - } - - // UTS - switch s.UtsNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.UtsNS.Value); err != nil { - return fmt.Errorf("cannot find specified UTS namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil { - return err - } - } - - hostname := s.Hostname - if hostname == "" { - switch { - case s.UtsNS.NSMode == specgen.FromPod: - hostname = pod.Hostname() - case s.UtsNS.NSMode == specgen.FromContainer: - utsCtr, err := rt.LookupContainer(s.UtsNS.Value) - if err != nil { - return fmt.Errorf("error looking up container to share uts namespace with: %w", err) - } - hostname = utsCtr.Hostname() - case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: - tmpHostname, err := os.Hostname() - if err != nil { - return fmt.Errorf("unable to retrieve hostname of the host: %w", err) - } - hostname = tmpHostname - default: - logrus.Debug("No hostname set; container's hostname will default to runtime default") - } - } - - g.RemoveHostname() - if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { - // Set the hostname in the OCI configuration only if specified by - // the user or if we are creating a new UTS namespace. - // TODO: Should we be doing this for pod or container shared - // namespaces? - g.SetHostname(hostname) - } - if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { - g.AddProcessEnv("HOSTNAME", hostname) - } - - // User - if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil { - return err - } - - // Cgroup - switch s.CgroupNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.CgroupNS.Value); err != nil { - return fmt.Errorf("cannot find specified cgroup namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil { - return err - } - } - - // Net - switch s.NetNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.NetNS.Value); err != nil { - return fmt.Errorf("cannot find specified network namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return err - } - case specgen.Private, specgen.NoNetwork: - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil { - return err - } - } - - if g.Config.Annotations == nil { - g.Config.Annotations = make(map[string]string) - } - if s.PublishExposedPorts { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue - } else { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse - } - - return nil -} - // GetNamespaceOptions transforms a slice of kernel namespaces // into a slice of pod create options. Currently, not all // kernel namespaces are supported, and they will be returned in an error diff --git a/pkg/specgen/generate/namespaces_freebsd.go b/pkg/specgen/generate/namespaces_freebsd.go new file mode 100644 index 000000000..f4246de34 --- /dev/null +++ b/pkg/specgen/generate/namespaces_freebsd.go @@ -0,0 +1,51 @@ +package generate + +import ( + "fmt" + "os" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" + "github.com/sirupsen/logrus" +) + +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { + // UTS + + hostname := s.Hostname + if hostname == "" { + switch { + case s.UtsNS.NSMode == specgen.FromPod: + hostname = pod.Hostname() + case s.UtsNS.NSMode == specgen.FromContainer: + utsCtr, err := rt.LookupContainer(s.UtsNS.Value) + if err != nil { + return fmt.Errorf("looking up container to share uts namespace with: %w", err) + } + hostname = utsCtr.Hostname() + case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: + tmpHostname, err := os.Hostname() + if err != nil { + return fmt.Errorf("unable to retrieve hostname of the host: %w", err) + } + hostname = tmpHostname + default: + logrus.Debug("No hostname set; container's hostname will default to runtime default") + } + } + + g.RemoveHostname() + if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { + // Set the hostname in the OCI configuration only if specified by + // the user or if we are creating a new UTS namespace. + // TODO: Should we be doing this for pod or container shared + // namespaces? + g.SetHostname(hostname) + } + if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { + g.AddProcessEnv("HOSTNAME", hostname) + } + + return nil +} diff --git a/pkg/specgen/generate/namespaces_linux.go b/pkg/specgen/generate/namespaces_linux.go new file mode 100644 index 000000000..9fda000f9 --- /dev/null +++ b/pkg/specgen/generate/namespaces_linux.go @@ -0,0 +1,160 @@ +package generate + +import ( + "fmt" + "os" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" + "github.com/sirupsen/logrus" +) + +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { + // PID + switch s.PidNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.PidNS.Value); err != nil { + return fmt.Errorf("cannot find specified PID namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil { + return err + } + } + + // IPC + switch s.IpcNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.IpcNS.Value); err != nil { + return fmt.Errorf("cannot find specified IPC namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil { + return err + } + } + + // UTS + switch s.UtsNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.UtsNS.Value); err != nil { + return fmt.Errorf("cannot find specified UTS namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil { + return err + } + } + + hostname := s.Hostname + if hostname == "" { + switch { + case s.UtsNS.NSMode == specgen.FromPod: + hostname = pod.Hostname() + case s.UtsNS.NSMode == specgen.FromContainer: + utsCtr, err := rt.LookupContainer(s.UtsNS.Value) + if err != nil { + return fmt.Errorf("looking up container to share uts namespace with: %w", err) + } + hostname = utsCtr.Hostname() + case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: + tmpHostname, err := os.Hostname() + if err != nil { + return fmt.Errorf("unable to retrieve hostname of the host: %w", err) + } + hostname = tmpHostname + default: + logrus.Debug("No hostname set; container's hostname will default to runtime default") + } + } + + g.RemoveHostname() + if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { + // Set the hostname in the OCI configuration only if specified by + // the user or if we are creating a new UTS namespace. + // TODO: Should we be doing this for pod or container shared + // namespaces? + g.SetHostname(hostname) + } + if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { + g.AddProcessEnv("HOSTNAME", hostname) + } + + // User + if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil { + return err + } + + // Cgroup + switch s.CgroupNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.CgroupNS.Value); err != nil { + return fmt.Errorf("cannot find specified cgroup namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil { + return err + } + } + + // Net + switch s.NetNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.NetNS.Value); err != nil { + return fmt.Errorf("cannot find specified network namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { + return err + } + case specgen.Private, specgen.NoNetwork: + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil { + return err + } + } + + if g.Config.Annotations == nil { + g.Config.Annotations = make(map[string]string) + } + if s.PublishExposedPorts { + g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue + } else { + g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse + } + + return nil +} diff --git a/pkg/specgen/generate/namespaces_unsupported.go b/pkg/specgen/generate/namespaces_unsupported.go new file mode 100644 index 000000000..c4a9c22d8 --- /dev/null +++ b/pkg/specgen/generate/namespaces_unsupported.go @@ -0,0 +1,16 @@ +//go:build !linux && !freebsd +// +build !linux,!freebsd + +package generate + +import ( + "errors" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" +) + +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { + return errors.New("unsupported specConfigureNamespaces") +} diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index f59fe1011..3ac1a9b3f 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -1,37 +1,19 @@ package generate import ( - "context" - "encoding/json" "fmt" - "path" "strings" "github.com/containers/common/libimage" - "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/config" - "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/specgen" - spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -func setProcOpts(s *specgen.SpecGenerator, g *generate.Generator) { - if s.ProcOpts == nil { - return - } - for i := range g.Config.Mounts { - if g.Config.Mounts[i].Destination == "/proc" { - g.Config.Mounts[i].Options = s.ProcOpts - return - } - } -} - func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) { var ( isRootless = rootless.IsRootless() @@ -58,38 +40,38 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) { // files and number of processes to the maximum they can be set to // (without overriding a sysctl) if !nofileSet { - max := define.RLimitDefaultValue - current := define.RLimitDefaultValue + max := rlimT(define.RLimitDefaultValue) + current := rlimT(define.RLimitDefaultValue) if isRootless { var rlimit unix.Rlimit if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { logrus.Warnf("Failed to return RLIMIT_NOFILE ulimit %q", err) } - if rlimit.Cur < current { - current = rlimit.Cur + if rlimT(rlimit.Cur) < current { + current = rlimT(rlimit.Cur) } - if rlimit.Max < max { - max = rlimit.Max + if rlimT(rlimit.Max) < max { + max = rlimT(rlimit.Max) } } - g.AddProcessRlimits("RLIMIT_NOFILE", max, current) + g.AddProcessRlimits("RLIMIT_NOFILE", uint64(max), uint64(current)) } if !nprocSet { - max := define.RLimitDefaultValue - current := define.RLimitDefaultValue + max := rlimT(define.RLimitDefaultValue) + current := rlimT(define.RLimitDefaultValue) if isRootless { var rlimit unix.Rlimit if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { logrus.Warnf("Failed to return RLIMIT_NPROC ulimit %q", err) } - if rlimit.Cur < current { - current = rlimit.Cur + if rlimT(rlimit.Cur) < current { + current = rlimT(rlimit.Cur) } - if rlimit.Max < max { - max = rlimit.Max + if rlimT(rlimit.Max) < max { + max = rlimT(rlimit.Max) } } - g.AddProcessRlimits("RLIMIT_NPROC", max, current) + g.AddProcessRlimits("RLIMIT_NPROC", uint64(max), uint64(current)) } } @@ -133,302 +115,3 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c return finalCommand, nil } - -// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container -func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { - if s.NetNS.IsHost() && (isRootless || isNewUserns) { - return false - } - if isNewUserns { - switch s.NetNS.NSMode { - case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: - return true - default: - return false - } - } - return true -} - -func getCgroupPermissons(unmask []string) string { - ro := "ro" - rw := "rw" - cgroup := "/sys/fs/cgroup" - - cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() - if !cgroupv2 { - return ro - } - - if unmask != nil && unmask[0] == "ALL" { - return rw - } - - for _, p := range unmask { - if path.Clean(p) == cgroup { - return rw - } - } - return ro -} - -// SpecGenToOCI returns the base configuration for the container. -func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { - cgroupPerm := getCgroupPermissons(s.Unmask) - - g, err := generate.New("linux") - if err != nil { - return nil, err - } - // Remove the default /dev/shm mount to ensure we overwrite it - g.RemoveMount("/dev/shm") - g.HostSpecific = true - addCgroup := true - - isRootless := rootless.IsRootless() - isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() - - canMountSys := canMountSys(isRootless, isNewUserns, s) - - if s.Privileged && canMountSys { - cgroupPerm = "rw" - g.RemoveMount("/sys") - sysMnt := spec.Mount{ - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, - } - g.AddMount(sysMnt) - } - if !canMountSys { - addCgroup = false - g.RemoveMount("/sys") - r := "ro" - if s.Privileged { - r = "rw" - } - sysMnt := spec.Mount{ - Destination: "/sys", - Type: "bind", // should we use a constant for this, like createconfig? - Source: "/sys", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, - } - g.AddMount(sysMnt) - if !s.Privileged && isRootless { - g.AddLinuxMaskedPaths("/sys/kernel") - } - } - gid5Available := true - if isRootless { - nGids, err := rootless.GetAvailableGids() - if err != nil { - return nil, err - } - gid5Available = nGids >= 5 - } - // When using a different user namespace, check that the GID 5 is mapped inside - // the container. - if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) { - mappingFound := false - for _, r := range s.IDMappings.GIDMap { - if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { - mappingFound = true - break - } - } - if !mappingFound { - gid5Available = false - } - } - if !gid5Available { - // If we have no GID mappings, the gid=5 default option would fail, so drop it. - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, - } - g.AddMount(devPts) - } - - inUserNS := isRootless || isNewUserns - - if inUserNS && s.IpcNS.IsHost() { - g.RemoveMount("/dev/mqueue") - devMqueue := spec.Mount{ - Destination: "/dev/mqueue", - Type: "bind", // constant ? - Source: "/dev/mqueue", - Options: []string{"bind", "nosuid", "noexec", "nodev"}, - } - g.AddMount(devMqueue) - } - if inUserNS && s.PidNS.IsHost() { - g.RemoveMount("/proc") - procMount := spec.Mount{ - Destination: "/proc", - Type: define.TypeBind, - Source: "/proc", - Options: []string{"rbind", "nosuid", "noexec", "nodev"}, - } - g.AddMount(procMount) - } - - if addCgroup { - cgroupMnt := spec.Mount{ - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Source: "cgroup", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm}, - } - g.AddMount(cgroupMnt) - } - - g.Config.Linux.Personality = s.Personality - - g.SetProcessCwd(s.WorkDir) - - g.SetProcessArgs(finalCmd) - - g.SetProcessTerminal(s.Terminal) - - for key, val := range s.Annotations { - g.AddAnnotation(key, val) - } - - if s.ResourceLimits != nil { - out, err := json.Marshal(s.ResourceLimits) - if err != nil { - return nil, err - } - err = json.Unmarshal(out, g.Config.Linux.Resources) - if err != nil { - return nil, err - } - g.Config.Linux.Resources = s.ResourceLimits - } - - weightDevices, err := WeightDevices(s.WeightDevice) - if err != nil { - return nil, err - } - if len(weightDevices) > 0 { - for _, dev := range weightDevices { - g.AddLinuxResourcesBlockIOWeightDevice(dev.Major, dev.Minor, *dev.Weight) - } - } - - // Devices - // set the default rule at the beginning of device configuration - if !inUserNS && !s.Privileged { - g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") - } - - var userDevices []spec.LinuxDevice - - if !s.Privileged { - // add default devices from containers.conf - for _, device := range rtc.Containers.Devices { - if err = DevicesFromPath(&g, device); err != nil { - return nil, err - } - } - if len(compatibleOptions.HostDeviceList) > 0 && len(s.Devices) == 0 { - userDevices = compatibleOptions.HostDeviceList - } else { - userDevices = s.Devices - } - // add default devices specified by caller - for _, device := range userDevices { - if err = DevicesFromPath(&g, device.Path); err != nil { - return nil, err - } - } - } - s.HostDeviceList = userDevices - - // set the devices cgroup when not running in a user namespace - if !inUserNS && !s.Privileged { - for _, dev := range s.DeviceCgroupRule { - g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) - } - } - - BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) - - g.ClearProcessEnv() - for name, val := range s.Env { - g.AddProcessEnv(name, val) - } - - addRlimits(s, &g) - - // NAMESPACES - if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { - return nil, err - } - configSpec := g.Config - - if err := securityConfigureGenerator(s, &g, newImage, rtc); err != nil { - return nil, err - } - - // BIND MOUNTS - configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) - // Process mounts to ensure correct options - if err := InitFSMounts(configSpec.Mounts); err != nil { - return nil, err - } - - // Add annotations - if configSpec.Annotations == nil { - configSpec.Annotations = make(map[string]string) - } - - if s.Remove { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse - } - - if len(s.VolumesFrom) > 0 { - configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") - } - - if s.Privileged { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse - } - - if s.Init { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse - } - - if s.OOMScoreAdj != nil { - g.SetProcessOOMScoreAdj(*s.OOMScoreAdj) - } - setProcOpts(s, &g) - - return configSpec, nil -} - -func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { - devs := []spec.LinuxWeightDevice{} - for k, v := range wtDevices { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return nil, fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) - } - dev := new(spec.LinuxWeightDevice) - dev.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - dev.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - dev.Weight = v.Weight - devs = append(devs, *dev) - } - return devs, nil -} diff --git a/pkg/specgen/generate/oci_freebsd.go b/pkg/specgen/generate/oci_freebsd.go new file mode 100644 index 000000000..71c926fd2 --- /dev/null +++ b/pkg/specgen/generate/oci_freebsd.go @@ -0,0 +1,96 @@ +//go:build freebsd + +package generate + +import ( + "context" + "strings" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" +) + +// SpecGenToOCI returns the base configuration for the container. +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { + g, err := generate.New("freebsd") + if err != nil { + return nil, err + } + + g.SetProcessCwd(s.WorkDir) + + g.SetProcessArgs(finalCmd) + + g.SetProcessTerminal(s.Terminal) + + for key, val := range s.Annotations { + g.AddAnnotation(key, val) + } + + g.ClearProcessEnv() + for name, val := range s.Env { + g.AddProcessEnv(name, val) + } + + addRlimits(s, &g) + + // NAMESPACES + if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { + return nil, err + } + configSpec := g.Config + + if err := securityConfigureGenerator(s, &g, newImage, rtc); err != nil { + return nil, err + } + + // BIND MOUNTS + configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) + // Process mounts to ensure correct options + if err := InitFSMounts(configSpec.Mounts); err != nil { + return nil, err + } + + // Add annotations + if configSpec.Annotations == nil { + configSpec.Annotations = make(map[string]string) + } + + if s.Remove { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse + } + + if len(s.VolumesFrom) > 0 { + configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") + } + + if s.Privileged { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse + } + + if s.Init { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse + } + + if s.OOMScoreAdj != nil { + g.SetProcessOOMScoreAdj(*s.OOMScoreAdj) + } + + return configSpec, nil +} + +func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { + devs := []spec.LinuxWeightDevice{} + return devs, nil +} diff --git a/pkg/specgen/generate/oci_linux.go b/pkg/specgen/generate/oci_linux.go new file mode 100644 index 000000000..341853de5 --- /dev/null +++ b/pkg/specgen/generate/oci_linux.go @@ -0,0 +1,331 @@ +package generate + +import ( + "context" + "encoding/json" + "fmt" + "path" + "strings" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/cgroups" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/rootless" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" + "golang.org/x/sys/unix" +) + +func setProcOpts(s *specgen.SpecGenerator, g *generate.Generator) { + if s.ProcOpts == nil { + return + } + for i := range g.Config.Mounts { + if g.Config.Mounts[i].Destination == "/proc" { + g.Config.Mounts[i].Options = s.ProcOpts + return + } + } +} + +// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container +func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { + if s.NetNS.IsHost() && (isRootless || isNewUserns) { + return false + } + if isNewUserns { + switch s.NetNS.NSMode { + case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: + return true + default: + return false + } + } + return true +} + +func getCgroupPermissons(unmask []string) string { + ro := "ro" + rw := "rw" + cgroup := "/sys/fs/cgroup" + + cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() + if !cgroupv2 { + return ro + } + + if unmask != nil && unmask[0] == "ALL" { + return rw + } + + for _, p := range unmask { + if path.Clean(p) == cgroup { + return rw + } + } + return ro +} + +// SpecGenToOCI returns the base configuration for the container. +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { + cgroupPerm := getCgroupPermissons(s.Unmask) + + g, err := generate.New("linux") + if err != nil { + return nil, err + } + // Remove the default /dev/shm mount to ensure we overwrite it + g.RemoveMount("/dev/shm") + g.HostSpecific = true + addCgroup := true + + isRootless := rootless.IsRootless() + isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() + + canMountSys := canMountSys(isRootless, isNewUserns, s) + + if s.Privileged && canMountSys { + cgroupPerm = "rw" + g.RemoveMount("/sys") + sysMnt := spec.Mount{ + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, + } + g.AddMount(sysMnt) + } + if !canMountSys { + addCgroup = false + g.RemoveMount("/sys") + r := "ro" + if s.Privileged { + r = "rw" + } + sysMnt := spec.Mount{ + Destination: "/sys", + Type: "bind", // should we use a constant for this, like createconfig? + Source: "/sys", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, + } + g.AddMount(sysMnt) + if !s.Privileged && isRootless { + g.AddLinuxMaskedPaths("/sys/kernel") + } + } + gid5Available := true + if isRootless { + nGids, err := rootless.GetAvailableGids() + if err != nil { + return nil, err + } + gid5Available = nGids >= 5 + } + // When using a different user namespace, check that the GID 5 is mapped inside + // the container. + if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) { + mappingFound := false + for _, r := range s.IDMappings.GIDMap { + if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { + mappingFound = true + break + } + } + if !mappingFound { + gid5Available = false + } + } + if !gid5Available { + // If we have no GID mappings, the gid=5 default option would fail, so drop it. + g.RemoveMount("/dev/pts") + devPts := spec.Mount{ + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, + } + g.AddMount(devPts) + } + + inUserNS := isRootless || isNewUserns + + if inUserNS && s.IpcNS.IsHost() { + g.RemoveMount("/dev/mqueue") + devMqueue := spec.Mount{ + Destination: "/dev/mqueue", + Type: "bind", // constant ? + Source: "/dev/mqueue", + Options: []string{"bind", "nosuid", "noexec", "nodev"}, + } + g.AddMount(devMqueue) + } + if inUserNS && s.PidNS.IsHost() { + g.RemoveMount("/proc") + procMount := spec.Mount{ + Destination: "/proc", + Type: define.TypeBind, + Source: "/proc", + Options: []string{"rbind", "nosuid", "noexec", "nodev"}, + } + g.AddMount(procMount) + } + + if addCgroup { + cgroupMnt := spec.Mount{ + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Source: "cgroup", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm}, + } + g.AddMount(cgroupMnt) + } + + g.Config.Linux.Personality = s.Personality + + g.SetProcessCwd(s.WorkDir) + + g.SetProcessArgs(finalCmd) + + g.SetProcessTerminal(s.Terminal) + + for key, val := range s.Annotations { + g.AddAnnotation(key, val) + } + + if s.ResourceLimits != nil { + out, err := json.Marshal(s.ResourceLimits) + if err != nil { + return nil, err + } + err = json.Unmarshal(out, g.Config.Linux.Resources) + if err != nil { + return nil, err + } + g.Config.Linux.Resources = s.ResourceLimits + } + + weightDevices, err := WeightDevices(s.WeightDevice) + if err != nil { + return nil, err + } + if len(weightDevices) > 0 { + for _, dev := range weightDevices { + g.AddLinuxResourcesBlockIOWeightDevice(dev.Major, dev.Minor, *dev.Weight) + } + } + + // Devices + // set the default rule at the beginning of device configuration + if !inUserNS && !s.Privileged { + g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") + } + + var userDevices []spec.LinuxDevice + + if !s.Privileged { + // add default devices from containers.conf + for _, device := range rtc.Containers.Devices { + if err = DevicesFromPath(&g, device); err != nil { + return nil, err + } + } + if len(compatibleOptions.HostDeviceList) > 0 && len(s.Devices) == 0 { + userDevices = compatibleOptions.HostDeviceList + } else { + userDevices = s.Devices + } + // add default devices specified by caller + for _, device := range userDevices { + if err = DevicesFromPath(&g, device.Path); err != nil { + return nil, err + } + } + } + s.HostDeviceList = userDevices + + // set the devices cgroup when not running in a user namespace + if !inUserNS && !s.Privileged { + for _, dev := range s.DeviceCgroupRule { + g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) + } + } + + BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) + + g.ClearProcessEnv() + for name, val := range s.Env { + g.AddProcessEnv(name, val) + } + + addRlimits(s, &g) + + // NAMESPACES + if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { + return nil, err + } + configSpec := g.Config + + if err := securityConfigureGenerator(s, &g, newImage, rtc); err != nil { + return nil, err + } + + // BIND MOUNTS + configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) + // Process mounts to ensure correct options + if err := InitFSMounts(configSpec.Mounts); err != nil { + return nil, err + } + + // Add annotations + if configSpec.Annotations == nil { + configSpec.Annotations = make(map[string]string) + } + + if s.Remove { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse + } + + if len(s.VolumesFrom) > 0 { + configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") + } + + if s.Privileged { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse + } + + if s.Init { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse + } + + if s.OOMScoreAdj != nil { + g.SetProcessOOMScoreAdj(*s.OOMScoreAdj) + } + setProcOpts(s, &g) + + return configSpec, nil +} + +func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { + devs := []spec.LinuxWeightDevice{} + for k, v := range wtDevices { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return nil, fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) + } + dev := new(spec.LinuxWeightDevice) + dev.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + dev.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + dev.Weight = v.Weight + devs = append(devs, *dev) + } + return devs, nil +} diff --git a/pkg/specgen/generate/oci_unsupported.go b/pkg/specgen/generate/oci_unsupported.go new file mode 100644 index 000000000..7e1b8c42c --- /dev/null +++ b/pkg/specgen/generate/oci_unsupported.go @@ -0,0 +1,24 @@ +//go:build !linux && !freebsd +// +build !linux,!freebsd + +package generate + +import ( + "context" + "errors" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +// SpecGenToOCI returns the base configuration for the container. +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { + return nil, errors.New("unsupported SpecGenToOCI") +} + +func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { + return []spec.LinuxWeightDevice{}, errors.New("unsupported WeightDevices") +} diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 4e6362c9b..14d390e49 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -2,6 +2,7 @@ package generate import ( "context" + "encoding/json" "fmt" "net" "os" @@ -44,7 +45,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { } if !p.PodSpecGen.NoInfra { - err := FinishThrottleDevices(p.PodSpecGen.InfraContainerSpec) + err := specgen.FinishThrottleDevices(p.PodSpecGen.InfraContainerSpec) if err != nil { return nil, err } @@ -52,17 +53,11 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { p.PodSpecGen.ResourceLimits.BlockIO = p.PodSpecGen.InfraContainerSpec.ResourceLimits.BlockIO } - weightDevices, err := WeightDevices(p.PodSpecGen.InfraContainerSpec.WeightDevice) + err = specgen.WeightDevices(p.PodSpecGen.InfraContainerSpec) if err != nil { return nil, err } - - if p.PodSpecGen.ResourceLimits != nil && len(weightDevices) > 0 { - if p.PodSpecGen.ResourceLimits.BlockIO == nil { - p.PodSpecGen.ResourceLimits.BlockIO = &specs.LinuxBlockIO{} - } - p.PodSpecGen.ResourceLimits.BlockIO.WeightDevice = weightDevices - } + p.PodSpecGen.ResourceLimits = p.PodSpecGen.InfraContainerSpec.ResourceLimits } options, err := createPodOptions(&p.PodSpecGen) @@ -327,6 +322,19 @@ func PodConfigToSpec(rt *libpod.Runtime, spec *specgen.PodSpecGenerator, infraOp } spec.InfraContainerSpec = infraSpec + matching, err := json.Marshal(infraSpec) + if err != nil { + return nil, err + } + + // track name before unmarshal so we do not overwrite w/ infra + name := spec.Name + err = json.Unmarshal(matching, spec) + if err != nil { + return nil, err + } + + spec.Name = name } // need to reset hostname, name etc of both pod and infra diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index 572f256c1..8ad249409 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -354,7 +354,7 @@ func createPortMappings(s *specgen.SpecGenerator, imageData *libimage.ImageData) } protocols, err := checkProtocol(proto, false) if err != nil { - return nil, nil, fmt.Errorf("error validating protocols for exposed port %d: %w", port, err) + return nil, nil, fmt.Errorf("validating protocols for exposed port %d: %w", port, err) } toExpose[port] = appendProtocolsNoDuplicates(toExpose[port], protocols) } diff --git a/pkg/specgen/generate/rlimit_int64.go b/pkg/specgen/generate/rlimit_int64.go new file mode 100644 index 000000000..b4cce3453 --- /dev/null +++ b/pkg/specgen/generate/rlimit_int64.go @@ -0,0 +1,6 @@ +//go:build freebsd +// +build freebsd + +package generate + +type rlimT int64 diff --git a/pkg/specgen/generate/rlimit_uint64.go b/pkg/specgen/generate/rlimit_uint64.go new file mode 100644 index 000000000..d85f8dd2c --- /dev/null +++ b/pkg/specgen/generate/rlimit_uint64.go @@ -0,0 +1,6 @@ +//go:build linux || darwin +// +build linux darwin + +package generate + +type rlimT uint64 diff --git a/pkg/specgen/generate/security_freebsd.go b/pkg/specgen/generate/security_freebsd.go new file mode 100644 index 000000000..5fd66c769 --- /dev/null +++ b/pkg/specgen/generate/security_freebsd.go @@ -0,0 +1,19 @@ +package generate + +import ( + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" +) + +// setLabelOpts sets the label options of the SecurityConfig according to the +// input. +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { + return nil +} + +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *libimage.Image, rtc *config.Config) error { + return nil +} diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security_linux.go index aacefcbac..aacefcbac 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security_linux.go diff --git a/pkg/specgen/generate/security_unsupported.go b/pkg/specgen/generate/security_unsupported.go new file mode 100644 index 000000000..d0f937e44 --- /dev/null +++ b/pkg/specgen/generate/security_unsupported.go @@ -0,0 +1,24 @@ +//go:build !linux && !freebsd +// +build !linux,!freebsd + +package generate + +import ( + "errors" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" +) + +// setLabelOpts sets the label options of the SecurityConfig according to the +// input. +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { + return errors.New("unsupported setLabelOpts") +} + +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *libimage.Image, rtc *config.Config) error { + return errors.New("unsupported securityConfigureGenerator") +} diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index 867bb4b79..c3cd61b36 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -175,7 +175,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru if mount.Type == define.TypeBind { absSrc, err := filepath.Abs(mount.Source) if err != nil { - return nil, nil, nil, fmt.Errorf("error getting absolute path of %s: %w", mount.Source, err) + return nil, nil, nil, fmt.Errorf("getting absolute path of %s: %w", mount.Source, err) } mount.Source = absSrc } @@ -208,7 +208,7 @@ func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGe inspect, err := img.Inspect(ctx, nil) if err != nil { - return nil, nil, fmt.Errorf("error inspecting image to get image volumes: %w", err) + return nil, nil, fmt.Errorf("inspecting image to get image volumes: %w", err) } for volume := range inspect.Config.Volumes { logrus.Debugf("Image has volume at %q", volume) @@ -269,7 +269,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s ctr, err := runtime.LookupContainer(splitVol[0]) if err != nil { - return nil, nil, fmt.Errorf("error looking up container %q for volumes-from: %w", splitVol[0], err) + return nil, nil, fmt.Errorf("looking up container %q for volumes-from: %w", splitVol[0], err) } logrus.Debugf("Adding volumes from container %s", ctr.ID()) diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go index 9c933d747..e9ebdfce3 100644 --- a/pkg/specgen/generate/validate.go +++ b/pkg/specgen/generate/validate.go @@ -9,6 +9,7 @@ import ( "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/sysinfo" + "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/utils" ) @@ -19,6 +20,11 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error sysInfo := sysinfo.New(true) + if s.ResourceLimits != nil && rootless.IsRootless() { + s.ResourceLimits = nil + warnings = append(warnings, "Resource limits are not supported and ignored on cgroups V1 rootless systems") + } + if s.ResourceLimits == nil { return warnings, nil } @@ -76,7 +82,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error } } - // CPU Checks + // CPU checks if s.ResourceLimits.CPU != nil { cpu := s.ResourceLimits.CPU if cpu.Shares != nil && !sysInfo.CPUShares { @@ -163,6 +169,7 @@ func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error return warnings, nil } + // Memory checks if s.ResourceLimits.Memory != nil && s.ResourceLimits.Memory.Swap != nil { own, err := utils.GetOwnCgroup() if err != nil { @@ -192,6 +199,19 @@ func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error s.ResourceLimits.Memory.Swap = nil } } + + // CPU checks + if s.ResourceLimits.CPU != nil { + cpu := s.ResourceLimits.CPU + if cpu.RealtimePeriod != nil { + warnings = append(warnings, "Realtime period not supported on cgroups V2 systems") + cpu.RealtimePeriod = nil + } + if cpu.RealtimeRuntime != nil { + warnings = append(warnings, "Realtime runtime not supported on cgroups V2 systems") + cpu.RealtimeRuntime = nil + } + } return warnings, nil } |