From a169f8b2220d7584de42e831c7cf6800f41dae02 Mon Sep 17 00:00:00 2001 From: Tim Ramlot <42113979+inteon@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:10:20 +0100 Subject: [PATCH] add missing limitedReader Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com> --- pkg/webhook/admission/http.go | 12 +++++++++++- pkg/webhook/admission/http_test.go | 14 ++++++++++++++ pkg/webhook/authentication/http.go | 12 +++++++++++- pkg/webhook/authentication/http_test.go | 14 ++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/pkg/webhook/admission/http.go b/pkg/webhook/admission/http.go index 4735800195..ea7b09992c 100644 --- a/pkg/webhook/admission/http.go +++ b/pkg/webhook/admission/http.go @@ -34,6 +34,9 @@ import ( var admissionScheme = runtime.NewScheme() var admissionCodecs = serializer.NewCodecFactory(admissionScheme) +// based on https://github.com/kubernetes/kubernetes/blob/c28c2009181fcc44c5f6b47e10e62dacf53e4da0/staging/src/k8s.io/pod-security-admission/cmd/webhook/server/server.go +var maxRequestSize = int64(3 * 1024 * 1024) + func init() { utilruntime.Must(v1.AddToScheme(admissionScheme)) utilruntime.Must(v1beta1.AddToScheme(admissionScheme)) @@ -55,12 +58,19 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) { } defer r.Body.Close() - body, err := io.ReadAll(r.Body) + limitedReader := &io.LimitedReader{R: r.Body, N: maxRequestSize} + body, err := io.ReadAll(limitedReader) if err != nil { wh.getLogger(nil).Error(err, "unable to read the body from the incoming request") wh.writeResponse(w, Errored(http.StatusBadRequest, err)) return } + if limitedReader.N <= 0 { + err := fmt.Errorf("request entity is too large; limit is %d bytes", maxRequestSize) + wh.getLogger(nil).Error(err, "unable to read the body from the incoming request; limit reached") + wh.writeResponse(w, Errored(http.StatusBadRequest, err)) + return + } // verify the content type is accurate if contentType := r.Header.Get("Content-Type"); contentType != "application/json" { diff --git a/pkg/webhook/admission/http_test.go b/pkg/webhook/admission/http_test.go index be10aea459..f496d3e71d 100644 --- a/pkg/webhook/admission/http_test.go +++ b/pkg/webhook/admission/http_test.go @@ -19,6 +19,7 @@ package admission import ( "bytes" "context" + "crypto/rand" "fmt" "io" "net/http" @@ -84,6 +85,19 @@ var _ = Describe("Admission Webhooks", func() { Expect(respRecorder.Body.String()).To(Equal(expected)) }) + It("should error when given an infinite body", func() { + req := &http.Request{ + Header: http.Header{"Content-Type": []string{"application/json"}}, + Method: http.MethodPost, + Body: nopCloser{Reader: rand.Reader}, + } + + expected := `{"response":{"uid":"","allowed":false,"status":{"metadata":{},"message":"request entity is too large; limit is 3145728 bytes","code":400}}} +` + webhook.ServeHTTP(respRecorder, req) + Expect(respRecorder.Body.String()).To(Equal(expected)) + }) + It("should return the response given by the handler with version defaulted to v1", func() { req := &http.Request{ Header: http.Header{"Content-Type": []string{"application/json"}}, diff --git a/pkg/webhook/authentication/http.go b/pkg/webhook/authentication/http.go index 3b0d5c4959..a657ef89db 100644 --- a/pkg/webhook/authentication/http.go +++ b/pkg/webhook/authentication/http.go @@ -34,6 +34,9 @@ import ( var authenticationScheme = runtime.NewScheme() var authenticationCodecs = serializer.NewCodecFactory(authenticationScheme) +// based on https://github.com/kubernetes/kubernetes/blob/c28c2009181fcc44c5f6b47e10e62dacf53e4da0/staging/src/k8s.io/pod-security-admission/cmd/webhook/server/server.go +var maxRequestSize = int64(3 * 1024 * 1024) + func init() { utilruntime.Must(authenticationv1.AddToScheme(authenticationScheme)) utilruntime.Must(authenticationv1beta1.AddToScheme(authenticationScheme)) @@ -55,12 +58,19 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) { } defer r.Body.Close() - body, err := io.ReadAll(r.Body) + limitedReader := &io.LimitedReader{R: r.Body, N: maxRequestSize} + body, err := io.ReadAll(limitedReader) if err != nil { wh.getLogger(nil).Error(err, "unable to read the body from the incoming request") wh.writeResponse(w, Errored(err)) return } + if limitedReader.N <= 0 { + err := fmt.Errorf("request entity is too large; limit is %d bytes", maxRequestSize) + wh.getLogger(nil).Error(err, "unable to read the body from the incoming request; limit reached") + wh.writeResponse(w, Errored(err)) + return + } // verify the content type is accurate if contentType := r.Header.Get("Content-Type"); contentType != "application/json" { diff --git a/pkg/webhook/authentication/http_test.go b/pkg/webhook/authentication/http_test.go index 86bd5d0153..d8679f99f0 100644 --- a/pkg/webhook/authentication/http_test.go +++ b/pkg/webhook/authentication/http_test.go @@ -19,6 +19,7 @@ package authentication import ( "bytes" "context" + "crypto/rand" "fmt" "io" "net/http" @@ -94,6 +95,19 @@ var _ = Describe("Authentication Webhooks", func() { Expect(respRecorder.Body.String()).To(Equal(expected)) }) + It("should error when given an infinite body", func() { + req := &http.Request{ + Header: http.Header{"Content-Type": []string{"application/json"}}, + Method: http.MethodPost, + Body: nopCloser{Reader: rand.Reader}, + } + + expected := `{"metadata":{"creationTimestamp":null},"spec":{},"status":{"user":{},"error":"request entity is too large; limit is 3145728 bytes"}} +` + webhook.ServeHTTP(respRecorder, req) + Expect(respRecorder.Body.String()).To(Equal(expected)) + }) + It("should return the response given by the handler with version defaulted to v1", func() { req := &http.Request{ Header: http.Header{"Content-Type": []string{"application/json"}},