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)