mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +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:
commit
fc4d6ac8ea
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user