mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #121731 from Taction/service-account-token-projected-volume-validation
Fix service account token projected volume validation
This commit is contained in:
		@@ -353,6 +353,24 @@ func hasInvalidTopologySpreadConstraintLabelSelector(spec *api.PodSpec) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hasNonLocalProjectedTokenPath return true if spec.Volumes have any entry with non-local projected token path
 | 
			
		||||
func hasNonLocalProjectedTokenPath(spec *api.PodSpec) bool {
 | 
			
		||||
	for _, volume := range spec.Volumes {
 | 
			
		||||
		if volume.Projected != nil {
 | 
			
		||||
			for _, source := range volume.Projected.Sources {
 | 
			
		||||
				if source.ServiceAccountToken == nil {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				errs := apivalidation.ValidateLocalNonReservedPath(source.ServiceAccountToken.Path, nil)
 | 
			
		||||
				if len(errs) != 0 {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata
 | 
			
		||||
func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, podMeta, oldPodMeta *metav1.ObjectMeta) apivalidation.PodValidationOptions {
 | 
			
		||||
	// default pod validation options based on feature gate
 | 
			
		||||
@@ -366,6 +384,7 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
 | 
			
		||||
		AllowInvalidTopologySpreadConstraintLabelSelector: false,
 | 
			
		||||
		AllowMutableNodeSelectorAndNodeAffinity:           utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness),
 | 
			
		||||
		AllowNamespacedSysctlsForHostNetAndHostIPC:        false,
 | 
			
		||||
		AllowNonLocalProjectedTokenPath:                   false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if oldPodSpec != nil {
 | 
			
		||||
@@ -378,6 +397,8 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
 | 
			
		||||
		opts.AllowInvalidLabelValueInSelector = hasInvalidLabelValueInAffinitySelector(oldPodSpec)
 | 
			
		||||
		// if old spec has invalid labelSelector in topologySpreadConstraint, we must allow it
 | 
			
		||||
		opts.AllowInvalidTopologySpreadConstraintLabelSelector = hasInvalidTopologySpreadConstraintLabelSelector(oldPodSpec)
 | 
			
		||||
		// if old spec has an invalid projected token volume path, we must allow it
 | 
			
		||||
		opts.AllowNonLocalProjectedTokenPath = hasNonLocalProjectedTokenPath(oldPodSpec)
 | 
			
		||||
 | 
			
		||||
		// if old spec has invalid sysctl with hostNet or hostIPC, we must allow it when update
 | 
			
		||||
		if oldPodSpec.SecurityContext != nil && len(oldPodSpec.SecurityContext.Sysctls) != 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -2744,6 +2744,88 @@ func TestValidateTopologySpreadConstraintLabelSelectorOption(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidateAllowNonLocalProjectedTokenPathOption(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		oldPodSpec *api.PodSpec
 | 
			
		||||
		wantOption bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Create",
 | 
			
		||||
			wantOption: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "UpdateInvalidProjectedTokenPath",
 | 
			
		||||
			oldPodSpec: &api.PodSpec{
 | 
			
		||||
				Volumes: []api.Volume{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "foo",
 | 
			
		||||
						VolumeSource: api.VolumeSource{
 | 
			
		||||
							Projected: &api.ProjectedVolumeSource{
 | 
			
		||||
								Sources: []api.VolumeProjection{
 | 
			
		||||
									{
 | 
			
		||||
										ServiceAccountToken: &api.ServiceAccountTokenProjection{
 | 
			
		||||
											Path: "../foo",
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantOption: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "UpdateValidProjectedTokenPath",
 | 
			
		||||
			oldPodSpec: &api.PodSpec{
 | 
			
		||||
				Volumes: []api.Volume{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "foo",
 | 
			
		||||
						VolumeSource: api.VolumeSource{
 | 
			
		||||
							Projected: &api.ProjectedVolumeSource{
 | 
			
		||||
								Sources: []api.VolumeProjection{
 | 
			
		||||
									{
 | 
			
		||||
										ServiceAccountToken: &api.ServiceAccountTokenProjection{
 | 
			
		||||
											Path: "foo",
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantOption: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "UpdateEmptyProjectedTokenPath",
 | 
			
		||||
			oldPodSpec: &api.PodSpec{
 | 
			
		||||
				Volumes: []api.Volume{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "foo",
 | 
			
		||||
						VolumeSource: api.VolumeSource{
 | 
			
		||||
							Projected: nil,
 | 
			
		||||
							HostPath:  &api.HostPathVolumeSource{Path: "foo"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantOption: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			// Pod meta doesn't impact the outcome.
 | 
			
		||||
			gotOptions := GetValidationOptionsFromPodSpecAndMeta(&api.PodSpec{}, tc.oldPodSpec, nil, nil)
 | 
			
		||||
			if tc.wantOption != gotOptions.AllowNonLocalProjectedTokenPath {
 | 
			
		||||
				t.Errorf("Got AllowNonLocalProjectedTokenPath=%t, want %t", gotOptions.AllowNonLocalProjectedTokenPath, tc.wantOption)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDropInPlacePodVerticalScaling(t *testing.T) {
 | 
			
		||||
	podWithInPlaceVerticalScaling := func() *api.Pod {
 | 
			
		||||
		return &api.Pod{
 | 
			
		||||
 
 | 
			
		||||
@@ -942,7 +942,7 @@ func validateKeyToPath(kp *core.KeyToPath, fldPath *field.Path) field.ErrorList
 | 
			
		||||
	if len(kp.Path) == 0 {
 | 
			
		||||
		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
 | 
			
		||||
	}
 | 
			
		||||
	allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
 | 
			
		||||
	allErrs = append(allErrs, ValidateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
 | 
			
		||||
	if kp.Mode != nil && (*kp.Mode > 0777 || *kp.Mode < 0) {
 | 
			
		||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *kp.Mode, fileModeErrorMsg))
 | 
			
		||||
	}
 | 
			
		||||
@@ -1050,7 +1050,7 @@ func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *fi
 | 
			
		||||
	if len(file.Path) == 0 {
 | 
			
		||||
		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
 | 
			
		||||
	}
 | 
			
		||||
	allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
 | 
			
		||||
	allErrs = append(allErrs, ValidateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
 | 
			
		||||
	if file.FieldRef != nil {
 | 
			
		||||
		allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validVolumeDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
 | 
			
		||||
		if file.ResourceFieldRef != nil {
 | 
			
		||||
@@ -1153,6 +1153,8 @@ func validateProjectionSources(projection *core.ProjectedVolumeSource, projectio
 | 
			
		||||
			}
 | 
			
		||||
			if source.ServiceAccountToken.Path == "" {
 | 
			
		||||
				allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
 | 
			
		||||
			} else if !opts.AllowNonLocalProjectedTokenPath {
 | 
			
		||||
				allErrs = append(allErrs, ValidateLocalNonReservedPath(source.ServiceAccountToken.Path, fldPath.Child("path"))...)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if projPath := srcPath.Child("clusterTrustBundlePEM"); source.ClusterTrustBundle != nil {
 | 
			
		||||
@@ -1209,7 +1211,7 @@ func validateProjectionSources(projection *core.ProjectedVolumeSource, projectio
 | 
			
		||||
				allErrs = append(allErrs, field.Required(projPath.Child("path"), ""))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			allErrs = append(allErrs, validateLocalNonReservedPath(source.ClusterTrustBundle.Path, projPath.Child("path"))...)
 | 
			
		||||
			allErrs = append(allErrs, ValidateLocalNonReservedPath(source.ClusterTrustBundle.Path, projPath.Child("path"))...)
 | 
			
		||||
 | 
			
		||||
			curPath := source.ClusterTrustBundle.Path
 | 
			
		||||
			if !allPaths.Has(curPath) {
 | 
			
		||||
@@ -1319,11 +1321,11 @@ func validateMountPropagation(mountPropagation *core.MountPropagationMode, conta
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This validate will make sure targetPath:
 | 
			
		||||
// ValidateLocalNonReservedPath makes sure targetPath:
 | 
			
		||||
// 1. is not abs path
 | 
			
		||||
// 2. does not contain any '..' elements
 | 
			
		||||
// 3. does not start with '..'
 | 
			
		||||
func validateLocalNonReservedPath(targetPath string, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
func ValidateLocalNonReservedPath(targetPath string, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	allErrs := field.ErrorList{}
 | 
			
		||||
	allErrs = append(allErrs, validateLocalDescendingPath(targetPath, fldPath)...)
 | 
			
		||||
	// Don't report this error if the check for .. elements already caught it.
 | 
			
		||||
@@ -3978,6 +3980,8 @@ type PodValidationOptions struct {
 | 
			
		||||
	AllowInvalidTopologySpreadConstraintLabelSelector bool
 | 
			
		||||
	// Allow node selector additions for gated pods.
 | 
			
		||||
	AllowMutableNodeSelectorAndNodeAffinity bool
 | 
			
		||||
	// Allow projected token volumes with non-local paths
 | 
			
		||||
	AllowNonLocalProjectedTokenPath bool
 | 
			
		||||
	// Allow namespaced sysctls in hostNet and hostIPC pods
 | 
			
		||||
	AllowNamespacedSysctlsForHostNetAndHostIPC bool
 | 
			
		||||
	// The top-level resource being validated is a Pod, not just a PodSpec
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user