mirror of
https://github.com/optim-enterprises-bv/kubernetes.git
synced 2025-10-29 17:32:47 +00:00
SELinuxMount stays off by default, because it changes the default kubelet behavior. SELinuxChangePolicy is on by default and notifies users on Pods that could get broken by SELinuxMount feature gate.
339 lines
13 KiB
Go
339 lines
13 KiB
Go
/*
|
|
Copyright 2024 The Kubernetes Authors.
|
|
|
|
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 util
|
|
|
|
import (
|
|
"testing"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
"k8s.io/component-base/featuregate"
|
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
"k8s.io/kubernetes/pkg/features"
|
|
"k8s.io/kubernetes/pkg/volume"
|
|
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
|
"k8s.io/utils/ptr"
|
|
)
|
|
|
|
func TestGetMountSELinuxLabel(t *testing.T) {
|
|
pvRWOP := &v1.PersistentVolume{
|
|
Spec: v1.PersistentVolumeSpec{
|
|
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod},
|
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
|
HostPath: &v1.HostPathVolumeSource{},
|
|
},
|
|
},
|
|
}
|
|
pvRWX := &v1.PersistentVolume{
|
|
Spec: v1.PersistentVolumeSpec{
|
|
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany},
|
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
|
HostPath: &v1.HostPathVolumeSource{},
|
|
},
|
|
},
|
|
}
|
|
|
|
seLinuxOpts1 := v1.SELinuxOptions{
|
|
Level: "s0:c123,c456",
|
|
}
|
|
seLinuxOpts2 := v1.SELinuxOptions{
|
|
Level: "s0:c234,c567",
|
|
}
|
|
seLinuxOpts3 := v1.SELinuxOptions{
|
|
Level: "s0:c345,c678",
|
|
}
|
|
label1 := "system_u:object_r:container_file_t:s0:c123,c456"
|
|
|
|
tests := []struct {
|
|
name string
|
|
featureGates []featuregate.Feature // SELinuxMountReadWriteOncePod is always enabled
|
|
pluginSupportsSELinux bool
|
|
volume *volume.Spec
|
|
podSecurityContext *v1.PodSecurityContext
|
|
seLinuxOptions []*v1.SELinuxOptions
|
|
expectError bool
|
|
expectedInfo SELinuxLabelInfo
|
|
}{
|
|
// Tests with no labels
|
|
{
|
|
name: "no label, no changePolicy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: nil,
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "", // no SELinuxOptions + the default policy is recursive
|
|
SELinuxProcessLabel: "", // no SELinuxOptions
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "no label, Recursive change policy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyRecursive)},
|
|
seLinuxOptions: nil,
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "", // no SELinuxOptions + recursive policy
|
|
SELinuxProcessLabel: "", // SELinuxOptions
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
// Tests with one label and RWOP volume
|
|
{
|
|
name: "one label, Recursive change policy, no feature gate",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyRecursive)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // Recursive policy is not observed when SELinuxChangePolicy is off
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, Recursive change policy, SELinuxChangePolicy",
|
|
featureGates: []featuregate.Feature{features.SELinuxChangePolicy},
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyRecursive)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "", // Recursive policy is effective with SELinuxChangePolicy, affects RWOP too.
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, no policy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // The default policy is MountOption
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, MountOption policy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyMountOption)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // SELinuxChangePolicy feature is disabled, but the default policy is MountOption anyway
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
// Tests with RWX volume
|
|
{
|
|
name: "one label, no policy, RWX",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWX},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // GetMountSELinuxLabel() does not check the access mode, it's up to the caller
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, no policy, RWX, SELinuxChangePolicy",
|
|
featureGates: []featuregate.Feature{features.SELinuxChangePolicy},
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWX},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // GetMountSELinuxLabel() does not check the access mode, it's up to the caller
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, MountOption policy, RWX, SELinuxChangePolicy",
|
|
featureGates: []featuregate.Feature{features.SELinuxChangePolicy},
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWX},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyMountOption)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // GetMountSELinuxLabel() does not check the access mode, it's up to the caller
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, no policy, RWX, SELinuxMount",
|
|
featureGates: []featuregate.Feature{features.SELinuxChangePolicy, features.SELinuxMount},
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWX},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // SELinuxMount FG + MountOption policy
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
// No plugin support
|
|
{
|
|
name: "one label, Recursive change policy, SELinuxChangePolicy, no plugin support",
|
|
featureGates: []featuregate.Feature{features.SELinuxChangePolicy},
|
|
pluginSupportsSELinux: false,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyRecursive)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "", // No plugin support
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: false,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, no policy, no plugin support",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: false,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "", // No plugin support
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: false,
|
|
},
|
|
},
|
|
{
|
|
name: "one label, MountOption policy, no plugin support",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: false,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyMountOption)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "", // No plugin support
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: false,
|
|
},
|
|
},
|
|
// Corner cases
|
|
{
|
|
name: "multiple same labels, no policy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1, &seLinuxOpts1, &seLinuxOpts1, &seLinuxOpts1},
|
|
expectError: false,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: label1, // The default policy is MountOption
|
|
SELinuxProcessLabel: label1, // Pod has a label assigned
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
// Error cases
|
|
{
|
|
name: "multiple different labels, no policy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: nil,
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1, &seLinuxOpts2, &seLinuxOpts3},
|
|
expectError: true,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "",
|
|
SELinuxProcessLabel: "",
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
{
|
|
name: "multiple different labels, Recursive policy",
|
|
featureGates: nil,
|
|
pluginSupportsSELinux: true,
|
|
volume: &volume.Spec{PersistentVolume: pvRWOP},
|
|
podSecurityContext: &v1.PodSecurityContext{SELinuxChangePolicy: ptr.To(v1.SELinuxChangePolicyRecursive)},
|
|
seLinuxOptions: []*v1.SELinuxOptions{&seLinuxOpts1, &seLinuxOpts2, &seLinuxOpts3},
|
|
expectError: true,
|
|
expectedInfo: SELinuxLabelInfo{
|
|
SELinuxMountLabel: "",
|
|
SELinuxProcessLabel: "",
|
|
PluginSupportsSELinuxContextMount: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Arrange
|
|
// Set feature gates for the test. *Disable* those that are not in tt.featureGates.
|
|
allGates := []featuregate.Feature{features.SELinuxChangePolicy, features.SELinuxMount}
|
|
enabledGates := sets.New(tt.featureGates...)
|
|
for _, fg := range allGates {
|
|
enable := enabledGates.Has(fg)
|
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, fg, enable)
|
|
}
|
|
seLinuxTranslator := NewFakeSELinuxLabelTranslator()
|
|
pluginMgr, plugin := volumetesting.GetTestKubeletVolumePluginMgr(t)
|
|
plugin.SupportsSELinux = tt.pluginSupportsSELinux
|
|
|
|
// Act
|
|
info, err := GetMountSELinuxLabel(tt.volume, tt.seLinuxOptions, tt.podSecurityContext, pluginMgr, seLinuxTranslator)
|
|
|
|
// Assert
|
|
if err != nil {
|
|
if !tt.expectError {
|
|
t.Errorf("GetMountSELinuxLabel() unexpected error: %v", err)
|
|
}
|
|
return
|
|
}
|
|
if tt.expectError {
|
|
t.Errorf("GetMountSELinuxLabel() expected error, got none")
|
|
return
|
|
}
|
|
|
|
if info != tt.expectedInfo {
|
|
t.Errorf("GetMountSELinuxLabel() expected %+v, got %+v", tt.expectedInfo, info)
|
|
}
|
|
})
|
|
}
|
|
}
|