Limit YAML/JSON decode size

Kubernetes-commit: 8ef4566cefebf49f9a806a36df2105c9149785a1
This commit is contained in:
Jordan Liggitt 2019-09-27 16:36:48 -04:00 committed by Kubernetes Publisher
parent 3653eff0ed
commit f7fbf2eee4
3 changed files with 29 additions and 13 deletions

View File

@ -195,11 +195,11 @@ func (f *fieldManager) Apply(liveObj runtime.Object, patch []byte, fieldManager
patchObj := &unstructured.Unstructured{Object: map[string]interface{}{}} patchObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
if err := yaml.Unmarshal(patch, &patchObj.Object); err != nil { if err := yaml.Unmarshal(patch, &patchObj.Object); err != nil {
return nil, fmt.Errorf("error decoding YAML: %v", err) return nil, errors.NewBadRequest(fmt.Sprintf("error decoding YAML: %v", err))
} }
if patchObj.GetManagedFields() != nil { if patchObj.GetManagedFields() != nil {
return nil, fmt.Errorf("managed fields must be nil but was %v", patchObj.GetManagedFields()) return nil, errors.NewBadRequest(fmt.Sprintf("metadata.managedFields must be nil"))
} }
if patchObj.GetAPIVersion() != f.groupVersion.String() { if patchObj.GetAPIVersion() != f.groupVersion.String() {

View File

@ -337,6 +337,15 @@ func (p *jsonPatcher) createNewObject() (runtime.Object, error) {
func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr error) { func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr error) {
switch p.patchType { switch p.patchType {
case types.JSONPatchType: case types.JSONPatchType:
// sanity check potentially abusive patches
// TODO(liggitt): drop this once golang json parser limits stack depth (https://github.com/golang/go/issues/31789)
if len(p.patchBytes) > 1024*1024 {
v := []interface{}{}
if err := json.Unmarshal(p.patchBytes, v); err != nil {
return nil, errors.NewBadRequest(fmt.Sprintf("error decoding patch: %v", err))
}
}
patchObj, err := jsonpatch.DecodePatch(p.patchBytes) patchObj, err := jsonpatch.DecodePatch(p.patchBytes)
if err != nil { if err != nil {
return nil, errors.NewBadRequest(err.Error()) return nil, errors.NewBadRequest(err.Error())
@ -352,6 +361,15 @@ func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr
} }
return patchedJS, nil return patchedJS, nil
case types.MergePatchType: case types.MergePatchType:
// sanity check potentially abusive patches
// TODO(liggitt): drop this once golang json parser limits stack depth (https://github.com/golang/go/issues/31789)
if len(p.patchBytes) > 1024*1024 {
v := map[string]interface{}{}
if err := json.Unmarshal(p.patchBytes, v); err != nil {
return nil, errors.NewBadRequest(fmt.Sprintf("error decoding patch: %v", err))
}
}
return jsonpatch.MergePatch(versionedJS, p.patchBytes) return jsonpatch.MergePatch(versionedJS, p.patchBytes)
default: default:
// only here as a safety net - go-restful filters content-type // only here as a safety net - go-restful filters content-type

View File

@ -180,7 +180,7 @@ type Config struct {
// patch may cause. // patch may cause.
// This affects all places that applies json patch in the binary. // This affects all places that applies json patch in the binary.
JSONPatchMaxCopyBytes int64 JSONPatchMaxCopyBytes int64
// The limit on the request body size that would be accepted and decoded in a write request. // The limit on the request size that would be accepted and decoded in a write request
// 0 means no limit. // 0 means no limit.
MaxRequestBodyBytes int64 MaxRequestBodyBytes int64
// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further // MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
@ -295,22 +295,20 @@ func NewConfig(codecs serializer.CodecFactory) *Config {
MinRequestTimeout: 1800, MinRequestTimeout: 1800,
LivezGracePeriod: time.Duration(0), LivezGracePeriod: time.Duration(0),
ShutdownDelayDuration: time.Duration(0), ShutdownDelayDuration: time.Duration(0),
// 10MB is the recommended maximum client request size in bytes // 1.5MB is the default client request size in bytes
// the etcd server should accept. See // the etcd server should accept. See
// https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/server.go#L90. // https://github.com/etcd-io/etcd/blob/release-3.4/embed/config.go#L56.
// A request body might be encoded in json, and is converted to // A request body might be encoded in json, and is converted to
// proto when persisted in etcd. Assuming the upper bound of // proto when persisted in etcd, so we allow 2x as the largest size
// the size ratio is 10:1, we set 100MB as the largest size
// increase the "copy" operations in a json patch may cause. // increase the "copy" operations in a json patch may cause.
JSONPatchMaxCopyBytes: int64(100 * 1024 * 1024), JSONPatchMaxCopyBytes: int64(3 * 1024 * 1024),
// 10MB is the recommended maximum client request size in bytes // 1.5MB is the recommended client request size in byte
// the etcd server should accept. See // the etcd server should accept. See
// https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/server.go#L90. // https://github.com/etcd-io/etcd/blob/release-3.4/embed/config.go#L56.
// A request body might be encoded in json, and is converted to // A request body might be encoded in json, and is converted to
// proto when persisted in etcd. Assuming the upper bound of // proto when persisted in etcd, so we allow 2x as the largest request
// the size ratio is 10:1, we set 100MB as the largest request
// body size to be accepted and decoded in a write request. // body size to be accepted and decoded in a write request.
MaxRequestBodyBytes: int64(100 * 1024 * 1024), MaxRequestBodyBytes: int64(3 * 1024 * 1024),
// Default to treating watch as a long-running operation // Default to treating watch as a long-running operation
// Generic API servers have no inherent long-running subresources // Generic API servers have no inherent long-running subresources