Skip to content

Commit

Permalink
add missing crashhandler, NoBody handling, limitedReader + code cleanup
Browse files Browse the repository at this point in the history
Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com>
  • Loading branch information
inteon committed Nov 28, 2023
1 parent 2154ffb commit 8f30d7c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 36 deletions.
40 changes: 24 additions & 16 deletions pkg/webhook/admission/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -42,36 +45,43 @@ func init() {
var _ http.Handler = &Webhook{}

func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var body []byte
var err error
defer utilruntime.HandleCrash(func(_ interface{}) {
// Assume the crash happened before the response was written.
http.Error(w, "internal server error", http.StatusInternalServerError)
})

ctx := r.Context()
if wh.WithContextFunc != nil {
ctx = wh.WithContextFunc(ctx, r)
}

var reviewResponse Response
if r.Body == nil {
err = errors.New("request body is empty")
if r.Body == nil || r.Body == http.NoBody {
err := errors.New("request body is empty")
wh.getLogger(nil).Error(err, "bad request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(http.StatusBadRequest, err))
return
}

defer r.Body.Close()
if body, err = io.ReadAll(r.Body); err != nil {
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")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
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" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
wh.getLogger(nil).Error(err, "unable to process a request with unknown content type")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(http.StatusBadRequest, err))
return
}

Expand All @@ -89,14 +99,12 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
_, actualAdmRevGVK, err := admissionCodecs.UniversalDeserializer().Decode(body, nil, &ar)
if err != nil {
wh.getLogger(nil).Error(err, "unable to decode the request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(http.StatusBadRequest, err))
return
}
wh.getLogger(&req).V(5).Info("received request")

reviewResponse = wh.Handle(ctx, req)
wh.writeResponseTyped(w, reviewResponse, actualAdmRevGVK)
wh.writeResponseTyped(w, wh.Handle(ctx, req), actualAdmRevGVK)
}

// writeResponse writes response to w generically, i.e. without encoding GVK information.
Expand Down
47 changes: 27 additions & 20 deletions pkg/webhook/authentication/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -42,36 +45,43 @@ func init() {
var _ http.Handler = &Webhook{}

func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var body []byte
var err error
defer utilruntime.HandleCrash(func(_ interface{}) {
// Assume the crash happened before the response was written.
http.Error(w, "internal server error", http.StatusInternalServerError)
})

ctx := r.Context()
if wh.WithContextFunc != nil {
ctx = wh.WithContextFunc(ctx, r)
}

var reviewResponse Response
if r.Body == nil {
err = errors.New("request body is empty")
if r.Body == nil || r.Body == http.NoBody {
err := errors.New("request body is empty")
wh.getLogger(nil).Error(err, "bad request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(err))
return
}

defer r.Body.Close()
if body, err = io.ReadAll(r.Body); err != nil {
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")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
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" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
err := fmt.Errorf("contentType=%s, expected application/json", contentType)
wh.getLogger(nil).Error(err, "unable to process a request with unknown content type")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(err))
return
}

Expand All @@ -90,22 +100,19 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
_, actualTokRevGVK, err := authenticationCodecs.UniversalDeserializer().Decode(body, nil, &ar)
if err != nil {
wh.getLogger(nil).Error(err, "unable to decode the request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(err))
return
}
wh.getLogger(&req).V(5).Info("received request")

if req.Spec.Token == "" {
err = errors.New("token is empty")
err := errors.New("token is empty")
wh.getLogger(&req).Error(err, "bad request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
wh.writeResponse(w, Errored(err))
return
}

reviewResponse = wh.Handle(ctx, req)
wh.writeResponseTyped(w, reviewResponse, actualTokRevGVK)
wh.writeResponseTyped(w, wh.Handle(ctx, req), actualTokRevGVK)
}

// writeResponse writes response to w generically, i.e. without encoding GVK information.
Expand Down

0 comments on commit 8f30d7c

Please sign in to comment.