Support volume relabling for pods which specify an SELinux label
This commit is contained in:
@@ -250,6 +250,10 @@ func (b *awsElasticBlockStoreBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b *awsElasticBlockStoreBuilder) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func makeGlobalPDPath(host volume.VolumeHost, volumeID string) string {
|
||||
// Clean up the URI to be more fs-friendly
|
||||
name := volumeID
|
||||
|
||||
@@ -187,6 +187,10 @@ func (cephfsVolume *cephfsBuilder) IsReadOnly() bool {
|
||||
return cephfsVolume.readonly
|
||||
}
|
||||
|
||||
func (cephfsVolume *cephfsBuilder) SupportsSELinux() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type cephfsCleaner struct {
|
||||
*cephfs
|
||||
}
|
||||
|
||||
@@ -225,6 +225,10 @@ func (b *cinderVolumeBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b *cinderVolumeBuilder) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func makeGlobalPDName(host volume.VolumeHost, devName string) string {
|
||||
return path.Join(host.GetPluginDir(cinderVolumePluginName), "mounts", devName)
|
||||
}
|
||||
|
||||
@@ -157,6 +157,10 @@ func (d *downwardAPIVolume) IsReadOnly() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *downwardAPIVolume) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// collectData collects requested downwardAPI in data map.
|
||||
// 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.
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 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 empty_dir
|
||||
|
||||
// chconRunner knows how to chcon a directory.
|
||||
type chconRunner interface {
|
||||
SetContext(dir, context string) error
|
||||
}
|
||||
|
||||
// newChconRunner returns a new chconRunner.
|
||||
func newChconRunner() chconRunner {
|
||||
return &realChconRunner{}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 empty_dir
|
||||
|
||||
import (
|
||||
"github.com/docker/libcontainer/selinux"
|
||||
)
|
||||
|
||||
type realChconRunner struct{}
|
||||
|
||||
func (_ *realChconRunner) SetContext(dir, context string) error {
|
||||
// If SELinux is not enabled, return an empty string
|
||||
if !selinux.SelinuxEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return selinux.Setfilecon(dir, context)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 empty_dir
|
||||
|
||||
type realChconRunner struct{}
|
||||
|
||||
func (_ *realChconRunner) SetContext(dir, context string) error {
|
||||
// NOP
|
||||
return nil
|
||||
}
|
||||
@@ -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) {
|
||||
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
|
||||
if spec.Volume.EmptyDir != nil { // Support a non-specified source as EmptyDir.
|
||||
medium = spec.Volume.EmptyDir.Medium
|
||||
@@ -86,7 +86,6 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *volume.Spec, pod *api.Pod
|
||||
mountDetector: mountDetector,
|
||||
plugin: plugin,
|
||||
rootContext: opts.RootContext,
|
||||
chconRunner: chconRunner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -134,7 +133,6 @@ type emptyDir struct {
|
||||
mountDetector mountDetector
|
||||
plugin *emptyDirPlugin
|
||||
rootContext string
|
||||
chconRunner chconRunner
|
||||
}
|
||||
|
||||
func (_ *emptyDir) SupportsOwnershipManagement() bool {
|
||||
@@ -175,7 +173,7 @@ func (ed *emptyDir) SetUpAt(dir string) error {
|
||||
|
||||
switch ed.medium {
|
||||
case api.StorageMediumDefault:
|
||||
err = ed.setupDir(dir, securityContext)
|
||||
err = ed.setupDir(dir)
|
||||
case api.StorageMediumMemory:
|
||||
err = ed.setupTmpfs(dir, securityContext)
|
||||
default:
|
||||
@@ -193,13 +191,17 @@ func (ed *emptyDir) IsReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (ed *emptyDir) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// setupTmpfs creates a tmpfs mount at the specified directory with the
|
||||
// specified SELinux context.
|
||||
func (ed *emptyDir) setupTmpfs(dir string, selinuxContext string) error {
|
||||
if ed.mounter == 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
|
||||
}
|
||||
// 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
|
||||
// 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.
|
||||
if err := os.MkdirAll(dir, perm); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -69,30 +69,10 @@ func (fake *fakeMountDetector) GetMountMedium(path string) (storageMedium, bool,
|
||||
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) {
|
||||
doTestPlugin(t, pluginTestConfig{
|
||||
medium: api.StorageMediumDefault,
|
||||
rootContext: "",
|
||||
expectedChcons: 0,
|
||||
expectedSetupMounts: 0,
|
||||
expectedTeardownMounts: 0})
|
||||
}
|
||||
@@ -106,7 +86,6 @@ func TestPluginRootContextSet(t *testing.T) {
|
||||
medium: api.StorageMediumDefault,
|
||||
rootContext: "user:role:type:range",
|
||||
expectedSELinuxContext: "user:role:type:range",
|
||||
expectedChcons: 1,
|
||||
expectedSetupMounts: 0,
|
||||
expectedTeardownMounts: 0})
|
||||
}
|
||||
@@ -120,7 +99,6 @@ func TestPluginTmpfs(t *testing.T) {
|
||||
medium: api.StorageMediumMemory,
|
||||
rootContext: "user:role:type:range",
|
||||
expectedSELinuxContext: "user:role:type:range",
|
||||
expectedChcons: 1,
|
||||
expectedSetupMounts: 1,
|
||||
shouldBeMountedBeforeTeardown: true,
|
||||
expectedTeardownMounts: 1})
|
||||
@@ -132,7 +110,6 @@ type pluginTestConfig struct {
|
||||
SELinuxOptions *api.SELinuxOptions
|
||||
idempotent bool
|
||||
expectedSELinuxContext string
|
||||
expectedChcons int
|
||||
expectedSetupMounts int
|
||||
shouldBeMountedBeforeTeardown bool
|
||||
expectedTeardownMounts int
|
||||
@@ -160,7 +137,6 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) {
|
||||
mounter = mount.FakeMounter{}
|
||||
mountDetector = fakeMountDetector{}
|
||||
pod = &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
|
||||
fakeChconRnr = &fakeChconRunner{}
|
||||
)
|
||||
|
||||
// Set up the SELinux options on the pod
|
||||
@@ -194,8 +170,7 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) {
|
||||
pod,
|
||||
&mounter,
|
||||
&mountDetector,
|
||||
volume.VolumeOptions{RootContext: config.rootContext},
|
||||
fakeChconRnr)
|
||||
volume.VolumeOptions{RootContext: config.rootContext})
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
// 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
|
||||
if e, a := config.expectedSetupMounts, len(mounter.Log); 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
|
||||
}
|
||||
|
||||
func (b *fcDiskBuilder) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Unmounts the bind mount, and detaches the disk only if the disk
|
||||
// resource was the last reference to that disk on the kubelet.
|
||||
func (c *fcDiskCleaner) TearDown() error {
|
||||
|
||||
@@ -205,6 +205,10 @@ func (b flockerBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b flockerBuilder) SupportsSELinux() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (b flockerBuilder) updateDatasetPrimary(datasetID, primaryUUID string) error {
|
||||
|
||||
@@ -238,6 +238,10 @@ func (b *gcePersistentDiskBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b *gcePersistentDiskBuilder) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func makeGlobalPDName(host volume.VolumeHost, devName string) string {
|
||||
return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), "mounts", devName)
|
||||
}
|
||||
|
||||
@@ -122,6 +122,10 @@ func (b *gitRepoVolumeBuilder) IsReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *gitRepoVolumeBuilder) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// This is the spec for the volume that this plugin wraps.
|
||||
var wrappedVolumeSpec = &volume.Spec{
|
||||
Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
|
||||
|
||||
@@ -189,6 +189,10 @@ func (b *glusterfsBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b *glusterfsBuilder) SupportsSELinux() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (glusterfsVolume *glusterfs) GetPath() string {
|
||||
name := glusterfsPluginName
|
||||
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
|
||||
}
|
||||
|
||||
func (b *hostPathBuilder) SupportsSELinux() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *hostPathBuilder) GetPath() string {
|
||||
return b.path
|
||||
}
|
||||
|
||||
@@ -185,6 +185,10 @@ func (b *iscsiDiskBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b *iscsiDiskBuilder) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Unmounts the bind mount, and detaches the disk only if the disk
|
||||
// resource was the last reference to that disk on the kubelet.
|
||||
func (c *iscsiDiskCleaner) TearDown() error {
|
||||
|
||||
@@ -226,6 +226,10 @@ func (b *nfsBuilder) IsReadOnly() bool {
|
||||
return b.readOnly
|
||||
}
|
||||
|
||||
func (b *nfsBuilder) SupportsSELinux() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
//
|
||||
//func (c *nfsCleaner) GetPath() string {
|
||||
// name := nfsPluginName
|
||||
|
||||
@@ -219,6 +219,10 @@ func (b *rbd) IsReadOnly() bool {
|
||||
return b.ReadOnly
|
||||
}
|
||||
|
||||
func (b *rbd) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Unmounts the bind mount, and detaches the disk only if the disk
|
||||
// resource was the last reference to that disk on the kubelet.
|
||||
func (c *rbdCleaner) TearDown() error {
|
||||
|
||||
@@ -176,6 +176,10 @@ func (sv *secretVolume) IsReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (sv *secretVolume) SupportsSELinux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func totalSecretBytes(secret *api.Secret) int {
|
||||
totalSize := 0
|
||||
for _, bytes := range secret.Data {
|
||||
|
||||
@@ -172,6 +172,10 @@ func (fv *FakeVolume) IsReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (fv *FakeVolume) SupportsSELinux() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (fv *FakeVolume) GetPath() string {
|
||||
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)
|
||||
// 3. Logical OR the permission bits with rw-rw----
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user