Skip to content

Commit 62809dd

Browse files
committed
node audience restriction: use csi translator to convert intree inline_vol/pv to csi
Signed-off-by: Anish Ramasekar <[email protected]>
1 parent d6c50c3 commit 62809dd

File tree

2 files changed

+89
-27
lines changed

2 files changed

+89
-27
lines changed

plugin/pkg/admission/noderestriction/admission.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import (
3939
storagelisters "k8s.io/client-go/listers/storage/v1"
4040
"k8s.io/component-base/featuregate"
4141
"k8s.io/component-helpers/storage/ephemeral"
42+
csitrans "k8s.io/csi-translation-lib"
43+
"k8s.io/klog/v2"
4244
kubeletapis "k8s.io/kubelet/pkg/apis"
4345
podutil "k8s.io/kubernetes/pkg/api/pod"
4446
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
@@ -80,6 +82,7 @@ type Plugin struct {
8082
csiDriverGetter storagelisters.CSIDriverLister
8183
pvcGetter corev1lister.PersistentVolumeClaimLister
8284
pvGetter corev1lister.PersistentVolumeLister
85+
csiTranslator csitrans.CSITranslator
8386

8487
expansionRecoveryEnabled bool
8588
dynamicResourceAllocationEnabled bool
@@ -109,6 +112,7 @@ func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactor
109112
p.csiDriverGetter = f.Storage().V1().CSIDrivers().Lister()
110113
p.pvcGetter = f.Core().V1().PersistentVolumeClaims().Lister()
111114
p.pvGetter = f.Core().V1().PersistentVolumes().Lister()
115+
p.csiTranslator = csitrans.New()
112116
}
113117
}
114118

@@ -189,7 +193,7 @@ func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.
189193
}
190194

191195
case svcacctResource:
192-
return p.admitServiceAccount(nodeName, a)
196+
return p.admitServiceAccount(ctx, nodeName, a)
193197

194198
case leaseResource:
195199
return p.admitLease(nodeName, a)
@@ -581,7 +585,7 @@ func (p *Plugin) getForbiddenLabels(modifiedLabels sets.String) sets.String {
581585
return forbiddenLabels
582586
}
583587

