mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 12:18:16 +00:00 
			
		
		
		
	Support volume relabling for pods which specify an SELinux label
This commit is contained in:
		@@ -238,6 +238,8 @@ type Mount struct {
 | 
				
			|||||||
	HostPath string
 | 
						HostPath string
 | 
				
			||||||
	// Whether the mount is read-only.
 | 
						// Whether the mount is read-only.
 | 
				
			||||||
	ReadOnly bool
 | 
						ReadOnly bool
 | 
				
			||||||
 | 
						// Whether the mount needs SELinux relabeling
 | 
				
			||||||
 | 
						SELinuxRelabel bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PortMapping struct {
 | 
					type PortMapping struct {
 | 
				
			||||||
@@ -273,7 +275,16 @@ type RunContainerOptions struct {
 | 
				
			|||||||
	CgroupParent string
 | 
						CgroupParent string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type VolumeMap map[string]volume.Volume
 | 
					// VolumeInfo contains information about the volume.
 | 
				
			||||||
 | 
					type VolumeInfo struct {
 | 
				
			||||||
 | 
						// Builder is the volume's builder
 | 
				
			||||||
 | 
						Builder volume.Builder
 | 
				
			||||||
 | 
						// SELinuxLabeled indicates whether this volume has had the
 | 
				
			||||||
 | 
						// pod's SELinux label applied to it or not
 | 
				
			||||||
 | 
						SELinuxLabeled bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type VolumeMap map[string]VolumeInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Pods []*Pod
 | 
					type Pods []*Pod
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -606,13 +606,29 @@ func makeEnvList(envs []kubecontainer.EnvVar) (result []string) {
 | 
				
			|||||||
// can be understood by docker.
 | 
					// can be understood by docker.
 | 
				
			||||||
// Each element in the string is in the form of:
 | 
					// Each element in the string is in the form of:
 | 
				
			||||||
// '<HostPath>:<ContainerPath>', or
 | 
					// '<HostPath>:<ContainerPath>', or
 | 
				
			||||||
// '<HostPath>:<ContainerPath>:ro', if the path is read only.
 | 
					// '<HostPath>:<ContainerPath>:ro', if the path is read only, or
 | 
				
			||||||
func makeMountBindings(mounts []kubecontainer.Mount) (result []string) {
 | 
					// '<HostPath>:<ContainerPath>:Z', if the volume requires SELinux
 | 
				
			||||||
 | 
					// relabeling and the pod provides an SELinux label
 | 
				
			||||||
 | 
					func makeMountBindings(mounts []kubecontainer.Mount, podHasSELinuxLabel bool) (result []string) {
 | 
				
			||||||
	for _, m := range mounts {
 | 
						for _, m := range mounts {
 | 
				
			||||||
		bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath)
 | 
							bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath)
 | 
				
			||||||
		if m.ReadOnly {
 | 
							if m.ReadOnly {
 | 
				
			||||||
			bind += ":ro"
 | 
								bind += ":ro"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Only request relabeling if the pod provides an
 | 
				
			||||||
 | 
							// SELinux context. If the pod does not provide an
 | 
				
			||||||
 | 
							// SELinux context relabeling will label the volume
 | 
				
			||||||
 | 
							// with the container's randomly allocated MCS label.
 | 
				
			||||||
 | 
							// This would restrict access to the volume to the
 | 
				
			||||||
 | 
							// container which mounts it first.
 | 
				
			||||||
 | 
							if m.SELinuxRelabel && podHasSELinuxLabel {
 | 
				
			||||||
 | 
								if m.ReadOnly {
 | 
				
			||||||
 | 
									bind += ",Z"
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									bind += ":Z"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		result = append(result, bind)
 | 
							result = append(result, bind)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
@@ -766,7 +782,8 @@ func (dm *DockerManager) runContainer(
 | 
				
			|||||||
		dm.recorder.Eventf(ref, "Created", "Created with docker id %v", util.ShortenString(dockerContainer.ID, 12))
 | 
							dm.recorder.Eventf(ref, "Created", "Created with docker id %v", util.ShortenString(dockerContainer.ID, 12))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	binds := makeMountBindings(opts.Mounts)
 | 
						podHasSELinuxLabel := pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SELinuxOptions != nil
 | 
				
			||||||
 | 
						binds := makeMountBindings(opts.Mounts, podHasSELinuxLabel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The reason we create and mount the log file in here (not in kubelet) is because
 | 
						// The reason we create and mount the log file in here (not in kubelet) is because
 | 
				
			||||||
	// the file's location depends on the ID of the container, and we need to create and
 | 
						// the file's location depends on the ID of the container, and we need to create and
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
@@ -62,6 +63,7 @@ import (
 | 
				
			|||||||
	kubeletutil "k8s.io/kubernetes/pkg/kubelet/util"
 | 
						kubeletutil "k8s.io/kubernetes/pkg/kubelet/util"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/labels"
 | 
						"k8s.io/kubernetes/pkg/labels"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/securitycontext"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/types"
 | 
						"k8s.io/kubernetes/pkg/types"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util"
 | 
						"k8s.io/kubernetes/pkg/util"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/bandwidth"
 | 
						"k8s.io/kubernetes/pkg/util/bandwidth"
 | 
				
			||||||
@@ -73,6 +75,7 @@ import (
 | 
				
			|||||||
	nodeutil "k8s.io/kubernetes/pkg/util/node"
 | 
						nodeutil "k8s.io/kubernetes/pkg/util/node"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/oom"
 | 
						"k8s.io/kubernetes/pkg/util/oom"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/procfs"
 | 
						"k8s.io/kubernetes/pkg/util/procfs"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/selinux"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/version"
 | 
						"k8s.io/kubernetes/pkg/version"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume"
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
@@ -975,6 +978,47 @@ func (kl *Kubelet) syncNodeStatus() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// relabelVolumes relabels SELinux volumes to match the pod's
 | 
				
			||||||
 | 
					// SELinuxOptions specification. This is only needed if the pod uses
 | 
				
			||||||
 | 
					// hostPID or hostIPC. Otherwise relabeling is delegated to docker.
 | 
				
			||||||
 | 
					func (kl *Kubelet) relabelVolumes(pod *api.Pod, volumes kubecontainer.VolumeMap) error {
 | 
				
			||||||
 | 
						if pod.Spec.SecurityContext.SELinuxOptions == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rootDirContext, err := kl.getRootDirContext()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chconRunner := selinux.NewChconRunner()
 | 
				
			||||||
 | 
						// Apply the pod's Level to the rootDirContext
 | 
				
			||||||
 | 
						rootDirSELinuxOptions, err := securitycontext.ParseSELinuxOptions(rootDirContext)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rootDirSELinuxOptions.Level = pod.Spec.SecurityContext.SELinuxOptions.Level
 | 
				
			||||||
 | 
						volumeContext := fmt.Sprintf("%s:%s:%s:%s", rootDirSELinuxOptions.User, rootDirSELinuxOptions.Role, rootDirSELinuxOptions.Type, rootDirSELinuxOptions.Level)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, volume := range volumes {
 | 
				
			||||||
 | 
							if volume.Builder.SupportsSELinux() && !volume.Builder.IsReadOnly() {
 | 
				
			||||||
 | 
								// Relabel the volume and its content to match the 'Level' of the pod
 | 
				
			||||||
 | 
								err := filepath.Walk(volume.Builder.GetPath(), func(path string, info os.FileInfo, err error) error {
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return chconRunner.SetContext(path, volumeContext)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								volume.SELinuxLabeled = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeMounts(pod *api.Pod, podDir string, container *api.Container, podVolumes kubecontainer.VolumeMap) ([]kubecontainer.Mount, error) {
 | 
					func makeMounts(pod *api.Pod, podDir string, container *api.Container, podVolumes kubecontainer.VolumeMap) ([]kubecontainer.Mount, error) {
 | 
				
			||||||
	// Kubernetes only mounts on /etc/hosts if :
 | 
						// Kubernetes only mounts on /etc/hosts if :
 | 
				
			||||||
	// - container does not use hostNetwork and
 | 
						// - container does not use hostNetwork and
 | 
				
			||||||
@@ -991,11 +1035,21 @@ func makeMounts(pod *api.Pod, podDir string, container *api.Container, podVolume
 | 
				
			|||||||
			glog.Warningf("Mount cannot be satisified for container %q, because the volume is missing: %q", container.Name, mount)
 | 
								glog.Warningf("Mount cannot be satisified for container %q, because the volume is missing: %q", container.Name, mount)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							relabelVolume := false
 | 
				
			||||||
 | 
							// If the volume supports SELinux and it has not been
 | 
				
			||||||
 | 
							// relabeled already and it is not a read-only volume,
 | 
				
			||||||
 | 
							// relabel it and mark it as labeled
 | 
				
			||||||
 | 
							if vol.Builder.SupportsSELinux() && !vol.SELinuxLabeled && !vol.Builder.IsReadOnly() {
 | 
				
			||||||
 | 
								vol.SELinuxLabeled = true
 | 
				
			||||||
 | 
								relabelVolume = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		mounts = append(mounts, kubecontainer.Mount{
 | 
							mounts = append(mounts, kubecontainer.Mount{
 | 
				
			||||||
			Name:          mount.Name,
 | 
								Name:           mount.Name,
 | 
				
			||||||
			ContainerPath: mount.MountPath,
 | 
								ContainerPath:  mount.MountPath,
 | 
				
			||||||
			HostPath:      vol.GetPath(),
 | 
								HostPath:       vol.Builder.GetPath(),
 | 
				
			||||||
			ReadOnly:      mount.ReadOnly,
 | 
								ReadOnly:       mount.ReadOnly,
 | 
				
			||||||
 | 
								SELinuxRelabel: relabelVolume,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if mountEtcHostsFile {
 | 
						if mountEtcHostsFile {
 | 
				
			||||||
@@ -1080,6 +1134,16 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *api.Pod, container *api.Cont
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	opts.PortMappings = makePortMappings(container)
 | 
						opts.PortMappings = makePortMappings(container)
 | 
				
			||||||
 | 
						// Docker does not relabel volumes if the container is running
 | 
				
			||||||
 | 
						// in the host pid or ipc namespaces so the kubelet must
 | 
				
			||||||
 | 
						// relabel the volumes
 | 
				
			||||||
 | 
						if pod.Spec.SecurityContext != nil && (pod.Spec.SecurityContext.HostIPC || pod.Spec.SecurityContext.HostPID) {
 | 
				
			||||||
 | 
							err = kl.relabelVolumes(pod, vol)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	opts.Mounts, err = makeMounts(pod, kl.getPodDir(pod.UID), container, vol)
 | 
						opts.Mounts, err = makeMounts(pod, kl.getPodDir(pod.UID), container, vol)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -510,6 +510,26 @@ func (f *stubVolume) GetPath() string {
 | 
				
			|||||||
	return f.path
 | 
						return f.path
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *stubVolume) IsReadOnly() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *stubVolume) SetUp() error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *stubVolume) SetUpAt(dir string) error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *stubVolume) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *stubVolume) SupportsOwnershipManagement() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMakeVolumeMounts(t *testing.T) {
 | 
					func TestMakeVolumeMounts(t *testing.T) {
 | 
				
			||||||
	container := api.Container{
 | 
						container := api.Container{
 | 
				
			||||||
		VolumeMounts: []api.VolumeMount{
 | 
							VolumeMounts: []api.VolumeMount{
 | 
				
			||||||
@@ -537,9 +557,9 @@ func TestMakeVolumeMounts(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podVolumes := kubecontainer.VolumeMap{
 | 
						podVolumes := kubecontainer.VolumeMap{
 | 
				
			||||||
		"disk":  &stubVolume{"/mnt/disk"},
 | 
							"disk":  kubecontainer.VolumeInfo{Builder: &stubVolume{"/mnt/disk"}},
 | 
				
			||||||
		"disk4": &stubVolume{"/mnt/host"},
 | 
							"disk4": kubecontainer.VolumeInfo{Builder: &stubVolume{"/mnt/host"}},
 | 
				
			||||||
		"disk5": &stubVolume{"/var/lib/kubelet/podID/volumes/empty/disk5"},
 | 
							"disk5": kubecontainer.VolumeInfo{Builder: &stubVolume{"/var/lib/kubelet/podID/volumes/empty/disk5"}},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pod := api.Pod{
 | 
						pod := api.Pod{
 | 
				
			||||||
@@ -558,24 +578,28 @@ func TestMakeVolumeMounts(t *testing.T) {
 | 
				
			|||||||
			"/etc/hosts",
 | 
								"/etc/hosts",
 | 
				
			||||||
			"/mnt/disk",
 | 
								"/mnt/disk",
 | 
				
			||||||
			false,
 | 
								false,
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"disk",
 | 
								"disk",
 | 
				
			||||||
			"/mnt/path3",
 | 
								"/mnt/path3",
 | 
				
			||||||
			"/mnt/disk",
 | 
								"/mnt/disk",
 | 
				
			||||||
			true,
 | 
								true,
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"disk4",
 | 
								"disk4",
 | 
				
			||||||
			"/mnt/path4",
 | 
								"/mnt/path4",
 | 
				
			||||||
			"/mnt/host",
 | 
								"/mnt/host",
 | 
				
			||||||
			false,
 | 
								false,
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"disk5",
 | 
								"disk5",
 | 
				
			||||||
			"/mnt/path5",
 | 
								"/mnt/path5",
 | 
				
			||||||
			"/var/lib/kubelet/podID/volumes/empty/disk5",
 | 
								"/var/lib/kubelet/podID/volumes/empty/disk5",
 | 
				
			||||||
			false,
 | 
								false,
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !reflect.DeepEqual(mounts, expectedMounts) {
 | 
						if !reflect.DeepEqual(mounts, expectedMounts) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -488,7 +488,7 @@ func (r *Runtime) makePodManifest(pod *api.Pod, pullSecrets []api.Secret) (*appc
 | 
				
			|||||||
		manifest.Volumes = append(manifest.Volumes, appctypes.Volume{
 | 
							manifest.Volumes = append(manifest.Volumes, appctypes.Volume{
 | 
				
			||||||
			Name:   *volName,
 | 
								Name:   *volName,
 | 
				
			||||||
			Kind:   "host",
 | 
								Kind:   "host",
 | 
				
			||||||
			Source: volume.GetPath(),
 | 
								Source: volume.Builder.GetPath(),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@ import (
 | 
				
			|||||||
	"github.com/docker/libcontainer/selinux"
 | 
						"github.com/docker/libcontainer/selinux"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getRootContext gets the SELinux context of the kubelet rootDir
 | 
					// getRootDirContext gets the SELinux context of the kubelet rootDir
 | 
				
			||||||
// or returns an error.
 | 
					// or returns an error.
 | 
				
			||||||
func (kl *Kubelet) getRootDirContext() (string, error) {
 | 
					func (kl *Kubelet) getRootDirContext() (string, error) {
 | 
				
			||||||
	// If SELinux is not enabled, return an empty string
 | 
						// If SELinux is not enabled, return an empty string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -146,7 +146,7 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap,
 | 
				
			|||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		podVolumes[volSpec.Name] = builder
 | 
							podVolumes[volSpec.Name] = kubecontainer.VolumeInfo{Builder: builder}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return podVolumes, nil
 | 
						return podVolumes, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								pkg/util/selinux/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								pkg/util/selinux/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2015 The Kubernetes Authors All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package selinux contains selinux utility functions.
 | 
				
			||||||
 | 
					package selinux
 | 
				
			||||||
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package empty_dir
 | 
					package selinux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// chconRunner knows how to chcon a directory.
 | 
					// chconRunner knows how to chcon a directory.
 | 
				
			||||||
type chconRunner interface {
 | 
					type ChconRunner interface {
 | 
				
			||||||
	SetContext(dir, context string) error
 | 
						SetContext(dir, context string) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newChconRunner returns a new chconRunner.
 | 
					// newChconRunner returns a new chconRunner.
 | 
				
			||||||
func newChconRunner() chconRunner {
 | 
					func NewChconRunner() ChconRunner {
 | 
				
			||||||
	return &realChconRunner{}
 | 
						return &realChconRunner{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package empty_dir
 | 
					package selinux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/docker/libcontainer/selinux"
 | 
						"github.com/docker/libcontainer/selinux"
 | 
				
			||||||
@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package empty_dir
 | 
					package selinux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type realChconRunner struct{}
 | 
					type realChconRunner struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -250,6 +250,10 @@ func (b *awsElasticBlockStoreBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *awsElasticBlockStoreBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeGlobalPDPath(host volume.VolumeHost, volumeID string) string {
 | 
					func makeGlobalPDPath(host volume.VolumeHost, volumeID string) string {
 | 
				
			||||||
	// Clean up the URI to be more fs-friendly
 | 
						// Clean up the URI to be more fs-friendly
 | 
				
			||||||
	name := volumeID
 | 
						name := volumeID
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -187,6 +187,10 @@ func (cephfsVolume *cephfsBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return cephfsVolume.readonly
 | 
						return cephfsVolume.readonly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cephfsVolume *cephfsBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type cephfsCleaner struct {
 | 
					type cephfsCleaner struct {
 | 
				
			||||||
	*cephfs
 | 
						*cephfs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -225,6 +225,10 @@ func (b *cinderVolumeBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *cinderVolumeBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeGlobalPDName(host volume.VolumeHost, devName string) string {
 | 
					func makeGlobalPDName(host volume.VolumeHost, devName string) string {
 | 
				
			||||||
	return path.Join(host.GetPluginDir(cinderVolumePluginName), "mounts", devName)
 | 
						return path.Join(host.GetPluginDir(cinderVolumePluginName), "mounts", devName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -157,6 +157,10 @@ func (d *downwardAPIVolume) IsReadOnly() bool {
 | 
				
			|||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *downwardAPIVolume) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// collectData collects requested downwardAPI in data map.
 | 
					// collectData collects requested downwardAPI in data map.
 | 
				
			||||||
// Map's key is the requested name of file to dump
 | 
					// Map's key is the requested name of file to dump
 | 
				
			||||||
// Map's value is the (sorted) content of the field to be dumped in the file.
 | 
					// Map's value is the (sorted) content of the field to be dumped in the file.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,10 +70,10 @@ func (plugin *emptyDirPlugin) CanSupport(spec *volume.Spec) bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (plugin *emptyDirPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) {
 | 
					func (plugin *emptyDirPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) {
 | 
				
			||||||
	return plugin.newBuilderInternal(spec, pod, plugin.host.GetMounter(), &realMountDetector{plugin.host.GetMounter()}, opts, newChconRunner())
 | 
						return plugin.newBuilderInternal(spec, pod, plugin.host.GetMounter(), &realMountDetector{plugin.host.GetMounter()}, opts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (plugin *emptyDirPlugin) newBuilderInternal(spec *volume.Spec, pod *api.Pod, mounter mount.Interface, mountDetector mountDetector, opts volume.VolumeOptions, chconRunner chconRunner) (volume.Builder, error) {
 | 
					func (plugin *emptyDirPlugin) newBuilderInternal(spec *volume.Spec, pod *api.Pod, mounter mount.Interface, mountDetector mountDetector, opts volume.VolumeOptions) (volume.Builder, error) {
 | 
				
			||||||
	medium := api.StorageMediumDefault
 | 
						medium := api.StorageMediumDefault
 | 
				
			||||||
	if spec.Volume.EmptyDir != nil { // Support a non-specified source as EmptyDir.
 | 
						if spec.Volume.EmptyDir != nil { // Support a non-specified source as EmptyDir.
 | 
				
			||||||
		medium = spec.Volume.EmptyDir.Medium
 | 
							medium = spec.Volume.EmptyDir.Medium
 | 
				
			||||||
@@ -86,7 +86,6 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *volume.Spec, pod *api.Pod
 | 
				
			|||||||
		mountDetector: mountDetector,
 | 
							mountDetector: mountDetector,
 | 
				
			||||||
		plugin:        plugin,
 | 
							plugin:        plugin,
 | 
				
			||||||
		rootContext:   opts.RootContext,
 | 
							rootContext:   opts.RootContext,
 | 
				
			||||||
		chconRunner:   chconRunner,
 | 
					 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -134,7 +133,6 @@ type emptyDir struct {
 | 
				
			|||||||
	mountDetector mountDetector
 | 
						mountDetector mountDetector
 | 
				
			||||||
	plugin        *emptyDirPlugin
 | 
						plugin        *emptyDirPlugin
 | 
				
			||||||
	rootContext   string
 | 
						rootContext   string
 | 
				
			||||||
	chconRunner   chconRunner
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (_ *emptyDir) SupportsOwnershipManagement() bool {
 | 
					func (_ *emptyDir) SupportsOwnershipManagement() bool {
 | 
				
			||||||
@@ -175,7 +173,7 @@ func (ed *emptyDir) SetUpAt(dir string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	switch ed.medium {
 | 
						switch ed.medium {
 | 
				
			||||||
	case api.StorageMediumDefault:
 | 
						case api.StorageMediumDefault:
 | 
				
			||||||
		err = ed.setupDir(dir, securityContext)
 | 
							err = ed.setupDir(dir)
 | 
				
			||||||
	case api.StorageMediumMemory:
 | 
						case api.StorageMediumMemory:
 | 
				
			||||||
		err = ed.setupTmpfs(dir, securityContext)
 | 
							err = ed.setupTmpfs(dir, securityContext)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
@@ -193,13 +191,17 @@ func (ed *emptyDir) IsReadOnly() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ed *emptyDir) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setupTmpfs creates a tmpfs mount at the specified directory with the
 | 
					// setupTmpfs creates a tmpfs mount at the specified directory with the
 | 
				
			||||||
// specified SELinux context.
 | 
					// specified SELinux context.
 | 
				
			||||||
func (ed *emptyDir) setupTmpfs(dir string, selinuxContext string) error {
 | 
					func (ed *emptyDir) setupTmpfs(dir string, selinuxContext string) error {
 | 
				
			||||||
	if ed.mounter == nil {
 | 
						if ed.mounter == nil {
 | 
				
			||||||
		return fmt.Errorf("memory storage requested, but mounter is nil")
 | 
							return fmt.Errorf("memory storage requested, but mounter is nil")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := ed.setupDir(dir, selinuxContext); err != nil {
 | 
						if err := ed.setupDir(dir); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Make SetUp idempotent.
 | 
						// Make SetUp idempotent.
 | 
				
			||||||
@@ -228,7 +230,7 @@ func (ed *emptyDir) setupTmpfs(dir string, selinuxContext string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// setupDir creates the directory with the specified SELinux context and
 | 
					// setupDir creates the directory with the specified SELinux context and
 | 
				
			||||||
// the default permissions specified by the perm constant.
 | 
					// the default permissions specified by the perm constant.
 | 
				
			||||||
func (ed *emptyDir) setupDir(dir, selinuxContext string) error {
 | 
					func (ed *emptyDir) setupDir(dir string) error {
 | 
				
			||||||
	// Create the directory if it doesn't already exist.
 | 
						// Create the directory if it doesn't already exist.
 | 
				
			||||||
	if err := os.MkdirAll(dir, perm); err != nil {
 | 
						if err := os.MkdirAll(dir, perm); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -262,12 +264,6 @@ func (ed *emptyDir) setupDir(dir, selinuxContext string) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set the context on the directory, if appropriate
 | 
					 | 
				
			||||||
	if selinuxContext != "" {
 | 
					 | 
				
			||||||
		glog.V(3).Infof("Setting SELinux context for %v to %v", dir, selinuxContext)
 | 
					 | 
				
			||||||
		return ed.chconRunner.SetContext(dir, selinuxContext)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,30 +69,10 @@ func (fake *fakeMountDetector) GetMountMedium(path string) (storageMedium, bool,
 | 
				
			|||||||
	return fake.medium, fake.isMount, nil
 | 
						return fake.medium, fake.isMount, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type fakeChconRequest struct {
 | 
					 | 
				
			||||||
	dir     string
 | 
					 | 
				
			||||||
	context string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type fakeChconRunner struct {
 | 
					 | 
				
			||||||
	requests []fakeChconRequest
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newFakeChconRunner() *fakeChconRunner {
 | 
					 | 
				
			||||||
	return &fakeChconRunner{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (f *fakeChconRunner) SetContext(dir, context string) error {
 | 
					 | 
				
			||||||
	f.requests = append(f.requests, fakeChconRequest{dir, context})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestPluginEmptyRootContext(t *testing.T) {
 | 
					func TestPluginEmptyRootContext(t *testing.T) {
 | 
				
			||||||
	doTestPlugin(t, pluginTestConfig{
 | 
						doTestPlugin(t, pluginTestConfig{
 | 
				
			||||||
		medium:                 api.StorageMediumDefault,
 | 
							medium:                 api.StorageMediumDefault,
 | 
				
			||||||
		rootContext:            "",
 | 
							rootContext:            "",
 | 
				
			||||||
		expectedChcons:         0,
 | 
					 | 
				
			||||||
		expectedSetupMounts:    0,
 | 
							expectedSetupMounts:    0,
 | 
				
			||||||
		expectedTeardownMounts: 0})
 | 
							expectedTeardownMounts: 0})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -106,7 +86,6 @@ func TestPluginRootContextSet(t *testing.T) {
 | 
				
			|||||||
		medium:                 api.StorageMediumDefault,
 | 
							medium:                 api.StorageMediumDefault,
 | 
				
			||||||
		rootContext:            "user:role:type:range",
 | 
							rootContext:            "user:role:type:range",
 | 
				
			||||||
		expectedSELinuxContext: "user:role:type:range",
 | 
							expectedSELinuxContext: "user:role:type:range",
 | 
				
			||||||
		expectedChcons:         1,
 | 
					 | 
				
			||||||
		expectedSetupMounts:    0,
 | 
							expectedSetupMounts:    0,
 | 
				
			||||||
		expectedTeardownMounts: 0})
 | 
							expectedTeardownMounts: 0})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -120,7 +99,6 @@ func TestPluginTmpfs(t *testing.T) {
 | 
				
			|||||||
		medium:                        api.StorageMediumMemory,
 | 
							medium:                        api.StorageMediumMemory,
 | 
				
			||||||
		rootContext:                   "user:role:type:range",
 | 
							rootContext:                   "user:role:type:range",
 | 
				
			||||||
		expectedSELinuxContext:        "user:role:type:range",
 | 
							expectedSELinuxContext:        "user:role:type:range",
 | 
				
			||||||
		expectedChcons:                1,
 | 
					 | 
				
			||||||
		expectedSetupMounts:           1,
 | 
							expectedSetupMounts:           1,
 | 
				
			||||||
		shouldBeMountedBeforeTeardown: true,
 | 
							shouldBeMountedBeforeTeardown: true,
 | 
				
			||||||
		expectedTeardownMounts:        1})
 | 
							expectedTeardownMounts:        1})
 | 
				
			||||||
@@ -132,7 +110,6 @@ type pluginTestConfig struct {
 | 
				
			|||||||
	SELinuxOptions                *api.SELinuxOptions
 | 
						SELinuxOptions                *api.SELinuxOptions
 | 
				
			||||||
	idempotent                    bool
 | 
						idempotent                    bool
 | 
				
			||||||
	expectedSELinuxContext        string
 | 
						expectedSELinuxContext        string
 | 
				
			||||||
	expectedChcons                int
 | 
					 | 
				
			||||||
	expectedSetupMounts           int
 | 
						expectedSetupMounts           int
 | 
				
			||||||
	shouldBeMountedBeforeTeardown bool
 | 
						shouldBeMountedBeforeTeardown bool
 | 
				
			||||||
	expectedTeardownMounts        int
 | 
						expectedTeardownMounts        int
 | 
				
			||||||
@@ -160,7 +137,6 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) {
 | 
				
			|||||||
		mounter       = mount.FakeMounter{}
 | 
							mounter       = mount.FakeMounter{}
 | 
				
			||||||
		mountDetector = fakeMountDetector{}
 | 
							mountDetector = fakeMountDetector{}
 | 
				
			||||||
		pod           = &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
 | 
							pod           = &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
 | 
				
			||||||
		fakeChconRnr  = &fakeChconRunner{}
 | 
					 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set up the SELinux options on the pod
 | 
						// Set up the SELinux options on the pod
 | 
				
			||||||
@@ -194,8 +170,7 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) {
 | 
				
			|||||||
		pod,
 | 
							pod,
 | 
				
			||||||
		&mounter,
 | 
							&mounter,
 | 
				
			||||||
		&mountDetector,
 | 
							&mountDetector,
 | 
				
			||||||
		volume.VolumeOptions{RootContext: config.rootContext},
 | 
							volume.VolumeOptions{RootContext: config.rootContext})
 | 
				
			||||||
		fakeChconRnr)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("Failed to make a new Builder: %v", err)
 | 
							t.Errorf("Failed to make a new Builder: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -231,19 +206,6 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) {
 | 
				
			|||||||
		t.Errorf("Volume directory was created unexpectedly")
 | 
							t.Errorf("Volume directory was created unexpectedly")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check the number of chcons during setup
 | 
					 | 
				
			||||||
	if e, a := config.expectedChcons, len(fakeChconRnr.requests); e != a {
 | 
					 | 
				
			||||||
		t.Errorf("Expected %v chcon calls, got %v", e, a)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if config.expectedChcons == 1 {
 | 
					 | 
				
			||||||
		if e, a := config.expectedSELinuxContext, fakeChconRnr.requests[0].context; e != a {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected chcon context argument; expected: %v, got: %v", e, a)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if e, a := volPath, fakeChconRnr.requests[0].dir; e != a {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected chcon path argument: expected: %v, got: %v", e, a)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check the number of mounts performed during setup
 | 
						// Check the number of mounts performed during setup
 | 
				
			||||||
	if e, a := config.expectedSetupMounts, len(mounter.Log); e != a {
 | 
						if e, a := config.expectedSetupMounts, len(mounter.Log); e != a {
 | 
				
			||||||
		t.Errorf("Expected %v mounter calls during setup, got %v", e, a)
 | 
							t.Errorf("Expected %v mounter calls during setup, got %v", e, a)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,6 +191,10 @@ func (b *fcDiskBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *fcDiskBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Unmounts the bind mount, and detaches the disk only if the disk
 | 
					// Unmounts the bind mount, and detaches the disk only if the disk
 | 
				
			||||||
// resource was the last reference to that disk on the kubelet.
 | 
					// resource was the last reference to that disk on the kubelet.
 | 
				
			||||||
func (c *fcDiskCleaner) TearDown() error {
 | 
					func (c *fcDiskCleaner) TearDown() error {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -205,6 +205,10 @@ func (b flockerBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b flockerBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// updateDatasetPrimary will update the primary in Flocker and wait for it to
 | 
					// updateDatasetPrimary will update the primary in Flocker and wait for it to
 | 
				
			||||||
// be ready. If it never gets to ready state it will timeout and error.
 | 
					// be ready. If it never gets to ready state it will timeout and error.
 | 
				
			||||||
func (b flockerBuilder) updateDatasetPrimary(datasetID, primaryUUID string) error {
 | 
					func (b flockerBuilder) updateDatasetPrimary(datasetID, primaryUUID string) error {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,6 +238,10 @@ func (b *gcePersistentDiskBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *gcePersistentDiskBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeGlobalPDName(host volume.VolumeHost, devName string) string {
 | 
					func makeGlobalPDName(host volume.VolumeHost, devName string) string {
 | 
				
			||||||
	return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), "mounts", devName)
 | 
						return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), "mounts", devName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,6 +122,10 @@ func (b *gitRepoVolumeBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *gitRepoVolumeBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This is the spec for the volume that this plugin wraps.
 | 
					// This is the spec for the volume that this plugin wraps.
 | 
				
			||||||
var wrappedVolumeSpec = &volume.Spec{
 | 
					var wrappedVolumeSpec = &volume.Spec{
 | 
				
			||||||
	Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
 | 
						Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -189,6 +189,10 @@ func (b *glusterfsBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *glusterfsBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (glusterfsVolume *glusterfs) GetPath() string {
 | 
					func (glusterfsVolume *glusterfs) GetPath() string {
 | 
				
			||||||
	name := glusterfsPluginName
 | 
						name := glusterfsPluginName
 | 
				
			||||||
	return glusterfsVolume.plugin.host.GetPodVolumeDir(glusterfsVolume.pod.UID, util.EscapeQualifiedNameForDisk(name), glusterfsVolume.volName)
 | 
						return glusterfsVolume.plugin.host.GetPodVolumeDir(glusterfsVolume.pod.UID, util.EscapeQualifiedNameForDisk(name), glusterfsVolume.volName)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -185,6 +185,10 @@ func (b *hostPathBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *hostPathBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *hostPathBuilder) GetPath() string {
 | 
					func (b *hostPathBuilder) GetPath() string {
 | 
				
			||||||
	return b.path
 | 
						return b.path
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -185,6 +185,10 @@ func (b *iscsiDiskBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *iscsiDiskBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Unmounts the bind mount, and detaches the disk only if the disk
 | 
					// Unmounts the bind mount, and detaches the disk only if the disk
 | 
				
			||||||
// resource was the last reference to that disk on the kubelet.
 | 
					// resource was the last reference to that disk on the kubelet.
 | 
				
			||||||
func (c *iscsiDiskCleaner) TearDown() error {
 | 
					func (c *iscsiDiskCleaner) TearDown() error {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -226,6 +226,10 @@ func (b *nfsBuilder) IsReadOnly() bool {
 | 
				
			|||||||
	return b.readOnly
 | 
						return b.readOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *nfsBuilder) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//func (c *nfsCleaner) GetPath() string {
 | 
					//func (c *nfsCleaner) GetPath() string {
 | 
				
			||||||
//	name := nfsPluginName
 | 
					//	name := nfsPluginName
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -219,6 +219,10 @@ func (b *rbd) IsReadOnly() bool {
 | 
				
			|||||||
	return b.ReadOnly
 | 
						return b.ReadOnly
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *rbd) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Unmounts the bind mount, and detaches the disk only if the disk
 | 
					// Unmounts the bind mount, and detaches the disk only if the disk
 | 
				
			||||||
// resource was the last reference to that disk on the kubelet.
 | 
					// resource was the last reference to that disk on the kubelet.
 | 
				
			||||||
func (c *rbdCleaner) TearDown() error {
 | 
					func (c *rbdCleaner) TearDown() error {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -176,6 +176,10 @@ func (sv *secretVolume) IsReadOnly() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sv *secretVolume) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func totalSecretBytes(secret *api.Secret) int {
 | 
					func totalSecretBytes(secret *api.Secret) int {
 | 
				
			||||||
	totalSize := 0
 | 
						totalSize := 0
 | 
				
			||||||
	for _, bytes := range secret.Data {
 | 
						for _, bytes := range secret.Data {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -172,6 +172,10 @@ func (fv *FakeVolume) IsReadOnly() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (fv *FakeVolume) SupportsSELinux() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (fv *FakeVolume) GetPath() string {
 | 
					func (fv *FakeVolume) GetPath() string {
 | 
				
			||||||
	return path.Join(fv.Plugin.Host.GetPodVolumeDir(fv.PodUID, util.EscapeQualifiedNameForDisk(fv.Plugin.PluginName), fv.VolName))
 | 
						return path.Join(fv.Plugin.Host.GetPodVolumeDir(fv.PodUID, util.EscapeQualifiedNameForDisk(fv.Plugin.PluginName), fv.VolName))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,10 @@ type Builder interface {
 | 
				
			|||||||
	// 2. Set the setgid bit is set (new files created in the volume will be owned by FSGroup)
 | 
						// 2. Set the setgid bit is set (new files created in the volume will be owned by FSGroup)
 | 
				
			||||||
	// 3. Logical OR the permission bits with rw-rw----
 | 
						// 3. Logical OR the permission bits with rw-rw----
 | 
				
			||||||
	SupportsOwnershipManagement() bool
 | 
						SupportsOwnershipManagement() bool
 | 
				
			||||||
 | 
						// SupportsSELinux reports whether the given builder supports
 | 
				
			||||||
 | 
						// SELinux and would like the kubelet to relabel the volume to
 | 
				
			||||||
 | 
						// match the pod to which it will be attached.
 | 
				
			||||||
 | 
						SupportsSELinux() bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Cleaner interface provides methods to cleanup/unmount the volumes.
 | 
					// Cleaner interface provides methods to cleanup/unmount the volumes.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,9 +29,10 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/util"
 | 
						"k8s.io/kubernetes/pkg/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	. "github.com/onsi/ginkgo"
 | 
						. "github.com/onsi/ginkgo"
 | 
				
			||||||
 | 
						. "github.com/onsi/gomega"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func scTestPod() *api.Pod {
 | 
					func scTestPod(hostIPC bool, hostPID bool) *api.Pod {
 | 
				
			||||||
	podName := "security-context-" + string(util.NewUUID())
 | 
						podName := "security-context-" + string(util.NewUUID())
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
@@ -39,7 +40,10 @@ func scTestPod() *api.Pod {
 | 
				
			|||||||
			Labels: map[string]string{"name": podName},
 | 
								Labels: map[string]string{"name": podName},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			SecurityContext: &api.PodSecurityContext{},
 | 
								SecurityContext: &api.PodSecurityContext{
 | 
				
			||||||
 | 
									HostIPC: hostIPC,
 | 
				
			||||||
 | 
									HostPID: hostPID,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			Containers: []api.Container{
 | 
								Containers: []api.Container{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					Name:  "test-container",
 | 
										Name:  "test-container",
 | 
				
			||||||
@@ -57,7 +61,7 @@ var _ = Describe("[Skipped] Security Context", func() {
 | 
				
			|||||||
	framework := NewFramework("security-context")
 | 
						framework := NewFramework("security-context")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	It("should support pod.Spec.SecurityContext.SupplementalGroups", func() {
 | 
						It("should support pod.Spec.SecurityContext.SupplementalGroups", func() {
 | 
				
			||||||
		pod := scTestPod()
 | 
							pod := scTestPod(false, false)
 | 
				
			||||||
		pod.Spec.Containers[0].Command = []string{"id", "-G"}
 | 
							pod.Spec.Containers[0].Command = []string{"id", "-G"}
 | 
				
			||||||
		pod.Spec.SecurityContext.SupplementalGroups = []int64{1234, 5678}
 | 
							pod.Spec.SecurityContext.SupplementalGroups = []int64{1234, 5678}
 | 
				
			||||||
		groups := []string{"1234", "5678"}
 | 
							groups := []string{"1234", "5678"}
 | 
				
			||||||
@@ -65,7 +69,7 @@ var _ = Describe("[Skipped] Security Context", func() {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	It("should support pod.Spec.SecurityContext.RunAsUser", func() {
 | 
						It("should support pod.Spec.SecurityContext.RunAsUser", func() {
 | 
				
			||||||
		pod := scTestPod()
 | 
							pod := scTestPod(false, false)
 | 
				
			||||||
		var uid int64 = 1001
 | 
							var uid int64 = 1001
 | 
				
			||||||
		pod.Spec.SecurityContext.RunAsUser = &uid
 | 
							pod.Spec.SecurityContext.RunAsUser = &uid
 | 
				
			||||||
		pod.Spec.Containers[0].Command = []string{"sh", "-c", "id -u"}
 | 
							pod.Spec.Containers[0].Command = []string{"sh", "-c", "id -u"}
 | 
				
			||||||
@@ -76,7 +80,7 @@ var _ = Describe("[Skipped] Security Context", func() {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	It("should support container.SecurityContext.RunAsUser", func() {
 | 
						It("should support container.SecurityContext.RunAsUser", func() {
 | 
				
			||||||
		pod := scTestPod()
 | 
							pod := scTestPod(false, false)
 | 
				
			||||||
		var uid int64 = 1001
 | 
							var uid int64 = 1001
 | 
				
			||||||
		var overrideUid int64 = 1002
 | 
							var overrideUid int64 = 1002
 | 
				
			||||||
		pod.Spec.SecurityContext.RunAsUser = &uid
 | 
							pod.Spec.SecurityContext.RunAsUser = &uid
 | 
				
			||||||
@@ -88,4 +92,112 @@ var _ = Describe("[Skipped] Security Context", func() {
 | 
				
			|||||||
			fmt.Sprintf("%v", overrideUid),
 | 
								fmt.Sprintf("%v", overrideUid),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						It("should support volume SELinux relabeling", func() {
 | 
				
			||||||
 | 
							testPodSELinuxLabeling(framework, false, false)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						It("should support volume SELinux relabeling when using hostIPC", func() {
 | 
				
			||||||
 | 
							testPodSELinuxLabeling(framework, true, false)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						It("should support volume SELinux relabeling when using hostPID", func() {
 | 
				
			||||||
 | 
							testPodSELinuxLabeling(framework, false, true)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testPodSELinuxLabeling(framework *Framework, hostIPC bool, hostPID bool) {
 | 
				
			||||||
 | 
						// Write and read a file with an empty_dir volume
 | 
				
			||||||
 | 
						// with a pod with the MCS label s0:c0,c1
 | 
				
			||||||
 | 
						pod := scTestPod(hostIPC, hostPID)
 | 
				
			||||||
 | 
						volumeName := "test-volume"
 | 
				
			||||||
 | 
						mountPath := "/mounted_volume"
 | 
				
			||||||
 | 
						pod.Spec.Containers[0].VolumeMounts = []api.VolumeMount{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:      volumeName,
 | 
				
			||||||
 | 
								MountPath: mountPath,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pod.Spec.Volumes = []api.Volume{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name: volumeName,
 | 
				
			||||||
 | 
								VolumeSource: api.VolumeSource{
 | 
				
			||||||
 | 
									EmptyDir: &api.EmptyDirVolumeSource{
 | 
				
			||||||
 | 
										Medium: api.StorageMediumDefault,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
 | 
				
			||||||
 | 
							Level: "s0:c0,c1",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pod.Spec.Containers[0].Command = []string{"sleep", "6000"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := framework.Client.Pods(framework.Namespace.Name)
 | 
				
			||||||
 | 
						_, err := client.Create(pod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expectNoError(err, "Error creating pod %v", pod)
 | 
				
			||||||
 | 
						defer client.Delete(pod.Name, nil)
 | 
				
			||||||
 | 
						expectNoError(waitForPodRunningInNamespace(framework.Client, pod.Name, framework.Namespace.Name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testContent := "hello"
 | 
				
			||||||
 | 
						testFilePath := mountPath + "/TEST"
 | 
				
			||||||
 | 
						err = framework.WriteFileViaContainer(pod.Name, pod.Spec.Containers[0].Name, testFilePath, testContent)
 | 
				
			||||||
 | 
						Expect(err).To(BeNil())
 | 
				
			||||||
 | 
						content, err := framework.ReadFileViaContainer(pod.Name, pod.Spec.Containers[0].Name, testFilePath)
 | 
				
			||||||
 | 
						Expect(err).To(BeNil())
 | 
				
			||||||
 | 
						Expect(content).To(ContainSubstring(testContent))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						foundPod, err := framework.Client.Pods(framework.Namespace.Name).Get(pod.Name)
 | 
				
			||||||
 | 
						Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Confirm that the file can be accessed from a second
 | 
				
			||||||
 | 
						// pod using host_path with the same MCS label
 | 
				
			||||||
 | 
						volumeHostPath := fmt.Sprintf("/var/lib/kubelet/pods/%s/volumes/kubernetes.io~empty-dir/%s", foundPod.UID, volumeName)
 | 
				
			||||||
 | 
						By("confirming a container with the same label can read the file")
 | 
				
			||||||
 | 
						pod = scTestPod(hostIPC, hostPID)
 | 
				
			||||||
 | 
						pod.Spec.NodeName = foundPod.Spec.NodeName
 | 
				
			||||||
 | 
						volumeMounts := []api.VolumeMount{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:      volumeName,
 | 
				
			||||||
 | 
								MountPath: mountPath,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						volumes := []api.Volume{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name: volumeName,
 | 
				
			||||||
 | 
								VolumeSource: api.VolumeSource{
 | 
				
			||||||
 | 
									HostPath: &api.HostPathVolumeSource{
 | 
				
			||||||
 | 
										Path: volumeHostPath,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pod.Spec.Containers[0].VolumeMounts = volumeMounts
 | 
				
			||||||
 | 
						pod.Spec.Volumes = volumes
 | 
				
			||||||
 | 
						pod.Spec.Containers[0].Command = []string{"cat", testFilePath}
 | 
				
			||||||
 | 
						pod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
 | 
				
			||||||
 | 
							Level: "s0:c0,c1",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						framework.TestContainerOutput("Pod with same MCS label reading test file", pod, 0, []string{testContent})
 | 
				
			||||||
 | 
						// Confirm that the same pod with a different MCS
 | 
				
			||||||
 | 
						// label cannot access the volume
 | 
				
			||||||
 | 
						pod = scTestPod(hostIPC, hostPID)
 | 
				
			||||||
 | 
						pod.Spec.Volumes = volumes
 | 
				
			||||||
 | 
						pod.Spec.Containers[0].VolumeMounts = volumeMounts
 | 
				
			||||||
 | 
						pod.Spec.Containers[0].Command = []string{"sleep", "6000"}
 | 
				
			||||||
 | 
						pod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
 | 
				
			||||||
 | 
							Level: "s0:c2,c3",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.Create(pod)
 | 
				
			||||||
 | 
						expectNoError(err, "Error creating pod %v", pod)
 | 
				
			||||||
 | 
						defer client.Delete(pod.Name, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = framework.WaitForPodRunning(pod.Name)
 | 
				
			||||||
 | 
						expectNoError(err, "Error waiting for pod to run %v", pod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						content, err = framework.ReadFileViaContainer(pod.Name, "test-container", testFilePath)
 | 
				
			||||||
 | 
						Expect(content).NotTo(ContainSubstring(testContent))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user