-
Notifications
You must be signed in to change notification settings - Fork 38.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure HPA has valid resource/name/subresource, validate path segments #16717
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,33 @@ func validateHorizontalPodAutoscalerSpec(autoscaler extensions.HorizontalPodAuto | |
if autoscaler.CPUUtilization != nil && autoscaler.CPUUtilization.TargetPercentage < 1 { | ||
allErrs = append(allErrs, errs.NewFieldInvalid("cpuUtilization.targetPercentage", autoscaler.CPUUtilization.TargetPercentage, `must be bigger or equal to 1`)) | ||
} | ||
if refErrs := ValidateSubresourceReference(autoscaler.ScaleRef); len(refErrs) > 0 { | ||
allErrs = append(allErrs, refErrs.Prefix("scaleRef")...) | ||
} else if autoscaler.ScaleRef.Subresource != "scale" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we have this lever? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's a good question, but as long as we do, we need to make sure we're not accepting bad data, so when we start making use of it, the world doesn't fall apart. @jszczepkowski, any feedback on why we have this field, and why we're not using it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Subresource is a generic reference, which may point to other sub-resources than scale. In HPA, we need to ensure that it points to scale. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see why it has to be named "scale", as long as the referenced subresource accepts and returns API objects of the correct type |
||
allErrs = append(allErrs, errs.NewFieldValueNotSupported("scaleRef.subresource", autoscaler.ScaleRef.Subresource, []string{"scale"})) | ||
} | ||
return allErrs | ||
} | ||
|
||
func ValidateSubresourceReference(ref extensions.SubresourceReference) errs.ValidationErrorList { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we do, it's unused, and it's unclear (to me) what the validation should be |
||
allErrs := errs.ValidationErrorList{} | ||
if len(ref.Kind) == 0 { | ||
allErrs = append(allErrs, errs.NewFieldRequired("kind")) | ||
} else if ok, msg := apivalidation.IsValidPathSegmentName(ref.Kind); !ok { | ||
allErrs = append(allErrs, errs.NewFieldInvalid("kind", ref.Kind, msg)) | ||
} | ||
|
||
if len(ref.Name) == 0 { | ||
allErrs = append(allErrs, errs.NewFieldRequired("name")) | ||
} else if ok, msg := apivalidation.IsValidPathSegmentName(ref.Name); !ok { | ||
allErrs = append(allErrs, errs.NewFieldInvalid("name", ref.Name, msg)) | ||
} | ||
|
||
if len(ref.Subresource) == 0 { | ||
allErrs = append(allErrs, errs.NewFieldRequired("subresource")) | ||
} else if ok, msg := apivalidation.IsValidPathSegmentName(ref.Subresource); !ok { | ||
allErrs = append(allErrs, errs.NewFieldInvalid("subresource", ref.Subresource, msg)) | ||
} | ||
return allErrs | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ import ( | |
"github.com/golang/glog" | ||
"k8s.io/kubernetes/pkg/api/errors" | ||
"k8s.io/kubernetes/pkg/api/unversioned" | ||
"k8s.io/kubernetes/pkg/api/validation" | ||
"k8s.io/kubernetes/pkg/client/metrics" | ||
"k8s.io/kubernetes/pkg/conversion/queryparams" | ||
"k8s.io/kubernetes/pkg/fields" | ||
|
@@ -149,6 +150,10 @@ func (r *Request) Resource(resource string) *Request { | |
r.err = fmt.Errorf("resource already set to %q, cannot change to %q", r.resource, resource) | ||
return r | ||
} | ||
if ok, msg := validation.IsValidPathSegmentName(resource); !ok { | ||
r.err = fmt.Errorf("invalid resource %q: %s", resource, msg) | ||
return r | ||
} | ||
r.resource = resource | ||
return r | ||
} | ||
|
@@ -164,6 +169,12 @@ func (r *Request) SubResource(subresources ...string) *Request { | |
r.err = fmt.Errorf("subresource already set to %q, cannot change to %q", r.resource, subresource) | ||
return r | ||
} | ||
for _, s := range subresources { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same everywhere in file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we either have to guard everywhere every one of these methods is called, or guard here. the primary problem is with names that will get collapsed or traverse client-side There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see examples called out in #16717 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixing this in the client is at best an optimization for non-adversarial clients, and I worry it will lead to us leaving a vulnerability in the server. If that doesn't persuade you, then I would at least like to not import the entire api/validation package, and I think it might be simpler to check the path once in the function that builds the final URL. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You need both client-side and server-side checks. Server-side URL normalization we get for free(ish) from ServeMux, and we obviously want to defer as much validation to the server as possible, but it only makes sense to prevent the client from building requests that are invalid, yet will appear valid to the server (like path segments that will collapse or traverse).
I can copy the check into this package if you like.
The checks need to remain on the individual segments. Once you combine segments into the final URL, you lose to ability to detect invalid resource names like "foo/bar" (not to mention path.Join(...) does a Clean, which also masks traversal) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Although we're already using these packages:
Why don't you want to use a shared function for minimal safe path segment validation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a huge import tree right now, which makes the client inconvenient for people to use, I'd like to not make that problem worse. But I guess we can fix that problem later. I guess I will not object as long as we're all on the same page that we do not get any additional security from these checks in the client. |
||
if ok, msg := validation.IsValidPathSegmentName(s); !ok { | ||
r.err = fmt.Errorf("invalid subresource %q: %s", s, msg) | ||
return r | ||
} | ||
} | ||
r.subresource = subresource | ||
return r | ||
} | ||
|
@@ -181,6 +192,10 @@ func (r *Request) Name(resourceName string) *Request { | |
r.err = fmt.Errorf("resource name already set to %q, cannot change to %q", r.resourceName, resourceName) | ||
return r | ||
} | ||
if ok, msg := validation.IsValidPathSegmentName(resourceName); !ok { | ||
r.err = fmt.Errorf("invalid resource name %q: %s", resourceName, msg) | ||
return r | ||
} | ||
r.resourceName = resourceName | ||
return r | ||
} | ||
|
@@ -194,6 +209,10 @@ func (r *Request) Namespace(namespace string) *Request { | |
r.err = fmt.Errorf("namespace already set to %q, cannot change to %q", r.namespace, namespace) | ||
return r | ||
} | ||
if ok, msg := validation.IsValidPathSegmentName(namespace); !ok { | ||
r.err = fmt.Errorf("invalid namespace %q: %s", namespace, msg) | ||
return r | ||
} | ||
r.namespaceSet = true | ||
r.namespace = namespace | ||
return r | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an example for why you would call this function and not
IsValidPathSegmentName
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to comply with the name validation required by ValidateObjectMeta, implemented here: https://github.com/kubernetes/kubernetes/pull/16717/files#diff-b72277cd2bba86ca73e54d9589bdf5f9R62
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, sorry, the "prefix" was confusing me.