584-
func (p *Plugin) admitServiceAccount(nodeName string, a admission.Attributes) error {
588+
func (p *Plugin) admitServiceAccount(ctx context.Context, nodeName string, a admission.Attributes) error {
585589
if a.GetOperation() != admission.Create {
586590
return nil
587591
}
@@ -620,7 +624,7 @@ func (p *Plugin) admitServiceAccount(nodeName string, a admission.Attributes) er
620624
}
621625

622626
if p.serviceAccountNodeAudienceRestriction {
623-
if err := p.validateNodeServiceAccountAudience(tr, pod); err != nil {
627+
if err := p.validateNodeServiceAccountAudience(ctx, tr, pod); err != nil {
624628
return admission.NewForbidden(a, err)
625629
}
626630
}
@@ -634,7 +638,7 @@ func (p *Plugin) admitServiceAccount(nodeName string, a admission.Attributes) er
634638
return nil
635639
}
636640

637-
func (p *Plugin) validateNodeServiceAccountAudience(tr *authenticationapi.TokenRequest, pod *v1.Pod) error {
641+
func (p *Plugin) validateNodeServiceAccountAudience(ctx context.Context, tr *authenticationapi.TokenRequest, pod *v1.Pod) error {
638642
// ensure all items in tr.Spec.Audiences are present in a volume mount in the pod
639643
requestedAudience := ""
640644
switch len(tr.Spec.Audiences) {
@@ -646,7 +650,7 @@ func (p *Plugin) validateNodeServiceAccountAudience(tr *authenticationapi.TokenR
646650
return fmt.Errorf("node may only request 0 or 1 audiences")
647651
}
648652

649-
foundAudiencesInPodSpec, err := p.podReferencesAudience(pod, requestedAudience)
653+
foundAudiencesInPodSpec, err := p.podReferencesAudience(ctx, pod, requestedAudience)
650654
if err != nil {
651655
return fmt.Errorf("error validating audience %q: %w", requestedAudience, err)
652656
}
@@ -656,7 +660,7 @@ func (p *Plugin) validateNodeServiceAccountAudience(tr *authenticationapi.TokenR
656660
return nil
657661
}
658662

659-
func (p *Plugin) podReferencesAudience(pod *v1.Pod, audience string) (bool, error) {
663+
func (p *Plugin) podReferencesAudience(ctx context.Context, pod *v1.Pod, audience string) (bool, error) {
660664
var errs []error
661665

662666
for _, v := range pod.Spec.Volumes {
@@ -677,11 +681,20 @@ func (p *Plugin) podReferencesAudience(pod *v1.Pod, audience string) (bool, erro
677681
switch {
678682
case v.Ephemeral != nil && v.Ephemeral.VolumeClaimTemplate != nil:
679683
pvcName := ephemeral.VolumeClaimName(pod, &v)
680-
driverName, err = p.getCSIFromPVC(pod.Namespace, pvcName)
684+
driverName, err = p.getCSIFromPVC(ctx, pod.Namespace, pvcName)
681685
case v.PersistentVolumeClaim != nil:
682-
driverName, err = p.getCSIFromPVC(pod.Namespace, v.PersistentVolumeClaim.ClaimName)
686+
driverName, err = p.getCSIFromPVC(ctx, pod.Namespace, v.PersistentVolumeClaim.ClaimName)
683687
case v.CSI != nil:
684688
driverName = v.CSI.Driver
689+
case p.csiTranslator.IsInlineMigratable(&v):
690+
pv, translateErr := p.csiTranslator.TranslateInTreeInlineVolumeToCSI(klog.FromContext(ctx), &v, pod.Namespace)
691+
if translateErr != nil {
692+
err = translateErr
693+
break
694+
}
695+
if pv != nil && pv.Spec.CSI != nil {
696+
driverName = pv.Spec.CSI.Driver
697+
}
685698
}
686699

687700
if err != nil {
@@ -705,7 +718,7 @@ func (p *Plugin) podReferencesAudience(pod *v1.Pod, audience string) (bool, erro
705718
}
706719

707720
// getCSIFromPVC returns the CSI driver name from the PVC->PV->CSI->Driver chain
708-
func (p *Plugin) getCSIFromPVC(namespace, claimName string) (string, error) {
721+
func (p *Plugin) getCSIFromPVC(ctx context.Context, namespace, claimName string) (string, error) {
709722
pvc, err := p.pvcGetter.PersistentVolumeClaims(namespace).Get(claimName)
710723
if err != nil {
711724
return "", err
@@ -717,6 +730,18 @@ func (p *Plugin) getCSIFromPVC(namespace, claimName string) (string, error) {
717730
if pv.Spec.CSI != nil {
718731
return pv.Spec.CSI.Driver, nil
719732
}
733+
734+
if p.csiTranslator.IsPVMigratable(pv) {
735+
// For in-tree PV, we need to convert ("translate") the PV to CSI before checking the driver name.
736+
translatedPV, err := p.csiTranslator.TranslateInTreePVToCSI(klog.FromContext(ctx), pv)
737+
if err != nil {
738+
return "", err
739+
}
740+
if translatedPV != nil && translatedPV.Spec.CSI != nil {
741+
return translatedPV.Spec.CSI.Driver, nil
742+
}
743+
}
744+
720745
return "", nil
721746
}
722747

test/integration/auth/node_test.go

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,27 @@ func TestNodeRestrictionServiceAccountAudience(t *testing.T) {
908908
}, metav1.CreateOptions{})
909909
checkNilError(t, err)
910910

911+
_, err = superuserClient.CoreV1().PersistentVolumeClaims("ns").Create(context.TODO(), &corev1.PersistentVolumeClaim{
912+
ObjectMeta: metav1.ObjectMeta{Name: "mypvc-azurefile"},
913+
Spec: corev1.PersistentVolumeClaimSpec{
914+
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany},
915+
Resources: corev1.VolumeResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("1")}},
916+
VolumeName: "mypv-azurefile",
917+
},
918+
}, metav1.CreateOptions{})
919+
checkNilError(t, err)
920+
921+
_, err = superuserClient.CoreV1().PersistentVolumes().Create(context.TODO(), &corev1.PersistentVolume{
922+
ObjectMeta: metav1.ObjectMeta{Name: "mypv-azurefile"},
923+
Spec: corev1.PersistentVolumeSpec{
924+
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany},
925+
Capacity: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("1")},
926+
ClaimRef: &corev1.ObjectReference{Namespace: "ns", Name: "mypvc-azurefile"},
927+
PersistentVolumeSource: corev1.PersistentVolumeSource{AzureFile: &corev1.AzureFilePersistentVolumeSource{ShareName: "share", SecretName: "secret"}},
928+
},
929+
}, metav1.CreateOptions{})
930+
checkNilError(t, err)
931+
911932
node1Client, _ := clientsetForToken(tokenNode1, clientConfig)
912933
createNode(t, node1Client, "node1")
913934
createDefaultServiceAccount(t, superuserClient)
@@ -934,12 +955,12 @@ func TestNodeRestrictionServiceAccountAudience(t *testing.T) {
934955
})
935956

936957
t.Run("pod --> csi --> driver --> tokenrequest with audience works", func(t *testing.T) {
937-
createCSIDriver(t, superuserClient, "csidriver-audience")
958+
createCSIDriver(t, superuserClient, "csidriver-audience", "com.example.csi.mydriver")
938959
csiDriverVolumeSource := &corev1.CSIVolumeSource{Driver: "com.example.csi.mydriver"}
939960
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{CSI: csiDriverVolumeSource}}})
940961
expectAllowed(t, createTokenRequest(node1Client, pod.UID, "csidriver-audience"))
941962
deletePod(t, superuserClient, "pod1")
942-
deleteCSIDriver(t, superuserClient)
963+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
943964
})
944965

945966
t.Run("pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience forbidden - CSI driver not found", func(t *testing.T) {
@@ -950,21 +971,21 @@ func TestNodeRestrictionServiceAccountAudience(t *testing.T) {
950971
})
951972

952973
t.Run("pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience forbidden - pvc not found", func(t *testing.T) {
953-
createCSIDriver(t, superuserClient, "pvcnotfound-audience")
974+
createCSIDriver(t, superuserClient, "pvcnotfound-audience", "com.example.csi.mydriver")
954975
persistentVolumeClaimVolumeSource := &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "mypvc1"}
955976
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: persistentVolumeClaimVolumeSource}}})
956977
expectedForbiddenMessage(t, createTokenRequest(node1Client, pod.UID, "pvcnotfound-audience"), `error validating audience "pvcnotfound-audience": persistentvolumeclaim "mypvc1" not found`)
957978
deletePod(t, superuserClient, "pod1")
958-
deleteCSIDriver(t, superuserClient)
979+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
959980
})
960981

