From 17d850ee0e945b4b7974103889939c58e6194d96 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 28 Jul 2022 17:32:01 +0200 Subject: [PATCH] Add interface for SELinuxOptionsToFileLabel github.com/opencontainers/selinux/go-selinux needs OS that supports SELinux and SELinux enabled in it to return useful data, therefore add an interface in front of it, so we can mock its behavior in unit tests. --- .../cache/desired_state_of_world.go | 18 +++- .../cache/desired_state_of_world_test.go | 30 ++++-- .../volumemanager/metrics/metrics_test.go | 3 +- .../desired_state_of_world_populator_test.go | 3 +- .../reconciler/reconciler_test.go | 48 ++++++---- pkg/kubelet/volumemanager/volume_manager.go | 3 +- pkg/volume/util/selinux.go | 92 +++++++++++++++++-- 7 files changed, 155 insertions(+), 42 deletions(-) diff --git a/pkg/kubelet/volumemanager/cache/desired_state_of_world.go b/pkg/kubelet/volumemanager/cache/desired_state_of_world.go index d5e76ff3c54..37b0b96b44b 100644 --- a/pkg/kubelet/volumemanager/cache/desired_state_of_world.go +++ b/pkg/kubelet/volumemanager/cache/desired_state_of_world.go @@ -142,14 +142,15 @@ type VolumeToMount struct { } // NewDesiredStateOfWorld returns a new instance of DesiredStateOfWorld. -func NewDesiredStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) DesiredStateOfWorld { +func NewDesiredStateOfWorld(volumePluginMgr *volume.VolumePluginMgr, seLinuxTranslator util.SELinuxLabelTranslator) DesiredStateOfWorld { if feature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { registerSELinuxMetrics() } return &desiredStateOfWorld{ - volumesToMount: make(map[v1.UniqueVolumeName]volumeToMount), - volumePluginMgr: volumePluginMgr, - podErrors: make(map[types.UniquePodName]sets.String), + volumesToMount: make(map[v1.UniqueVolumeName]volumeToMount), + volumePluginMgr: volumePluginMgr, + podErrors: make(map[types.UniquePodName]sets.String), + seLinuxTranslator: seLinuxTranslator, } } @@ -164,6 +165,8 @@ type desiredStateOfWorld struct { volumePluginMgr *volume.VolumePluginMgr // podErrors are errors caught by desiredStateOfWorldPopulator about volumes for a given pod. podErrors map[types.UniquePodName]sets.String + // seLinuxTranslator translates v1.SELinuxOptions to a file SELinux label. + seLinuxTranslator util.SELinuxLabelTranslator sync.RWMutex } @@ -373,6 +376,11 @@ func (dsw *desiredStateOfWorld) getSELinuxLabel(volumeSpec *volume.Spec, seLinux if feature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { var err error + + if !dsw.seLinuxTranslator.SELinuxEnabled() { + return "", false, nil + } + pluginSupportsSELinuxContextMount, err = dsw.getSELinuxMountSupport(volumeSpec) if err != nil { return "", false, err @@ -382,7 +390,7 @@ func (dsw *desiredStateOfWorld) getSELinuxLabel(volumeSpec *volume.Spec, seLinux // Ensure that a volume that can be mounted with "-o context=XYZ" is // used only by containers with the same SELinux contexts. for _, containerContext := range seLinuxContainerContexts { - newLabel, err := util.SELinuxOptionsToFileLabel(containerContext) + newLabel, err := dsw.seLinuxTranslator.SELinuxOptionsToFileLabel(containerContext) if err != nil { fullErr := fmt.Errorf("failed to construct SELinux label from context %q: %s", containerContext, err) if err := handlerSELinuxMetricError(fullErr, isRWOP, seLinuxContainerContextWarnings, seLinuxContainerContextErrors); err != nil { diff --git a/pkg/kubelet/volumemanager/cache/desired_state_of_world_test.go b/pkg/kubelet/volumemanager/cache/desired_state_of_world_test.go index c79a8d071bd..32dd140fbf6 100644 --- a/pkg/kubelet/volumemanager/cache/desired_state_of_world_test.go +++ b/pkg/kubelet/volumemanager/cache/desired_state_of_world_test.go @@ -37,7 +37,8 @@ import ( func Test_AddPodToVolume_Positive_NewPodNewVolume(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod3", @@ -82,7 +83,8 @@ func Test_AddPodToVolume_Positive_NewPodNewVolume(t *testing.T) { func Test_AddPodToVolume_Positive_ExistingPodExistingVolume(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod3", @@ -156,7 +158,8 @@ func Test_AddPodToVolume_Positive_NamesForDifferentPodsAndDifferentVolumes(t *te } volumePluginMgr := volume.VolumePluginMgr{} volumePluginMgr.InitPlugins(plugins, nil /* prober */, fakeVolumeHost) - dsw := NewDesiredStateOfWorld(&volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(&volumePluginMgr, seLinuxTranslator) testcases := map[string]struct { pod1 *v1.Pod @@ -289,7 +292,8 @@ func Test_AddPodToVolume_Positive_NamesForDifferentPodsAndDifferentVolumes(t *te func Test_DeletePodFromVolume_Positive_PodExistsVolumeExists(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod3", @@ -341,7 +345,8 @@ func Test_DeletePodFromVolume_Positive_PodExistsVolumeExists(t *testing.T) { func Test_MarkVolumesReportedInUse_Positive_NewPodNewVolume(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) pod1 := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -466,7 +471,8 @@ func Test_MarkVolumesReportedInUse_Positive_NewPodNewVolume(t *testing.T) { func Test_AddPodToVolume_WithEmptyDirSizeLimit(t *testing.T) { volumePluginMgr, _ := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) quantity1Gi := resource.MustParse("1Gi") quantity2Gi := resource.MustParse("2Gi") quantity3Gi := resource.MustParse("3Gi") @@ -621,7 +627,8 @@ func Test_AddPodToVolume_Positive_SELinuxNoRWOP(t *testing.T) { nil, /* plugins */ ) volumePluginMgr.InitPlugins(plugins, nil /* prober */, fakeVolumeHost) - dsw := NewDesiredStateOfWorld(&volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(&volumePluginMgr, seLinuxTranslator) seLinux := v1.SELinuxOptions{ User: "system_u", Role: "object_r", @@ -701,7 +708,8 @@ func Test_AddPodToVolume_Positive_NoSELinuxPlugin(t *testing.T) { nil, /* plugins */ ) volumePluginMgr.InitPlugins(plugins, nil /* prober */, fakeVolumeHost) - dsw := NewDesiredStateOfWorld(&volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(&volumePluginMgr, seLinuxTranslator) seLinux := v1.SELinuxOptions{ User: "system_u", Role: "object_r", @@ -782,7 +790,8 @@ func Test_AddPodToVolume_Positive_ExistingPodSameSELinuxRWOP(t *testing.T) { nil, /* plugins */ ) volumePluginMgr.InitPlugins(plugins, nil /* prober */, fakeVolumeHost) - dsw := NewDesiredStateOfWorld(&volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(&volumePluginMgr, seLinuxTranslator) seLinux := v1.SELinuxOptions{ User: "system_u", Role: "object_r", @@ -882,7 +891,8 @@ func Test_AddPodToVolume_Negative_ExistingPodDifferentSELinuxRWOP(t *testing.T) nil, /* plugins */ ) volumePluginMgr.InitPlugins(plugins, nil /* prober */, fakeVolumeHost) - dsw := NewDesiredStateOfWorld(&volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := NewDesiredStateOfWorld(&volumePluginMgr, seLinuxTranslator) seLinux1 := v1.SELinuxOptions{ User: "system_u", Role: "object_r", diff --git a/pkg/kubelet/volumemanager/metrics/metrics_test.go b/pkg/kubelet/volumemanager/metrics/metrics_test.go index cd30a8023c1..151967bb242 100644 --- a/pkg/kubelet/volumemanager/metrics/metrics_test.go +++ b/pkg/kubelet/volumemanager/metrics/metrics_test.go @@ -32,7 +32,8 @@ import ( func TestMetricCollection(t *testing.T) { volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(k8stypes.NodeName("node-name"), volumePluginMgr) pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go index 24769116004..5b9dd66ac4b 100644 --- a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go +++ b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go @@ -1308,7 +1308,8 @@ func createDswpWithVolumeWithCustomPluginMgr(t *testing.T, pv *v1.PersistentVolu fakePodManager := kubepod.NewBasicPodManager( podtest.NewFakeMirrorClient(), fakeSecretManager, fakeConfigMapManager) - fakesDSW := cache.NewDesiredStateOfWorld(fakeVolumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + fakesDSW := cache.NewDesiredStateOfWorld(fakeVolumePluginMgr, seLinuxTranslator) fakeASW := cache.NewActualStateOfWorld("fake", fakeVolumePluginMgr) fakeRuntime := &containertest.FakeRuntime{} fakeStateProvider := &fakePodStateProvider{} diff --git a/pkg/kubelet/volumemanager/reconciler/reconciler_test.go b/pkg/kubelet/volumemanager/reconciler/reconciler_test.go index bd49d90b994..be7a26338f9 100644 --- a/pkg/kubelet/volumemanager/reconciler/reconciler_test.go +++ b/pkg/kubelet/volumemanager/reconciler/reconciler_test.go @@ -70,7 +70,8 @@ func hasAddedPods() bool { return true } func Test_Run_Positive_DoNothing(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -114,7 +115,8 @@ func Test_Run_Positive_DoNothing(t *testing.T) { func Test_Run_Positive_VolumeAttachAndMount(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -204,7 +206,8 @@ func Test_Run_Positive_VolumeAttachAndMountMigrationEnabled(t *testing.T) { }, } volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient(v1.AttachedVolume{ @@ -309,7 +312,8 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) { }, } volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -388,7 +392,8 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) { func Test_Run_Negative_VolumeMountControllerAttachEnabled(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -466,7 +471,8 @@ func Test_Run_Negative_VolumeMountControllerAttachEnabled(t *testing.T) { func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -568,7 +574,8 @@ func Test_Run_Positive_VolumeUnmountControllerAttachEnabled(t *testing.T) { }, } volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -691,7 +698,8 @@ func Test_Run_Positive_VolumeAttachAndMap(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(gcepv, gcepvc) fakeRecorder := &record.FakeRecorder{} @@ -803,7 +811,8 @@ func Test_Run_Positive_BlockVolumeMapControllerAttachEnabled(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(gcepv, gcepvc, v1.AttachedVolume{ Name: "fake-plugin/fake-device1", @@ -903,7 +912,8 @@ func Test_Run_Positive_BlockVolumeAttachMapUnmapDetach(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(gcepv, gcepvc) fakeRecorder := &record.FakeRecorder{} @@ -1024,7 +1034,8 @@ func Test_Run_Positive_VolumeUnmapControllerAttachEnabled(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(gcepv, gcepvc, v1.AttachedVolume{ Name: "fake-plugin/fake-device1", @@ -1293,7 +1304,8 @@ func Test_Run_Positive_VolumeFSResizeControllerAttachEnabled(t *testing.T) { }, } volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(pv, pvc, v1.AttachedVolume{ Name: v1.UniqueVolumeName(fmt.Sprintf("fake-plugin/%s", tc.pvName)), @@ -1547,8 +1559,9 @@ func Test_UncertainDeviceGlobalMounts(t *testing.T) { } volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) fakePlugin.SupportsRemount = tc.supportRemount + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(pv, pvc, v1.AttachedVolume{ Name: v1.UniqueVolumeName(fmt.Sprintf("fake-plugin/%s", tc.volumeName)), @@ -1770,7 +1783,8 @@ func Test_UncertainVolumeMountState(t *testing.T) { volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) fakePlugin.SupportsRemount = tc.supportRemount - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createtestClientWithPVPVC(pv, pvc, v1.AttachedVolume{ Name: v1.UniqueVolumeName(fmt.Sprintf("fake-plugin/%s", tc.volumeName)), @@ -2087,8 +2101,9 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabledRace(t *testing.T) { }, } volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} @@ -2234,8 +2249,9 @@ func getReconciler(kubeletDir string, t *testing.T, volumePaths []string) (Recon node := getFakeNode() volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNodeAndRoot(t, node, kubeletDir) tmpKubeletPodDir := filepath.Join(kubeletDir, "pods") + seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() - dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) + dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} diff --git a/pkg/kubelet/volumemanager/volume_manager.go b/pkg/kubelet/volumemanager/volume_manager.go index 0357ea0f5d1..ee58b2fcaa7 100644 --- a/pkg/kubelet/volumemanager/volume_manager.go +++ b/pkg/kubelet/volumemanager/volume_manager.go @@ -186,10 +186,11 @@ func NewVolumeManager( keepTerminatedPodVolumes bool, blockVolumePathHandler volumepathhandler.BlockVolumePathHandler) VolumeManager { + seLinuxTranslator := util.NewSELinuxLabelTranslator() vm := &volumeManager{ kubeClient: kubeClient, volumePluginMgr: volumePluginMgr, - desiredStateOfWorld: cache.NewDesiredStateOfWorld(volumePluginMgr), + desiredStateOfWorld: cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator), actualStateOfWorld: cache.NewActualStateOfWorld(nodeName, volumePluginMgr), operationExecutor: operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( kubeClient, diff --git a/pkg/volume/util/selinux.go b/pkg/volume/util/selinux.go index f05c30121f4..cd537610607 100644 --- a/pkg/volume/util/selinux.go +++ b/pkg/volume/util/selinux.go @@ -28,8 +28,36 @@ import ( "k8s.io/kubernetes/pkg/volume" ) -// SELinuxOptionsToFileLabel returns SELinux file label for given options. -func SELinuxOptionsToFileLabel(opts *v1.SELinuxOptions) (string, error) { +// SELinuxLabelTranslator translates v1.SELinuxOptions of a process to SELinux file label. +type SELinuxLabelTranslator interface { + // SELinuxOptionsToFileLabel returns SELinux file label for given SELinuxOptions + // of a container process. + // When Role, User or Type are empty, they're read from the system defaults. + // It returns "" and no error on platforms that do not have SELinux enabled + // or don't support SELinux at all. + SELinuxOptionsToFileLabel(opts *v1.SELinuxOptions) (string, error) + + // SELinuxEnabled returns true when the OS has enabled SELinux support. + SELinuxEnabled() bool +} + +// Real implementation of the interface. +// On Linux with SELinux enabled it translates. Otherwise it always returns an empty string and no error. +type translator struct{} + +var _ SELinuxLabelTranslator = &translator{} + +// NewSELinuxLabelTranslator returns new SELinuxLabelTranslator for the platform. +func NewSELinuxLabelTranslator() SELinuxLabelTranslator { + return &translator{} +} + +// SELinuxOptionsToFileLabel returns SELinux file label for given SELinuxOptions +// of a container process. +// When Role, User or Type are empty, they're read from the system defaults. +// It returns "" and no error on platforms that do not have SELinux enabled +// or don't support SELinux at all. +func (l *translator) SELinuxOptionsToFileLabel(opts *v1.SELinuxOptions) (string, error) { if opts == nil { return "", nil } @@ -39,7 +67,6 @@ func SELinuxOptionsToFileLabel(opts *v1.SELinuxOptions) (string, error) { return "", nil } - // TODO: use interface for InitLabels for unit tests. processLabel, fileLabel, err := label.InitLabels(args) if err != nil { // In theory, this should be unreachable. InitLabels can fail only when args contain an unknown option, @@ -75,13 +102,62 @@ func contextOptions(opts *v1.SELinuxOptions) []string { return args } -// SupportsSELinuxContextMount checks if the given volumeSpec supports with mount -o context -func SupportsSELinuxContextMount(volumeSpec *volume.Spec, volumePluginMgr *volume.VolumePluginMgr) (bool, error) { - // This is cheap - if !selinux.GetEnabled() { - return false, nil +func (l *translator) SELinuxEnabled() bool { + return selinux.GetEnabled() +} + +// Fake implementation of the interface for unit tests. +type fakeTranslator struct{} + +var _ SELinuxLabelTranslator = &fakeTranslator{} + +// NewFakeSELinuxLabelTranslator returns a fake translator for unit tests. +// It imitates a real translator on platforms that do not have SELinux enabled +// or don't support SELinux at all. +func NewFakeSELinuxLabelTranslator() SELinuxLabelTranslator { + return &fakeTranslator{} +} + +// SELinuxOptionsToFileLabel returns SELinux file label for given options. +func (l *fakeTranslator) SELinuxOptionsToFileLabel(opts *v1.SELinuxOptions) (string, error) { + if opts == nil { + return "", nil + } + // Fill empty values from "system defaults" (taken from Fedora Linux). + user := opts.User + if user == "" { + user = "system_u" } + role := opts.Role + if role == "" { + role = "object_r" + } + + // opts is context of the *process* to run in a container. Translate + // process type "container_t" to file label type "container_file_t". + // (The rest of the context is the same for processes and files). + fileType := opts.Type + if fileType == "" || fileType == "container_t" { + fileType = "container_file_t" + } + + level := opts.Level + if level == "" { + // If empty, level is allocated randomly. + level = "s0:c998,c999" + } + + ctx := fmt.Sprintf("%s:%s:%s:%s", user, role, fileType, level) + return ctx, nil +} + +func (l *fakeTranslator) SELinuxEnabled() bool { + return true +} + +// SupportsSELinuxContextMount checks if the given volumeSpec supports with mount -o context +func SupportsSELinuxContextMount(volumeSpec *volume.Spec, volumePluginMgr *volume.VolumePluginMgr) (bool, error) { plugin, _ := volumePluginMgr.FindPluginBySpec(volumeSpec) if plugin != nil { return plugin.SupportsSELinuxContextMount(volumeSpec)