mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Reconstruct SELinux mount option
When reconstructing volumes from disk after kubelet restart, reconstruct also context=XYZ mount option and add it to the ActualStateOfWorld.
This commit is contained in:
parent
95bd687a28
commit
e575e60ea4
@ -160,7 +160,6 @@ func (rc *reconciler) updateStates(volumesNeedUpdate map[v1.UniqueVolumeName]*gl
|
|||||||
klog.ErrorS(err, "Could not find device mount path for volume", "volumeName", gvl.volumeName)
|
klog.ErrorS(err, "Could not find device mount path for volume", "volumeName", gvl.volumeName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO(jsafrane): add reconstructed SELinux context
|
|
||||||
err = rc.actualStateOfWorld.MarkDeviceAsMounted(gvl.volumeName, gvl.devicePath, deviceMountPath, "")
|
err = rc.actualStateOfWorld.MarkDeviceAsMounted(gvl.volumeName, gvl.devicePath, deviceMountPath, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.ErrorS(err, "Could not mark device is mounted to actual state of world", "volume", gvl.volumeName)
|
klog.ErrorS(err, "Could not mark device is mounted to actual state of world", "volume", gvl.volumeName)
|
||||||
|
@ -56,6 +56,7 @@ type reconstructedVolume struct {
|
|||||||
mounter volumepkg.Mounter
|
mounter volumepkg.Mounter
|
||||||
deviceMounter volumepkg.DeviceMounter
|
deviceMounter volumepkg.DeviceMounter
|
||||||
blockVolumeMapper volumepkg.BlockVolumeMapper
|
blockVolumeMapper volumepkg.BlockVolumeMapper
|
||||||
|
seLinuxMountContext string
|
||||||
}
|
}
|
||||||
|
|
||||||
// globalVolumeInfo stores reconstructed volume information
|
// globalVolumeInfo stores reconstructed volume information
|
||||||
@ -211,6 +212,9 @@ func (rc *reconciler) reconstructVolume(volume podVolume) (*reconstructedVolume,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
volumeSpec := reconstructed.Spec
|
volumeSpec := reconstructed.Spec
|
||||||
|
if volumeSpec == nil {
|
||||||
|
return nil, fmt.Errorf("failed to reconstruct volume for plugin %q (spec.Name: %q) pod %q (UID: %q): got nil", volume.pluginName, volume.volumeSpecName, volume.podName, pod.UID)
|
||||||
|
}
|
||||||
|
|
||||||
// We have to find the plugins by volume spec (NOT by plugin name) here
|
// We have to find the plugins by volume spec (NOT by plugin name) here
|
||||||
// in order to correctly reconstruct ephemeral volume types.
|
// in order to correctly reconstruct ephemeral volume types.
|
||||||
@ -312,9 +316,10 @@ func (rc *reconciler) reconstructVolume(volume podVolume) (*reconstructedVolume,
|
|||||||
volumeGidValue: "",
|
volumeGidValue: "",
|
||||||
// devicePath is updated during updateStates() by checking node status's VolumesAttached data.
|
// devicePath is updated during updateStates() by checking node status's VolumesAttached data.
|
||||||
// TODO: get device path directly from the volume mount path.
|
// TODO: get device path directly from the volume mount path.
|
||||||
devicePath: "",
|
devicePath: "",
|
||||||
mounter: volumeMounter,
|
mounter: volumeMounter,
|
||||||
blockVolumeMapper: volumeMapper,
|
blockVolumeMapper: volumeMapper,
|
||||||
|
seLinuxMountContext: reconstructed.SELinuxMountContext,
|
||||||
}
|
}
|
||||||
return reconstructedVolume, nil
|
return reconstructedVolume, nil
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ func (rc *reconciler) updateStatesNew(reconstructedVolumes map[v1.UniqueVolumeNa
|
|||||||
klog.ErrorS(err, "Could not add volume information to actual state of world", "volumeName", gvl.volumeName)
|
klog.ErrorS(err, "Could not add volume information to actual state of world", "volumeName", gvl.volumeName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
var seLinuxMountContext string
|
||||||
for _, volume := range gvl.podVolumes {
|
for _, volume := range gvl.podVolumes {
|
||||||
markVolumeOpts := operationexecutor.MarkVolumeOpts{
|
markVolumeOpts := operationexecutor.MarkVolumeOpts{
|
||||||
PodName: volume.podName,
|
PodName: volume.podName,
|
||||||
@ -123,6 +124,7 @@ func (rc *reconciler) updateStatesNew(reconstructedVolumes map[v1.UniqueVolumeNa
|
|||||||
VolumeGidVolume: volume.volumeGidValue,
|
VolumeGidVolume: volume.volumeGidValue,
|
||||||
VolumeSpec: volume.volumeSpec,
|
VolumeSpec: volume.volumeSpec,
|
||||||
VolumeMountState: operationexecutor.VolumeMountUncertain,
|
VolumeMountState: operationexecutor.VolumeMountUncertain,
|
||||||
|
SELinuxMountContext: volume.seLinuxMountContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = rc.actualStateOfWorld.CheckAndMarkVolumeAsUncertainViaReconstruction(markVolumeOpts)
|
_, err = rc.actualStateOfWorld.CheckAndMarkVolumeAsUncertainViaReconstruction(markVolumeOpts)
|
||||||
@ -130,7 +132,8 @@ func (rc *reconciler) updateStatesNew(reconstructedVolumes map[v1.UniqueVolumeNa
|
|||||||
klog.ErrorS(err, "Could not add pod to volume information to actual state of world", "pod", klog.KObj(volume.pod))
|
klog.ErrorS(err, "Could not add pod to volume information to actual state of world", "pod", klog.KObj(volume.pod))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
klog.V(2).InfoS("Volume is marked as uncertain and added into the actual state", "pod", klog.KObj(volume.pod), "podName", volume.podName, "volumeName", volume.volumeName)
|
seLinuxMountContext = volume.seLinuxMountContext
|
||||||
|
klog.V(2).InfoS("Volume is marked as uncertain and added into the actual state", "pod", klog.KObj(volume.pod), "podName", volume.podName, "volumeName", volume.volumeName, "seLinuxMountContext", volume.seLinuxMountContext)
|
||||||
}
|
}
|
||||||
// If the volume has device to mount, we mark its device as uncertain.
|
// If the volume has device to mount, we mark its device as uncertain.
|
||||||
if gvl.deviceMounter != nil || gvl.blockVolumeMapper != nil {
|
if gvl.deviceMounter != nil || gvl.blockVolumeMapper != nil {
|
||||||
@ -139,7 +142,7 @@ func (rc *reconciler) updateStatesNew(reconstructedVolumes map[v1.UniqueVolumeNa
|
|||||||
klog.ErrorS(err, "Could not find device mount path for volume", "volumeName", gvl.volumeName)
|
klog.ErrorS(err, "Could not find device mount path for volume", "volumeName", gvl.volumeName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = rc.actualStateOfWorld.MarkDeviceAsUncertain(gvl.volumeName, gvl.devicePath, deviceMountPath, "")
|
err = rc.actualStateOfWorld.MarkDeviceAsUncertain(gvl.volumeName, gvl.devicePath, deviceMountPath, seLinuxMountContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.ErrorS(err, "Could not mark device is uncertain to actual state of world", "volumeName", gvl.volumeName, "deviceMountPath", deviceMountPath)
|
klog.ErrorS(err, "Could not mark device is uncertain to actual state of world", "volumeName", gvl.volumeName, "deviceMountPath", deviceMountPath)
|
||||||
continue
|
continue
|
||||||
|
@ -60,7 +60,7 @@ var (
|
|||||||
testAccount = "test-service-account"
|
testAccount = "test-service-account"
|
||||||
)
|
)
|
||||||
|
|
||||||
func prepareVolumeInfoFile(mountPath string, plug *csiPlugin, specVolumeName, volumeID, driverName, lifecycleMode string) error {
|
func prepareVolumeInfoFile(mountPath string, plug *csiPlugin, specVolumeName, volumeID, driverName, lifecycleMode, seLinuxMountContext string) error {
|
||||||
nodeName := string(plug.host.GetNodeName())
|
nodeName := string(plug.host.GetNodeName())
|
||||||
volData := map[string]string{
|
volData := map[string]string{
|
||||||
volDataKey.specVolID: specVolumeName,
|
volDataKey.specVolID: specVolumeName,
|
||||||
@ -69,6 +69,7 @@ func prepareVolumeInfoFile(mountPath string, plug *csiPlugin, specVolumeName, vo
|
|||||||
volDataKey.nodeName: nodeName,
|
volDataKey.nodeName: nodeName,
|
||||||
volDataKey.attachmentID: getAttachmentName(volumeID, driverName, nodeName),
|
volDataKey.attachmentID: getAttachmentName(volumeID, driverName, nodeName),
|
||||||
volDataKey.volumeLifecycleMode: lifecycleMode,
|
volDataKey.volumeLifecycleMode: lifecycleMode,
|
||||||
|
volDataKey.seLinuxMountContext: seLinuxMountContext,
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
||||||
return fmt.Errorf("failed to create dir for volume info file: %s", err)
|
return fmt.Errorf("failed to create dir for volume info file: %s", err)
|
||||||
|
@ -455,22 +455,23 @@ func (p *csiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return volume.ReconstructedVolume{}, errors.New(log("plugin.ConstructVolumeSpec failed loading volume data using [%s]: %v", mountPath, err))
|
return volume.ReconstructedVolume{}, errors.New(log("plugin.ConstructVolumeSpec failed loading volume data using [%s]: %v", mountPath, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(4).Info(log("plugin.ConstructVolumeSpec extracted [%#v]", volData))
|
klog.V(4).Info(log("plugin.ConstructVolumeSpec extracted [%#v]", volData))
|
||||||
|
|
||||||
var spec *volume.Spec
|
var ret volume.ReconstructedVolume
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
|
||||||
|
ret.SELinuxMountContext = volData[volDataKey.seLinuxMountContext]
|
||||||
|
}
|
||||||
|
|
||||||
// If mode is VolumeLifecycleEphemeral, use constructVolSourceSpec
|
// If mode is VolumeLifecycleEphemeral, use constructVolSourceSpec
|
||||||
// to construct volume source spec. If mode is VolumeLifecyclePersistent,
|
// to construct volume source spec. If mode is VolumeLifecyclePersistent,
|
||||||
// use constructPVSourceSpec to construct volume construct pv source spec.
|
// use constructPVSourceSpec to construct volume construct pv source spec.
|
||||||
if storage.VolumeLifecycleMode(volData[volDataKey.volumeLifecycleMode]) == storage.VolumeLifecycleEphemeral {
|
if storage.VolumeLifecycleMode(volData[volDataKey.volumeLifecycleMode]) == storage.VolumeLifecycleEphemeral {
|
||||||
spec = p.constructVolSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName])
|
ret.Spec = p.constructVolSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName])
|
||||||
return volume.ReconstructedVolume{Spec: spec}, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle])
|
ret.Spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle])
|
||||||
return volume.ReconstructedVolume{
|
return ret, nil
|
||||||
Spec: spec,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructVolSourceSpec constructs volume.Spec with CSIVolumeSource
|
// constructVolSourceSpec constructs volume.Spec with CSIVolumeSource
|
||||||
|
@ -29,9 +29,12 @@ import (
|
|||||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
fakeclient "k8s.io/client-go/kubernetes/fake"
|
fakeclient "k8s.io/client-go/kubernetes/fake"
|
||||||
utiltesting "k8s.io/client-go/util/testing"
|
utiltesting "k8s.io/client-go/util/testing"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||||
)
|
)
|
||||||
@ -316,16 +319,20 @@ func TestPluginConstructVolumeSpec(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
originSpec *volume.Spec
|
seLinuxMountEnabled bool
|
||||||
specVolID string
|
originSpec *volume.Spec
|
||||||
volHandle string
|
originSELinuxMountContext string
|
||||||
podUID types.UID
|
specVolID string
|
||||||
|
volHandle string
|
||||||
|
expectedSELinuxContext string
|
||||||
|
podUID types.UID
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "construct spec1 from original persistent spec",
|
name: "construct spec1 from original persistent spec",
|
||||||
specVolID: "test.vol.id",
|
specVolID: "test.vol.id",
|
||||||
volHandle: "testvol-handle1",
|
volHandle: "testvol-handle1",
|
||||||
|
|
||||||
originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("test.vol.id", 20, testDriver, "testvol-handle1"), true),
|
originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("test.vol.id", 20, testDriver, "testvol-handle1"), true),
|
||||||
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
|
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
|
||||||
},
|
},
|
||||||
@ -336,12 +343,35 @@ func TestPluginConstructVolumeSpec(t *testing.T) {
|
|||||||
originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true),
|
originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true),
|
||||||
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
|
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "construct SELinux context from original persistent spec when the feature is enabled",
|
||||||
|
seLinuxMountEnabled: true,
|
||||||
|
specVolID: "spec3",
|
||||||
|
volHandle: "handle3",
|
||||||
|
originSELinuxMountContext: "system_u:object_r:container_file_t:s0:c314,c894",
|
||||||
|
originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec3", 20, testDriver, "handle3"), true),
|
||||||
|
expectedSELinuxContext: "system_u:object_r:container_file_t:s0:c314,c894",
|
||||||
|
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "construct empty SELinux from original persistent spec when the feature is disabled",
|
||||||
|
seLinuxMountEnabled: false,
|
||||||
|
specVolID: "spec4",
|
||||||
|
volHandle: "handle4",
|
||||||
|
originSELinuxMountContext: "system_u:object_r:container_file_t:s0:c314,c894",
|
||||||
|
originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec4", 20, testDriver, "handle4"), true),
|
||||||
|
expectedSELinuxContext: "", // The context is cleared when the feature gate is off
|
||||||
|
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
|
registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ReadWriteOncePod, tc.seLinuxMountEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, tc.seLinuxMountEnabled)()
|
||||||
|
|
||||||
mounter, err := plug.NewMounter(
|
mounter, err := plug.NewMounter(
|
||||||
tc.originSpec,
|
tc.originSpec,
|
||||||
&api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}},
|
&api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}},
|
||||||
@ -356,7 +386,7 @@ func TestPluginConstructVolumeSpec(t *testing.T) {
|
|||||||
csiMounter := mounter.(*csiMountMgr)
|
csiMounter := mounter.(*csiMountMgr)
|
||||||
|
|
||||||
mountPath := filepath.Dir(csiMounter.GetPath())
|
mountPath := filepath.Dir(csiMounter.GetPath())
|
||||||
err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode))
|
err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode), tc.originSELinuxMountContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to save fake volume info file: %s", err)
|
t.Fatalf("failed to save fake volume info file: %s", err)
|
||||||
}
|
}
|
||||||
@ -395,6 +425,10 @@ func TestPluginConstructVolumeSpec(t *testing.T) {
|
|||||||
if rec.Spec.Name() != tc.specVolID {
|
if rec.Spec.Name() != tc.specVolID {
|
||||||
t.Errorf("Unexpected spec name constructed %s", rec.Spec.Name())
|
t.Errorf("Unexpected spec name constructed %s", rec.Spec.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rec.SELinuxMountContext != tc.expectedSELinuxContext {
|
||||||
|
t.Errorf("Expected SELinux context %q, got %q", tc.expectedSELinuxContext, rec.SELinuxMountContext)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -490,7 +524,7 @@ func TestPluginConstructVolumeSpecWithInline(t *testing.T) {
|
|||||||
csiMounter := mounter.(*csiMountMgr)
|
csiMounter := mounter.(*csiMountMgr)
|
||||||
|
|
||||||
mountPath := filepath.Dir(csiMounter.GetPath())
|
mountPath := filepath.Dir(csiMounter.GetPath())
|
||||||
err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode))
|
err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to save fake volume info file: %s", err)
|
t.Fatalf("failed to save fake volume info file: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -283,10 +283,25 @@ func (plugin *fcPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volum
|
|||||||
FC: &v1.FCVolumeSource{WWIDs: wwids, Lun: &lun, TargetWWNs: wwns},
|
FC: &v1.FCVolumeSource{WWIDs: wwids, Lun: &lun, TargetWWNs: wwns},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mountContext string
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
|
||||||
|
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
|
||||||
|
if !ok {
|
||||||
|
return volume.ReconstructedVolume{}, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
|
||||||
|
}
|
||||||
|
hu := kvh.GetHostUtil()
|
||||||
|
mountContext, err = hu.GetSELinuxMountContext(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
return volume.ReconstructedVolume{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
klog.V(5).Infof("ConstructVolumeSpec: TargetWWNs: %v, Lun: %v, WWIDs: %v",
|
klog.V(5).Infof("ConstructVolumeSpec: TargetWWNs: %v, Lun: %v, WWIDs: %v",
|
||||||
fcVolume.VolumeSource.FC.TargetWWNs, *fcVolume.VolumeSource.FC.Lun, fcVolume.VolumeSource.FC.WWIDs)
|
fcVolume.VolumeSource.FC.TargetWWNs, *fcVolume.VolumeSource.FC.Lun, fcVolume.VolumeSource.FC.WWIDs)
|
||||||
return volume.ReconstructedVolume{
|
return volume.ReconstructedVolume{
|
||||||
Spec: volume.NewSpecFromVolume(fcVolume),
|
Spec: volume.NewSpecFromVolume(fcVolume),
|
||||||
|
SELinuxMountContext: mountContext,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,8 +279,23 @@ func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (vo
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mountContext string
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
|
||||||
|
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
|
||||||
|
if !ok {
|
||||||
|
return volume.ReconstructedVolume{}, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
|
||||||
|
}
|
||||||
|
hu := kvh.GetHostUtil()
|
||||||
|
mountContext, err = hu.GetSELinuxMountContext(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
return volume.ReconstructedVolume{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return volume.ReconstructedVolume{
|
return volume.ReconstructedVolume{
|
||||||
Spec: volume.NewSpecFromVolume(iscsiVolume),
|
Spec: volume.NewSpecFromVolume(iscsiVolume),
|
||||||
|
SELinuxMountContext: mountContext,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,7 +573,11 @@ type VolumeConfig struct {
|
|||||||
// ReconstructedVolume contains information about a volume reconstructed by
|
// ReconstructedVolume contains information about a volume reconstructed by
|
||||||
// ConstructVolumeSpec().
|
// ConstructVolumeSpec().
|
||||||
type ReconstructedVolume struct {
|
type ReconstructedVolume struct {
|
||||||
|
// Spec is the volume spec of a mounted volume
|
||||||
Spec *Spec
|
Spec *Spec
|
||||||
|
// SELinuxMountContext is value of -o context=XYZ mount option.
|
||||||
|
// If empty, no such mount option is used.
|
||||||
|
SELinuxMountContext string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSpecFromVolume creates an Spec from an v1.Volume
|
// NewSpecFromVolume creates an Spec from an v1.Volume
|
||||||
|
@ -430,8 +430,18 @@ func (plugin *rbdPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volu
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mountContext string
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
|
||||||
|
mountContext, err = hu.GetSELinuxMountContext(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
return volume.ReconstructedVolume{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return volume.ReconstructedVolume{
|
return volume.ReconstructedVolume{
|
||||||
Spec: volume.NewSpecFromVolume(rbdVolume),
|
Spec: volume.NewSpecFromVolume(rbdVolume),
|
||||||
|
SELinuxMountContext: mountContext,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,3 +116,9 @@ func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) {
|
|||||||
func (hu *FakeHostUtil) GetMode(pathname string) (os.FileMode, error) {
|
func (hu *FakeHostUtil) GetMode(pathname string) (os.FileMode, error) {
|
||||||
return 0, errors.New("not implemented")
|
return 0, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSELinuxMountContext returns value of -o context=XYZ mount option on
|
||||||
|
// given mount point.
|
||||||
|
func (hu *FakeHostUtil) GetSELinuxMountContext(pathname string) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
@ -68,6 +68,9 @@ type HostUtils interface {
|
|||||||
GetSELinuxSupport(pathname string) (bool, error)
|
GetSELinuxSupport(pathname string) (bool, error)
|
||||||
// GetMode returns permissions of the path.
|
// GetMode returns permissions of the path.
|
||||||
GetMode(pathname string) (os.FileMode, error)
|
GetMode(pathname string) (os.FileMode, error)
|
||||||
|
// GetSELinuxMountContext returns value of -o context=XYZ mount option on
|
||||||
|
// given mount point.
|
||||||
|
GetSELinuxMountContext(pathname string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile-time check to ensure all HostUtil implementations satisfy
|
// Compile-time check to ensure all HostUtil implementations satisfy
|
||||||
|
@ -299,3 +299,35 @@ func GetModeLinux(pathname string) (os.FileMode, error) {
|
|||||||
}
|
}
|
||||||
return info.Mode(), nil
|
return info.Mode(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSELinuxMountContext returns value of -o context=XYZ mount option on
|
||||||
|
// given mount point.
|
||||||
|
func (hu *HostUtil) GetSELinuxMountContext(pathname string) (string, error) {
|
||||||
|
return getSELinuxMountContext(pathname, procMountInfoPath, selinux.GetEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSELinux is common implementation of GetSELinuxSupport on Linux.
|
||||||
|
// Using an extra function for unit tests.
|
||||||
|
func getSELinuxMountContext(path string, mountInfoFilename string, selinuxEnabled seLinuxEnabledFunc) (string, error) {
|
||||||
|
// Skip /proc/mounts parsing if SELinux is disabled.
|
||||||
|
if !selinuxEnabled() {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := findMountInfo(path, mountInfoFilename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range info.SuperOptions {
|
||||||
|
if !strings.HasPrefix(opt, "context=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Remove context=
|
||||||
|
context := strings.TrimPrefix(opt, "context=")
|
||||||
|
// Remove double quotes
|
||||||
|
context = strings.Trim(context, "\"")
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
@ -331,3 +331,60 @@ func writeFile(content string) (string, string, error) {
|
|||||||
}
|
}
|
||||||
return tempDir, filename, nil
|
return tempDir, filename, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSELinuxMountContext(t *testing.T) {
|
||||||
|
info :=
|
||||||
|
`840 60 8:0 / /var/lib/kubelet/pods/d4f3b306-ad4c-4f7a-8983-b5b228039a8c/volumes/kubernetes.io~iscsi/mypv rw,relatime shared:421 - ext4 /dev/sda rw,context="system_u:object_r:container_file_t:s0:c314,c894"
|
||||||
|
224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
|
||||||
|
82 62 0:43 / /var/lib/foo rw,relatime shared:32 - tmpfs tmpfs rw
|
||||||
|
83 63 0:44 / /var/lib/bar rw,relatime - tmpfs tmpfs rw
|
||||||
|
`
|
||||||
|
tempDir, filename, err := writeFile(info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create temporary file: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
mountPoint string
|
||||||
|
seLinuxEnabled bool
|
||||||
|
expectedContext string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"no context",
|
||||||
|
"/var/lib/foo",
|
||||||
|
true,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"with context with SELinux",
|
||||||
|
"/var/lib/kubelet/pods/d4f3b306-ad4c-4f7a-8983-b5b228039a8c/volumes/kubernetes.io~iscsi/mypv",
|
||||||
|
true,
|
||||||
|
"system_u:object_r:container_file_t:s0:c314,c894",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"with context with no SELinux",
|
||||||
|
"/var/lib/kubelet/pods/d4f3b306-ad4c-4f7a-8983-b5b228039a8c/volumes/kubernetes.io~iscsi/mypv",
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no context with seclabel",
|
||||||
|
"/var/lib/docker/devicemapper/test/shared",
|
||||||
|
true,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
out, err := getSELinuxMountContext(test.mountPoint, filename, func() bool { return test.seLinuxEnabled })
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test %s failed with error: %s", test.name, err)
|
||||||
|
}
|
||||||
|
if test.expectedContext != out {
|
||||||
|
t.Errorf("Test %s failed: expected %v, got %v", test.name, test.expectedContext, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -101,3 +101,9 @@ func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
|
|||||||
func getDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
func getDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
return "", errUnsupported
|
return "", errUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSELinuxMountContext returns value of -o context=XYZ mount option on
|
||||||
|
// given mount point.
|
||||||
|
func (hu *HostUtil) GetSELinuxMountContext(pathname string) (string, error) {
|
||||||
|
return "", errUnsupported
|
||||||
|
}
|
||||||
|
@ -123,3 +123,9 @@ func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
|
|||||||
}
|
}
|
||||||
return info.Mode(), nil
|
return info.Mode(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSELinuxMountContext returns value of -o context=XYZ mount option on
|
||||||
|
// given mount point.
|
||||||
|
func (hu *HostUtil) GetSELinuxMountContext(pathname string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user