961982
t.Run("pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience works", func(t *testing.T) {
962-
createCSIDriver(t, superuserClient, "pvccsidriver-audience")
983+
createCSIDriver(t, superuserClient, "pvccsidriver-audience", "com.example.csi.mydriver")
963984
persistentVolumeClaimVolumeSource := &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "mypvc"}
964985
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: persistentVolumeClaimVolumeSource}}})
965986
expectAllowed(t, createTokenRequest(node1Client, pod.UID, "pvccsidriver-audience"))
966987
deletePod(t, superuserClient, "pod1")
967-
deleteCSIDriver(t, superuserClient)
988+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
968989
})
969990

970991
t.Run("pod --> ephemeral --> pvc --> pv --> csi --> driver --> tokenrequest with audience forbidden - CSI driver not found", func(t *testing.T) {
@@ -980,7 +1001,7 @@ func TestNodeRestrictionServiceAccountAudience(t *testing.T) {
9801001
})
9811002

9821003
t.Run("pod --> ephemeral --> pvc --> pv --> csi --> driver --> tokenrequest with audience works", func(t *testing.T) {
983-
createCSIDriver(t, superuserClient, "ephemeralcsidriver-audience")
1004+
createCSIDriver(t, superuserClient, "ephemeralcsidriver-audience", "com.example.csi.mydriver")
9841005
ephemeralVolumeSource := &corev1.EphemeralVolumeSource{VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
9851006
Spec: corev1.PersistentVolumeClaimSpec{
9861007
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany},
@@ -990,28 +1011,28 @@ func TestNodeRestrictionServiceAccountAudience(t *testing.T) {
9901011
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{Ephemeral: ephemeralVolumeSource}}})
9911012
expectAllowed(t, createTokenRequest(node1Client, pod.UID, "ephemeralcsidriver-audience"))
9921013
deletePod(t, superuserClient, "pod1")
993-
deleteCSIDriver(t, superuserClient)
1014+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
9941015
})
9951016

9961017
t.Run("csidriver exists but tokenrequest audience not found should be forbidden", func(t *testing.T) {
997-
createCSIDriver(t, superuserClient, "csidriver-audience")
1018+
createCSIDriver(t, superuserClient, "csidriver-audience", "com.example.csi.mydriver")
9981019
pod := createPod(t, superuserClient, nil)
9991020
expectedForbiddenMessage(t, createTokenRequest(node1Client, pod.UID, "csidriver-audience-not-found"), `audience "csidriver-audience-not-found" not found in pod spec volume`)
10001021
deletePod(t, superuserClient, "pod1")
1001-
deleteCSIDriver(t, superuserClient)
1022+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
10021023
})
10031024

