mirror of
https://github.com/containers/podman.git
synced 2025-07-03 17:27:18 +08:00
Must use mountlabel when creating builtin volumes
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
@ -1665,6 +1665,18 @@ func WithVolumeLabels(labels map[string]string) VolumeCreateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithVolumeMountLabel sets the MountLabel of the volume.
|
||||
func WithVolumeMountLabel(mountLabel string) VolumeCreateOption {
|
||||
return func(volume *Volume) error {
|
||||
if volume.valid {
|
||||
return define.ErrVolumeFinalized
|
||||
}
|
||||
|
||||
volume.config.MountLabel = mountLabel
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithVolumeOptions sets the options of the volume.
|
||||
func WithVolumeOptions(options map[string]string) VolumeCreateOption {
|
||||
return func(volume *Volume) error {
|
||||
|
@ -107,6 +107,18 @@ func (p *Pod) Name() string {
|
||||
return p.config.Name
|
||||
}
|
||||
|
||||
// MountLabel returns the SELinux label associated with the pod
|
||||
func (p *Pod) MountLabel() (string, error) {
|
||||
if !p.HasInfraContainer() {
|
||||
return "", nil
|
||||
}
|
||||
ctr, err := p.infraContainer()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ctr.MountLabel(), nil
|
||||
}
|
||||
|
||||
// Namespace returns the pod's libpod namespace.
|
||||
// Namespaces are used to logically separate containers and pods in the state.
|
||||
func (p *Pod) Namespace() string {
|
||||
|
@ -495,7 +495,10 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
||||
logrus.Debugf("Creating new volume %s for container", vol.Name)
|
||||
|
||||
// The volume does not exist, so we need to create it.
|
||||
volOptions := []VolumeCreateOption{WithVolumeName(vol.Name)}
|
||||
volOptions := []VolumeCreateOption{
|
||||
WithVolumeName(vol.Name),
|
||||
WithVolumeMountLabel(ctr.MountLabel()),
|
||||
}
|
||||
if isAnonymous {
|
||||
volOptions = append(volOptions, withSetAnon())
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
pluginapi "github.com/docker/go-plugins-helpers/volume"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -122,6 +123,13 @@ func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, opti
|
||||
storageConfig := storage.ContainerOptions{
|
||||
LabelOpts: []string{"filetype:container_file_t:s0"},
|
||||
}
|
||||
if len(volume.config.MountLabel) > 0 {
|
||||
context, err := selinux.NewContext(volume.config.MountLabel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get SELinux context from %s: %w", volume.config.MountLabel, err)
|
||||
}
|
||||
storageConfig.LabelOpts = []string{fmt.Sprintf("filetype:%s:s0", context["type"])}
|
||||
}
|
||||
if _, err := r.storageService.CreateContainerStorage(ctx, r.imageContext, imgString, image.ID(), volume.config.StorageName, volume.config.StorageID, storageConfig); err != nil {
|
||||
return nil, fmt.Errorf("creating backing storage for image driver: %w", err)
|
||||
}
|
||||
@ -161,7 +169,7 @@ func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, opti
|
||||
if err := idtools.SafeChown(fullVolPath, volume.config.UID, volume.config.GID); err != nil {
|
||||
return nil, fmt.Errorf("chowning volume directory %q to %d:%d: %w", fullVolPath, volume.config.UID, volume.config.GID, err)
|
||||
}
|
||||
if err := LabelVolumePath(fullVolPath); err != nil {
|
||||
if err := LabelVolumePath(fullVolPath, volume.config.MountLabel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if volume.config.DisableQuota {
|
||||
|
@ -25,7 +25,7 @@ func deleteSystemdCgroup(path string, resources *spec.LinuxResources) error {
|
||||
}
|
||||
|
||||
// No equivalent on FreeBSD?
|
||||
func LabelVolumePath(path string) error {
|
||||
func LabelVolumePath(path, mountLabel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -109,14 +109,17 @@ var lvpReleaseLabel = label.ReleaseLabel
|
||||
|
||||
// LabelVolumePath takes a mount path for a volume and gives it an
|
||||
// selinux label of either shared or not
|
||||
func LabelVolumePath(path string) error {
|
||||
_, mountLabel, err := lvpInitLabels([]string{})
|
||||
func LabelVolumePath(path, mountLabel string) error {
|
||||
if mountLabel == "" {
|
||||
var err error
|
||||
_, mountLabel, err = lvpInitLabels([]string{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting default mountlabels: %w", err)
|
||||
}
|
||||
if err := lvpReleaseLabel(mountLabel); err != nil {
|
||||
return fmt.Errorf("releasing label %q: %w", mountLabel, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := lvpRelabel(path, mountLabel, true); err != nil {
|
||||
if err == syscall.ENOTSUP {
|
||||
|
@ -34,6 +34,6 @@ func TestLabelVolumePath(t *testing.T) {
|
||||
}
|
||||
|
||||
// LabelVolumePath should not return an error if the operation is unsupported.
|
||||
err := LabelVolumePath("/foo/bar")
|
||||
err := LabelVolumePath("/foo/bar", "")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
@ -22,6 +22,6 @@ func Unmount(mount string) {
|
||||
|
||||
// LabelVolumePath takes a mount path for a volume and gives it an
|
||||
// selinux label of either shared or not
|
||||
func LabelVolumePath(path string) error {
|
||||
func LabelVolumePath(path, mountLabel string) error {
|
||||
return errors.New("not implemented LabelVolumePath")
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ type VolumeConfig struct {
|
||||
// StorageImageID is the ID of the image the volume was based off of.
|
||||
// Only used for image volumes.
|
||||
StorageImageID string `json:"storageImageID,omitempty"`
|
||||
// MountLabel is the SELinux label to assign to mount points
|
||||
MountLabel string `json:"mountlabel,omitempty"`
|
||||
}
|
||||
|
||||
// VolumeState holds the volume's mutable state.
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"github.com/coreos/go-systemd/v22/daemon"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/sirupsen/logrus"
|
||||
yamlv3 "gopkg.in/yaml.v3"
|
||||
)
|
||||
@ -279,7 +280,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
|
||||
}
|
||||
}
|
||||
|
||||
r, err := ic.playKubePVC(ctx, &pvcYAML)
|
||||
r, err := ic.playKubePVC(ctx, "", &pvcYAML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -535,7 +536,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
configMaps = append(configMaps, cm)
|
||||
}
|
||||
|
||||
volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps, secretsManager)
|
||||
mountLabel, err := getMountLabel(podYAML.Spec.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps, secretsManager, mountLabel)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -544,7 +550,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
// defined by a configmap or secret
|
||||
for _, v := range volumes {
|
||||
if (v.Type == kube.KubeVolumeTypeConfigMap || v.Type == kube.KubeVolumeTypeSecret) && !v.Optional {
|
||||
vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source))
|
||||
volumeOptions := []libpod.VolumeCreateOption{
|
||||
libpod.WithVolumeName(v.Source),
|
||||
libpod.WithVolumeMountLabel(mountLabel),
|
||||
}
|
||||
vol, err := ic.Libpod.NewVolume(ctx, volumeOptions...)
|
||||
if err != nil {
|
||||
if errors.Is(err, define.ErrVolumeExists) {
|
||||
// Volume for this configmap already exists do not
|
||||
@ -971,7 +981,7 @@ func (ic *ContainerEngine) getImageAndLabelInfo(ctx context.Context, cwd string,
|
||||
}
|
||||
|
||||
// playKubePVC creates a podman volume from a kube persistent volume claim.
|
||||
func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.PersistentVolumeClaim) (*entities.PlayKubeReport, error) {
|
||||
func (ic *ContainerEngine) playKubePVC(ctx context.Context, mountLabel string, pvcYAML *v1.PersistentVolumeClaim) (*entities.PlayKubeReport, error) {
|
||||
var report entities.PlayKubeReport
|
||||
opts := make(map[string]string)
|
||||
|
||||
@ -987,6 +997,7 @@ func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.Persiste
|
||||
libpod.WithVolumeName(name),
|
||||
libpod.WithVolumeLabels(pvcYAML.Labels),
|
||||
libpod.WithVolumeIgnoreIfExist(),
|
||||
libpod.WithVolumeMountLabel(mountLabel),
|
||||
}
|
||||
|
||||
// Get pvc annotations and create remaining podman volume options if available.
|
||||
@ -1415,3 +1426,31 @@ func (ic *ContainerEngine) playKubeSecret(secret *v1.Secret) (*entities.SecretCr
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func getMountLabel(securityContext *v1.PodSecurityContext) (string, error) {
|
||||
var mountLabel string
|
||||
if securityContext == nil {
|
||||
return "", nil
|
||||
}
|
||||
seopt := securityContext.SELinuxOptions
|
||||
if seopt == nil {
|
||||
return mountLabel, nil
|
||||
}
|
||||
if seopt.Level == "" && seopt.FileType == "" {
|
||||
return mountLabel, nil
|
||||
}
|
||||
privLabel := selinux.PrivContainerMountLabel()
|
||||
con, err := selinux.NewContext(privLabel)
|
||||
if err != nil {
|
||||
return mountLabel, err
|
||||
}
|
||||
|
||||
con["level"] = "s0"
|
||||
if seopt.Level != "" {
|
||||
con["level"] = seopt.Level
|
||||
}
|
||||
if seopt.FileType != "" {
|
||||
con["type"] = seopt.FileType
|
||||
}
|
||||
return con.Get(), nil
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ type KubeVolume struct {
|
||||
}
|
||||
|
||||
// Create a KubeVolume from an HostPathVolumeSource
|
||||
func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) {
|
||||
func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource, mountLabel string) (*KubeVolume, error) {
|
||||
if hostPath.Type != nil {
|
||||
switch *hostPath.Type {
|
||||
case v1.HostPathDirectoryOrCreate:
|
||||
@ -59,7 +59,7 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error)
|
||||
return nil, err
|
||||
}
|
||||
// Label a newly created volume
|
||||
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
|
||||
if err := libpod.LabelVolumePath(hostPath.Path, mountLabel); err != nil {
|
||||
return nil, fmt.Errorf("giving %s a label: %w", hostPath.Path, err)
|
||||
}
|
||||
case v1.HostPathFileOrCreate:
|
||||
@ -73,7 +73,8 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error)
|
||||
}
|
||||
}
|
||||
// unconditionally label a newly created volume
|
||||
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
|
||||
|
||||
if err := libpod.LabelVolumePath(hostPath.Path, mountLabel); err != nil {
|
||||
return nil, fmt.Errorf("giving %s a label: %w", hostPath.Path, err)
|
||||
}
|
||||
case v1.HostPathSocket:
|
||||
@ -232,10 +233,10 @@ func VolumeFromEmptyDir(emptyDirVolumeSource *v1.EmptyDirVolumeSource, name stri
|
||||
}
|
||||
|
||||
// Create a KubeVolume from one of the supported VolumeSource
|
||||
func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, volName string) (*KubeVolume, error) {
|
||||
func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, volName, mountLabel string) (*KubeVolume, error) {
|
||||
switch {
|
||||
case volumeSource.HostPath != nil:
|
||||
return VolumeFromHostPath(volumeSource.HostPath)
|
||||
return VolumeFromHostPath(volumeSource.HostPath, mountLabel)
|
||||
case volumeSource.PersistentVolumeClaim != nil:
|
||||
return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim)
|
||||
case volumeSource.ConfigMap != nil:
|
||||
@ -250,11 +251,11 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, s
|
||||
}
|
||||
|
||||
// Create a map of volume name to KubeVolume
|
||||
func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager) (map[string]*KubeVolume, error) {
|
||||
func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, mountLabel string) (map[string]*KubeVolume, error) {
|
||||
volumes := make(map[string]*KubeVolume)
|
||||
|
||||
for _, specVolume := range specVolumes {
|
||||
volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager, specVolume.Name)
|
||||
volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager, specVolume.Name, mountLabel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err)
|
||||
}
|
||||
|
@ -519,5 +519,15 @@ EOF
|
||||
run_podman rm -f -t 0 -a
|
||||
}
|
||||
|
||||
@test "podman run with building volume and selinux file label" {
|
||||
skip_if_no_selinux
|
||||
run_podman create --security-opt label=filetype:usr_t --volume myvol:/myvol $IMAGE top
|
||||
run_podman volume inspect myvol --format '{{ .Mountpoint }}'
|
||||
path=${output}
|
||||
run ls -Zd $path
|
||||
is "$output" "system_u:object_r:usr_t:s0 $path" "volume should be labeled with usr_t type"
|
||||
run_podman volume rm myvol --force
|
||||
}
|
||||
|
||||
|
||||
# vim: filetype=sh
|
||||
|
@ -559,18 +559,24 @@ EOF
|
||||
|
||||
@test "podman kube generate filetype" {
|
||||
YAML=$PODMAN_TMPDIR/test.yml
|
||||
run_podman create --pod new:pod1 --security-opt label=level:s0:c1,c2 --security-opt label=filetype:usr_t --name test1 $IMAGE true
|
||||
run_podman create --pod new:pod1 --security-opt label=level:s0:c1,c2 --security-opt label=filetype:usr_t -v myvol:/myvol --name test1 $IMAGE true
|
||||
run_podman kube generate pod1 -f $YAML
|
||||
run cat $YAML
|
||||
is "$output" ".*filetype: usr_t" "Generated YAML file should contain filetype usr_t"
|
||||
run_podman pod rm --force pod1
|
||||
run_podman volume rm myvol --force
|
||||
|
||||
run_podman kube play $YAML
|
||||
if selinux_enabled; then
|
||||
run_podman inspect pod1-test1 --format "{{ .MountLabel }}"
|
||||
is "$output" "system_u:object_r:usr_t:s0:c1,c2" "Generated container should use filetype usr_t"
|
||||
run_podman volume inspect myvol --format '{{ .Mountpoint }}'
|
||||
path=${output}
|
||||
run ls -Zd $path
|
||||
is "$output" "system_u:object_r:usr_t:s0 $path" "volume should be labeled with usr_t type"
|
||||
fi
|
||||
run_podman kube down $YAML
|
||||
run_podman volume rm myvol --force
|
||||
}
|
||||
|
||||
# kube play --wait=true, where we clear up the created containers, pods, and volumes when a kill or sigterm is triggered
|
||||
|
Reference in New Issue
Block a user