Skip to content

Commit 1135d04

Browse files
authored
Merge pull request kubernetes#134826 from aramase/aramase/f/kep_5538_beta_impl
CSI driver opt-in for service account tokens via secrets field
2 parents ec5425a + 30a6d4b commit 1135d04

39 files changed

+1102
-131
lines changed

api/openapi-spec/swagger.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/openapi-spec/v3/apis__storage.k8s.io__v1_openapi.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/storage/types.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,31 @@ type CSIDriverSpec struct {
433433
// +featureGate=MutableCSINodeAllocatableCount
434434
// +optional
435435
NodeAllocatableUpdatePeriodSeconds *int64
436+
437+
// serviceAccountTokenInSecrets is an opt-in for CSI drivers to indicate that
438+
// service account tokens should be passed via the Secrets field in NodePublishVolumeRequest
439+
// instead of the VolumeContext field. The CSI specification provides a dedicated Secrets
440+
// field for sensitive information like tokens, which is the appropriate mechanism for
441+
// handling credentials. This addresses security concerns where sensitive tokens were being
442+
// logged as part of volume context, leading to vulnerabilities like CVE-2023-2878 and
443+
// CVE-2024-3744.
444+
//
445+
// When "true", kubelet will pass the tokens only in the Secrets field with the key
446+
// "csi.storage.k8s.io/serviceAccount.tokens". The CSI driver must be updated to read
447+
// tokens from the Secrets field instead of VolumeContext.
448+
//
449+
// When "false" or not set, kubelet will pass the tokens in VolumeContext with the key
450+
// "csi.storage.k8s.io/serviceAccount.tokens" (existing behavior). This maintains backward
451+
// compatibility with existing CSI drivers.
452+
//
453+
// This field can only be set when TokenRequests is configured. The API server will reject
454+
// CSIDriver specs that set this field without TokenRequests.
455+
//
456+
// Default is "false".
457+
//
458+
// +featureGate=CSIServiceAccountTokenSecrets
459+
// +optional
460+
ServiceAccountTokenInSecrets *bool
436461
}
437462

438463
// FSGroupPolicy specifies if a CSI Driver supports modifying

pkg/apis/storage/v1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/storage/v1beta1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/storage/validation/validation.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ func validateCSIDriverSpec(
456456
allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...)
457457
allErrs = append(allErrs, validateSELinuxMount(spec.SELinuxMount, fldPath.Child("seLinuxMount"))...)
458458
allErrs = append(allErrs, validateNodeAllocatableUpdatePeriodSeconds(spec.NodeAllocatableUpdatePeriodSeconds, fldPath.Child("nodeAllocatableUpdatePeriodSeconds"))...)
459+
allErrs = append(allErrs, validateServiceAccountTokenInSecrets(spec.ServiceAccountTokenInSecrets, spec.TokenRequests, fldPath.Child("serviceAccountTokenInSecrets"))...)
459460
return allErrs
460461
}
461462

@@ -572,6 +573,16 @@ func validateSELinuxMount(seLinuxMount *bool, fldPath *field.Path) field.ErrorLi
572573
return allErrs
573574
}
574575

576+
// validvalidateServiceAccountTokenInSecrets validates serviceAccountTokenInSecrets and its relation to tokenRequests.
577+
func validateServiceAccountTokenInSecrets(serviceAccountTokenInSecrets *bool, tokenRequests []storage.TokenRequest, fldPath *field.Path) field.ErrorList {
578+
allErrs := field.ErrorList{}
579+
if serviceAccountTokenInSecrets != nil && len(tokenRequests) == 0 {
580+
allErrs = append(allErrs, field.Invalid(fldPath, serviceAccountTokenInSecrets, "serviceAccountTokenInSecrets is set but no tokenRequests are specified"))
581+
}
582+
583+
return allErrs
584+
}
585+
575586
// ValidateStorageCapacityName checks that a name is appropriate for a
576587
// CSIStorageCapacity object.
577588
var ValidateStorageCapacityName = apimachineryvalidation.NameIsDNSSubdomain

pkg/apis/storage/validation/validation_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,7 @@ func TestCSIDriverValidation(t *testing.T) {
15231523
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)
15241524
// assume this feature is on for this test, detailed enabled/disabled tests in TestMutableCSINodeAllocatableCountEnabledDisabled
15251525
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutableCSINodeAllocatableCount, true)
1526+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIServiceAccountTokenSecrets, true)
15261527

