Skip to content

Commit 7b38ff4

Browse files
authored
Merge pull request kubernetes#129993 from aramase/aramase/i/fix_129935
Fix service account node audience restriction for in-tree pv to csi migration
2 parents e6be5f9 + 62809dd commit 7b38ff4

File tree

3 files changed

+162
-27
lines changed

3 files changed

+162
-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

plugin/pkg/admission/noderestriction/admission_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,19 @@ func Test_nodePlugin_Admit(t *testing.T) {
408408
},
409409
}
410410

411+
azureFileCSIDriver = &storagev1.CSIDriver{
412+
ObjectMeta: metav1.ObjectMeta{
413+
Name: "file.csi.azure.com",
414+
},
415+
Spec: storagev1.CSIDriverSpec{
416+
TokenRequests: []storagev1.TokenRequest{
417+
{
418+
Audience: "foo",
419+
},
420+
},
421+
},
422+
}
423+
411424
csiDriverIndex = cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil)
412425
csiDriverLister = storagelisters.NewCSIDriverLister(csiDriverIndex)
413426

@@ -424,6 +437,16 @@ func Test_nodePlugin_Admit(t *testing.T) {
424437
},
425438
}
426439

440+
pvcWithIntreeAzureFile = &corev1.PersistentVolumeClaim{
441+
ObjectMeta: metav1.ObjectMeta{
442+
Name: "pvclaim-azurefile",
443+
Namespace: "ns",
444+
},
445+
Spec: corev1.PersistentVolumeClaimSpec{
446+
VolumeName: "pvname-azurefile",
447+
},
448+
}
449+
427450
ephemeralVolumePVCWithCSIDriver = &corev1.PersistentVolumeClaim{
428451
ObjectMeta: metav1.ObjectMeta{
429452
Name: "myephemeralpod-myvol",
@@ -451,6 +474,20 @@ func Test_nodePlugin_Admit(t *testing.T) {
451474
},
452475
}
453476

477+
pvWithIntreeAzureFile = &corev1.PersistentVolume{
478+
ObjectMeta: metav1.ObjectMeta{
479+
Name: "pvname-azurefile",
480+
},
481+
Spec: corev1.PersistentVolumeSpec{
482+
ClaimRef: &corev1.ObjectReference{
483+
Namespace: "ns",
484+
},
485+
PersistentVolumeSource: corev1.PersistentVolumeSource{
486+
AzureFile: &corev1.AzureFilePersistentVolumeSource{ShareName: "default", SecretName: "secret"},
487+
},
488+
},
489+
}
490+
454491
pvIndex = cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil)
455492
pvLister = corev1lister.NewPersistentVolumeLister(pvIndex)
456493

@@ -463,6 +500,7 @@ func Test_nodePlugin_Admit(t *testing.T) {
463500
projectedVolumeSource := &corev1.ProjectedVolumeSource{Sources: []corev1.VolumeProjection{{ServiceAccountToken: &corev1.ServiceAccountTokenProjection{Audience: "foo"}}}}
464501
csiDriverVolumeSource := &corev1.CSIVolumeSource{Driver: "com.example.csi.mydriver"}
465502
persistentVolumeClaimVolumeSource := &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "pvclaim"}
503+
persistentVolumeClaimVolumeSourceAzureFile := &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "pvclaim-azurefile"}
466504
ephemeralVolumeSource := &corev1.EphemeralVolumeSource{VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{}}
467505

468506
coremypodWithProjectedServiceAccountEmptyAudience, v1mypodWithProjectedServiceAccountEmptyAudience := makeTestPod("ns", "mysapod", "mynode", false)
@@ -483,10 +521,19 @@ func Test_nodePlugin_Admit(t *testing.T) {
483521
coremypodWithPVCAndCSI, v1mypodWithPVCAndCSI := makeTestPod("ns", "mypvcandcsipod", "mynode", false)
484522
v1mypodWithPVCAndCSI.Spec.Volumes = []corev1.Volume{{VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: persistentVolumeClaimVolumeSource}}, {VolumeSource: corev1.VolumeSource{CSI: csiDriverVolumeSource}}}
485523

