diff options
author | Brent Baude <bbaude@redhat.com> | 2021-09-29 14:57:33 -0500 |
---|---|---|
committer | Brent Baude <bbaude@redhat.com> | 2021-09-30 10:49:14 -0500 |
commit | 1ff6a5082a440fe4a4c3f3670534ab6185d26752 (patch) | |
tree | 8018f0ee002631fc383ac8fbd12ebebcec6ed1ab | |
parent | 966b6030fa5cc31c163e344b64109eddeffc3529 (diff) | |
download | podman-1ff6a5082a440fe4a4c3f3670534ab6185d26752.tar.gz podman-1ff6a5082a440fe4a4c3f3670534ab6185d26752.tar.bz2 podman-1ff6a5082a440fe4a4c3f3670534ab6185d26752.zip |
Support selinux options with bind mounts play/gen
When using play kube and generate kube, we need to support if bind
mounts have selinux options. As kubernetes does not support selinux in
this way, we tuck the selinux values into a pod annotation for
generation of the kube yaml. Then on play, we check annotations to see
if a value for the mount exists and apply it.
Fixes BZ #1984081
Signed-off-by: Brent Baude <bbaude@redhat.com>
-rw-r--r-- | libpod/define/config.go | 3 | ||||
-rw-r--r-- | libpod/kube.go | 51 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 2 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/kube.go | 11 | ||||
-rw-r--r-- | test/e2e/generate_kube_test.go | 11 |
5 files changed, 58 insertions, 20 deletions
diff --git a/libpod/define/config.go b/libpod/define/config.go index 7a0d39e42..a5cf07afc 100644 --- a/libpod/define/config.go +++ b/libpod/define/config.go @@ -90,3 +90,6 @@ const ( // DefaultRlimitValue is the value set by default for nofile and nproc const RLimitDefaultValue = uint64(1048576) + +// BindMountPrefix distinguishes its annotations from others +const BindMountPrefix = "bind-mount-options:" diff --git a/libpod/kube.go b/libpod/kube.go index 57d99f3ef..bf86a9d16 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -241,11 +241,13 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po isInit := ctr.IsInitCtr() - ctr, volumes, _, err := containerToV1Container(ctx, ctr) + ctr, volumes, _, annotations, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } - + for k, v := range annotations { + podAnnotations[define.BindMountPrefix+k] = v + } // Since port bindings for the pod are handled by the // infra container, wipe them here. ctr.Ports = nil @@ -271,7 +273,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po deDupPodVolumes[vol.Name] = &vol } } else { - _, _, infraDNS, err := containerToV1Container(ctx, ctr) + _, _, infraDNS, _, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } @@ -359,17 +361,19 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, if !ctr.HostNetwork() { hostNetwork = false } - kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctx, ctr) + kubeCtr, kubeVols, ctrDNS, annotations, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } + for k, v := range annotations { + kubeAnnotations[define.BindMountPrefix+k] = v + } if isInit { kubeInitCtrs = append(kubeInitCtrs, kubeCtr) } else { kubeCtrs = append(kubeCtrs, kubeCtr) } kubeVolumes = append(kubeVolumes, kubeVols...) - // Combine DNS information in sum'd structure if ctrDNS != nil { // nameservers @@ -415,42 +419,44 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, // containerToV1Container converts information we know about a libpod container // to a V1.Container specification. -func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, error) { +func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, map[string]string, error) { kubeContainer := v1.Container{} kubeVolumes := []v1.Volume{} + annotations := make(map[string]string) kubeSec, err := generateKubeSecurityContext(c) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } // NOTE: a privileged container mounts all of /dev/*. if !c.Privileged() && len(c.config.Spec.Linux.Devices) > 0 { // TODO Enable when we can support devices and their names kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.config.Spec.Linux.Devices) - return kubeContainer, kubeVolumes, nil, errors.Wrapf(define.ErrNotImplemented, "linux devices") + return kubeContainer, kubeVolumes, nil, annotations, errors.Wrapf(define.ErrNotImplemented, "linux devices") } if len(c.config.UserVolumes) > 0 { - volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c) + volumeMounts, volumes, localAnnotations, err := libpodMountsToKubeVolumeMounts(c) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, nil, err } + annotations = localAnnotations kubeContainer.VolumeMounts = volumeMounts kubeVolumes = append(kubeVolumes, volumes...) } envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } portmappings, err := c.PortMappings() if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } ports, err := ocicniPortMappingToContainerPort(portmappings) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } // Handle command and arguments. @@ -469,11 +475,11 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] kubeContainer.Stdin = c.Stdin() img, _, err := c.runtime.libimageRuntime.LookupImage(image, nil) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } imgData, err := img.Inspect(ctx, false) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) { kubeContainer.Command = nil @@ -555,7 +561,7 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] } dns.Options = dnsOptions } - return kubeContainer, kubeVolumes, &dns, nil + return kubeContainer, kubeVolumes, &dns, annotations, nil } // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts @@ -606,16 +612,23 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { } // libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands -func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, error) { +func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, map[string]string, error) { namedVolumes, mounts := c.sortUserVolumes(c.config.Spec) vms := make([]v1.VolumeMount, 0, len(mounts)) vos := make([]v1.Volume, 0, len(mounts)) + annotations := make(map[string]string) var suffix string for index, m := range mounts { + for _, opt := range m.Options { + if opt == "Z" || opt == "z" { + annotations[m.Source] = opt + break + } + } vm, vo, err := generateKubeVolumeMount(m) if err != nil { - return vms, vos, err + return vms, vos, annotations, err } // Name will be the same, so use the index as suffix suffix = fmt.Sprintf("-%d", index) @@ -629,7 +642,7 @@ func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume vms = append(vms, vm) vos = append(vos, vo) } - return vms, vos, nil + return vms, vos, annotations, nil } // generateKubePersistentVolumeClaim converts a ContainerNamedVolume to a Kubernetes PersistentVolumeClaim diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 35389ec5e..cf72a6253 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -319,8 +319,8 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - specgenOpts := kube.CtrSpecGenOptions{ + Annotations: annotations, Container: initCtr, Image: pulledImage, Volumes: volumes, diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index c01d7a1f0..27a1e5a72 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -12,6 +12,7 @@ import ( "github.com/containers/common/pkg/parse" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/network/types" ann "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/domain/entities" @@ -86,6 +87,8 @@ func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, } type CtrSpecGenOptions struct { + // Annotations from the Pod + Annotations map[string]string // Container as read from the pod yaml Container v1.Container // Image available to use (pulled or found local) @@ -289,6 +292,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener volume.MountPath = dest switch volumeSource.Type { case KubeVolumeTypeBindMount: + // If the container has bind mounts, we need to check if + // 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 && !util.StringInSlice("z", options) && !util.StringInSlice("Z", options) { + options = append(options, v) + } + } mount := spec.Mount{ Destination: volume.MountPath, Source: volumeSource.Source, diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index cb556991c..cb987e139 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -6,6 +6,8 @@ import ( "path/filepath" "strconv" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" "github.com/ghodss/yaml" @@ -555,6 +557,15 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) + b, err := ioutil.ReadFile(outputFile) + Expect(err).ShouldNot(HaveOccurred()) + pod := new(v1.Pod) + err = yaml.Unmarshal(b, pod) + Expect(err).To(BeNil()) + val, found := pod.Annotations[define.BindMountPrefix+vol1] + Expect(found).To(BeTrue()) + Expect(val).To(HaveSuffix("z")) + rm := podmanTest.Podman([]string{"pod", "rm", "-f", "test1"}) rm.WaitWithDefaultTimeout() Expect(rm).Should(Exit(0)) |