15271528
driverName := "test-driver"
15281529
longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
@@ -1536,10 +1537,13 @@ func TestCSIDriverValidation(t *testing.T) {
15361537
notStorageCapacity := false
15371538
seLinuxMount := true
15381539
notSELinuxMount := false
1540+
serviceAccountTokenInSecrets := true
1541+
notServiceAccountTokenInSecrets := false
15391542
supportedFSGroupPolicy := storage.FileFSGroupPolicy
15401543
invalidFSGroupPolicy := storage.FSGroupPolicy("invalid-mode")
15411544
validNodeAllocatableUpdatePeriodSeconds := int64(10)
15421545
invalidNodeAllocatableUpdatePeriodSeconds := int64(9)
1546+
tokenRequests := []storage.TokenRequest{{Audience: "test-audience"}}
15431547
successCases := []storage.CSIDriver{{
15441548
ObjectMeta: metav1.ObjectMeta{Name: driverName},
15451549
Spec: storage.CSIDriverSpec{
@@ -1709,6 +1713,41 @@ func TestCSIDriverValidation(t *testing.T) {
17091713
SELinuxMount: &seLinuxMount,
17101714
NodeAllocatableUpdatePeriodSeconds: &validNodeAllocatableUpdatePeriodSeconds,
17111715
},
1716+
}, {
1717+
// With ServiceAccountTokenInSecrets set to true with TokenRequests
1718+
ObjectMeta: metav1.ObjectMeta{Name: driverName},
1719+
Spec: storage.CSIDriverSpec{
1720+
AttachRequired: &attachNotRequired,
1721+
PodInfoOnMount: &notPodInfoOnMount,
1722+
RequiresRepublish: &notRequiresRepublish,
1723+
StorageCapacity: &storageCapacity,
1724+
SELinuxMount: &seLinuxMount,
1725+
ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets,
1726+
TokenRequests: tokenRequests,
1727+
},
1728+
}, {
1729+
// With ServiceAccountTokenInSecrets set to false with TokenRequests
1730+
ObjectMeta: metav1.ObjectMeta{Name: driverName},
1731+
Spec: storage.CSIDriverSpec{
1732+
AttachRequired: &attachNotRequired,
1733+
PodInfoOnMount: &notPodInfoOnMount,
1734+
RequiresRepublish: &notRequiresRepublish,
1735+
StorageCapacity: &storageCapacity,
1736+
SELinuxMount: &seLinuxMount,
1737+
ServiceAccountTokenInSecrets: &notServiceAccountTokenInSecrets,
1738+
TokenRequests: tokenRequests,
1739+
},
1740+
}, {
1741+
// With ServiceAccountTokenInSecrets set to nil (not set)
1742+
ObjectMeta: metav1.ObjectMeta{Name: driverName},
1743+
Spec: storage.CSIDriverSpec{
1744+
AttachRequired: &attachNotRequired,
1745+
PodInfoOnMount: &notPodInfoOnMount,
1746+
RequiresRepublish: &notRequiresRepublish,
1747+
StorageCapacity: &storageCapacity,
1748+
SELinuxMount: &seLinuxMount,
1749+
ServiceAccountTokenInSecrets: nil,
1750+
},
17121751
}}
17131752

17141753
for _, csiDriver := range successCases {
@@ -1799,6 +1838,16 @@ func TestCSIDriverValidation(t *testing.T) {
17991838
SELinuxMount: &seLinuxMount,
18001839
NodeAllocatableUpdatePeriodSeconds: &invalidNodeAllocatableUpdatePeriodSeconds,
18011840
},
1841+
}, {
1842+
// ServiceAccountTokenInSecrets set without TokenRequests (invalid)
1843+
ObjectMeta: metav1.ObjectMeta{Name: driverName},
1844+
Spec: storage.CSIDriverSpec{
1845+
AttachRequired: &attachNotRequired,
1846+
PodInfoOnMount: &notPodInfoOnMount,
1847+
StorageCapacity: &storageCapacity,
1848+
SELinuxMount: &seLinuxMount,
1849+
ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets,
1850+
},
18021851
}}
18031852

18041853
for _, csiDriver := range errorCases {
@@ -1813,6 +1862,7 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
18131862
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)
18141863
// assume this feature is on for this test, detailed enabled/disabled tests in TestMutableCSINodeAllocatableCountEnabledDisabled
18151864
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutableCSINodeAllocatableCount, true)
1865+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIServiceAccountTokenSecrets, true)
18161866