10041025
t.Run("pvc and csidriver exists but tokenrequest audience not found should be forbidden", func(t *testing.T) {
1005-
createCSIDriver(t, superuserClient, "csidriver-audience")
1026+
createCSIDriver(t, superuserClient, "csidriver-audience", "com.example.csi.mydriver")
10061027
persistentVolumeClaimVolumeSource := &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "mypvc"}
10071028
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: persistentVolumeClaimVolumeSource}}})
10081029
expectedForbiddenMessage(t, createTokenRequest(node1Client, pod.UID, "csidriver-audience-not-found"), `audience "csidriver-audience-not-found" not found in pod spec volume`)
10091030
deletePod(t, superuserClient, "pod1")
1010-
deleteCSIDriver(t, superuserClient)
1031+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
10111032
})
10121033

10131034
t.Run("ephemeral volume source with audience not found should be forbidden", func(t *testing.T) {
1014-
createCSIDriver(t, superuserClient, "csidriver-audience")
1035+
createCSIDriver(t, superuserClient, "csidriver-audience", "com.example.csi.mydriver")
10151036
ephemeralVolumeSource := &corev1.EphemeralVolumeSource{VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
10161037
Spec: corev1.PersistentVolumeClaimSpec{
10171038
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany},
@@ -1021,7 +1042,23 @@ func TestNodeRestrictionServiceAccountAudience(t *testing.T) {
10211042
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{Ephemeral: ephemeralVolumeSource}}})
10221043
expectedForbiddenMessage(t, createTokenRequest(node1Client, pod.UID, "csidriver-audience-not-found"), `audience "csidriver-audience-not-found" not found in pod spec volume`)
10231044
deletePod(t, superuserClient, "pod1")
1024-
deleteCSIDriver(t, superuserClient)
1045+
deleteCSIDriver(t, superuserClient, "com.example.csi.mydriver")
1046+
})
1047+
1048+
t.Run("intree pv to csi migration, pod --> csi --> driver --> tokenrequest with audience works", func(t *testing.T) {
1049+
createCSIDriver(t, superuserClient, "csidriver-audience", "file.csi.azure.com")
1050+
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "mypvc-azurefile"}}}})
1051+
expectAllowed(t, createTokenRequest(node1Client, pod.UID, "csidriver-audience"))
1052+
deletePod(t, superuserClient, "pod1")
1053+
deleteCSIDriver(t, superuserClient, "file.csi.azure.com")
1054+
})
1055+
1056+
t.Run("intree inline volume to csi migration, pod --> csi --> driver --> tokenrequest with audience works", func(t *testing.T) {
1057+
createCSIDriver(t, superuserClient, "csidriver-audience", "file.csi.azure.com")
1058+
pod := createPod(t, superuserClient, []corev1.Volume{{Name: "foo", VolumeSource: corev1.VolumeSource{AzureFile: &corev1.AzureFileVolumeSource{ShareName: "default", SecretName: "mypvsecret"}}}})
1059+
expectAllowed(t, createTokenRequest(node1Client, pod.UID, "csidriver-audience"))
1060+
deletePod(t, superuserClient, "pod1")
1061+
deleteCSIDriver(t, superuserClient, "file.csi.azure.com")
10251062
})
10261063

10271064
t.Run("token request with multiple audiences should be forbidden", func(t *testing.T) {
@@ -1090,22 +1127,22 @@ func createTokenRequest(client clientset.Interface, uid types.UID, audiences ...
10901127
}
10911128
}
10921129

1093-
func createCSIDriver(t *testing.T, client clientset.Interface, audience string) {
1130+
func createCSIDriver(t *testing.T, client clientset.Interface, audience, driverName string) {
10941131
t.Helper()
10951132

10961133
_, err := client.StorageV1().CSIDrivers().Create(context.TODO(), &storagev1.CSIDriver{
1097-
ObjectMeta: metav1.ObjectMeta{Name: "com.example.csi.mydriver"},
1134+
ObjectMeta: metav1.ObjectMeta{Name: driverName},
10981135
Spec: storagev1.CSIDriverSpec{
10991136
TokenRequests: []storagev1.TokenRequest{{Audience: audience}},
11001137
},
11011138
}, metav1.CreateOptions{})
11021139
checkNilError(t, err)
11031140
}
11041141

1105-
func deleteCSIDriver(t *testing.T, client clientset.Interface) {
1142+
func deleteCSIDriver(t *testing.T, client clientset.Interface, driverName string) {
11061143
t.Helper()
11071144

1108-
checkNilError(t, client.StorageV1().CSIDrivers().Delete(context.TODO(), "com.example.csi.mydriver", metav1.DeleteOptions{GracePeriodSeconds: ptr.To[int64](0)}))
1145+
checkNilError(t, client.StorageV1().CSIDrivers().Delete(context.TODO(), driverName, metav1.DeleteOptions{GracePeriodSeconds: ptr.To[int64](0)}))
11091146
}
11101147

11111148
func createDefaultServiceAccount(t *testing.T, client clientset.Interface) {

0 commit comments

Comments
 (0)