524+
coremypodIntreeInlineVolToCSI, v1mypodIntreeInlineVolToCSI := makeTestPod("ns", "myintreeinlinevoltocsipod", "mynode", false)
525+
v1mypodIntreeInlineVolToCSI.Spec.Volumes = []corev1.Volume{{VolumeSource: corev1.VolumeSource{AzureFile: &corev1.AzureFileVolumeSource{ShareName: "default", SecretName: "secret"}}}}
526+
527+
coremypodIntreePVToCSI, v1mypodIntreePVToCSI := makeTestPod("ns", "myintreepvtocsipod", "mynode", false)
528+
v1mypodIntreePVToCSI.Spec.Volumes = []corev1.Volume{{VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: persistentVolumeClaimVolumeSourceAzureFile}}}
529+
486530
checkNilError(t, csiDriverIndex.Add(csiDriverWithAudience))
531+
checkNilError(t, csiDriverIndex.Add(azureFileCSIDriver))
487532
checkNilError(t, pvcIndex.Add(pvcWithCSIDriver))
533+
checkNilError(t, pvcIndex.Add(pvcWithIntreeAzureFile))
488534
checkNilError(t, pvcIndex.Add(ephemeralVolumePVCWithCSIDriver))
489535
checkNilError(t, pvIndex.Add(pvWithCSIDriver))
536+
checkNilError(t, pvIndex.Add(pvWithIntreeAzureFile))
490537

491538
existingPodsIndex.Add(v1mymirrorpod)
492539
existingPodsIndex.Add(v1othermirrorpod)
@@ -501,6 +548,8 @@ func Test_nodePlugin_Admit(t *testing.T) {
501548
checkNilError(t, existingPodsIndex.Add(v1mypodWithPVCRefCSI))
502549
checkNilError(t, existingPodsIndex.Add(v1mypodWithEphemeralVolume))
503550
checkNilError(t, existingPodsIndex.Add(v1mypodWithPVCAndCSI))
551+
checkNilError(t, existingPodsIndex.Add(v1mypodIntreePVToCSI))
552+
checkNilError(t, existingPodsIndex.Add(v1mypodIntreeInlineVolToCSI))
504553

505554
existingNodesIndex.Add(&corev1.Node{ObjectMeta: mynodeObjMeta})
506555

@@ -1430,6 +1479,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
14301479
},
14311480
attributes: admission.NewAttributesRecord(makeTokenRequest(coremypodWithCSI.Name, v1mypodWithCSI.UID, []string{"foo"}), nil, tokenrequestKind, coremypod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
14321481
},
1482+
{
1483+
name: "intree pv to csi, allow create of token when audience in pod --> csi --> driver --> tokenrequest with audience, ServiceAccountNodeAudienceRestriction=true",
1484+
podsGetter: existingPods,
1485+
csiDriverGetter: csiDriverLister,
1486+
pvcGetter: pvcLister,
1487+
pvGetter: pvLister,
1488+
features: feature.DefaultFeatureGate,
1489+
setupFunc: func(t *testing.T) {
1490+
t.Helper()
1491+
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.ServiceAccountNodeAudienceRestriction, true)
1492+
},
1493+
attributes: admission.NewAttributesRecord(makeTokenRequest(coremypodIntreePVToCSI.Name, v1mypodIntreePVToCSI.UID, []string{"foo"}), nil, tokenrequestKind, coremypod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
1494+
},
1495+
{
1496+
name: "intree inline vol to csi, allow create of token when audience in pod --> csi --> driver --> tokenrequest with audience, ServiceAccountNodeAudienceRestriction=true",
1497+
podsGetter: existingPods,
1498+
csiDriverGetter: csiDriverLister,
1499+
features: feature.DefaultFeatureGate,
1500+
setupFunc: func(t *testing.T) {
1501+
t.Helper()
1502+
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.ServiceAccountNodeAudienceRestriction, true)
1503+
},
1504+
attributes: admission.NewAttributesRecord(makeTokenRequest(coremypodIntreeInlineVolToCSI.Name, v1mypodIntreeInlineVolToCSI.UID, []string{"foo"}), nil, tokenrequestKind, coremypod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
1505+
},
14331506

14341507
// Unrelated objects
14351508
{

0 commit comments

Comments
 (0)