18171867
driverName := "test-driver"
18181868
longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
@@ -1828,8 +1878,11 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
18281878
notStorageCapacity := false
18291879
seLinuxMount := true
18301880
notSELinuxMount := false
1881+
serviceAccountTokenInSecrets := true
1882+
notServiceAccountTokenInSecrets := false
18311883
validNodeAllocatableUpdatePeriodSeconds := int64(10)
18321884
invalidNodeAllocatableUpdatePeriodSeconds := int64(9)
1885+
tokenRequests := []storage.TokenRequest{{Audience: "test-audience"}}
18331886

18341887
old := storage.CSIDriver{
18351888
ObjectMeta: metav1.ObjectMeta{Name: driverName, ResourceVersion: "1"},
@@ -1888,6 +1941,26 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
18881941
modify: func(new *storage.CSIDriver) {
18891942
new.Spec.NodeAllocatableUpdatePeriodSeconds = &validNodeAllocatableUpdatePeriodSeconds
18901943
},
1944+
}, {
1945+
name: "change ServiceAccountTokenInSecrets from nil to true with TokenRequests",
1946+
modify: func(new *storage.CSIDriver) {
1947+
new.Spec.ServiceAccountTokenInSecrets = &serviceAccountTokenInSecrets
1948+
new.Spec.TokenRequests = tokenRequests
1949+
},
1950+
}, {
1951+
name: "change ServiceAccountTokenInSecrets from nil to false with TokenRequests",
1952+
modify: func(new *storage.CSIDriver) {
1953+
new.Spec.ServiceAccountTokenInSecrets = &notServiceAccountTokenInSecrets
1954+
new.Spec.TokenRequests = tokenRequests
1955+
},
1956+
}, {
1957+
name: "change ServiceAccountTokenInSecrets from true to false",
1958+
modify: func(new *storage.CSIDriver) {
1959+
new.Spec.ServiceAccountTokenInSecrets = &serviceAccountTokenInSecrets
1960+
new.Spec.TokenRequests = tokenRequests
1961+
old := new.DeepCopy()
1962+
old.Spec.ServiceAccountTokenInSecrets = &notServiceAccountTokenInSecrets
1963+
},
18911964
}}
18921965

18931966
for _, test := range successCases {
@@ -1980,6 +2053,11 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
19802053
modify: func(new *storage.CSIDriver) {
19812054
new.Spec.NodeAllocatableUpdatePeriodSeconds = &invalidNodeAllocatableUpdatePeriodSeconds
19822055
},
2056+
}, {
2057+
name: "ServiceAccountTokenInSecrets set without TokenRequests",
2058+
modify: func(new *storage.CSIDriver) {
2059+
new.Spec.ServiceAccountTokenInSecrets = &serviceAccountTokenInSecrets
2060+
},
19832061
}}
19842062

19852063
for _, test := range errorCases {

pkg/apis/storage/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/features/kube_features.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ const (
116116
// Enables the Portworx in-tree driver to Portworx migration feature.
117117
CSIMigrationPortworx featuregate.Feature = "CSIMigrationPortworx"
118118

119+
// owner: @aramase
120+
// kep: http://kep.k8s.io/5538
121+
//
122+
// Enables CSI drivers to opt-in for receiving service account tokens from kubelet
123+
// through the dedicated secrets field in NodePublishVolumeRequest instead of the volume_context field.
124+
CSIServiceAccountTokenSecrets featuregate.Feature = "CSIServiceAccountTokenSecrets"
125+
119126
// owner: @fengzixu
120127
//
121128
// Enables kubelet to detect CSI volume condition and send the event of the abnormal volume to the corresponding pod that is using it.
@@ -1097,6 +1104,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
10971104
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.36
10981105
},
10991106

1107+
CSIServiceAccountTokenSecrets: {
1108+
{Version: version.MustParse("1.35"), Default: true, PreRelease: featuregate.Beta},
1109+
},
1110+
11001111
CSIVolumeHealth: {
11011112
{Version: version.MustParse("1.21"), Default: false, PreRelease: featuregate.Alpha},
11021113
},
@@ -2016,6 +2027,8 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
20162027

20172028
CSIMigrationPortworx: {},
20182029

2030+
CSIServiceAccountTokenSecrets: {},
2031+
20192032
CSIVolumeHealth: {},
20202033

20212034
ClearingNominatedNodeNameAfterBinding: {},

0 commit comments

Comments
 (0)