-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathhttp.go
152 lines (129 loc) · 5.53 KB
/
http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package authentication
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var authenticationScheme = runtime.NewScheme()
var authenticationCodecs = serializer.NewCodecFactory(authenticationScheme)
// The TokenReview resource mostly contains a bearer token which
// at most should have a few KB's of size, so we picked 1 MB to
// have plenty of buffer.
// If your use case requires larger max request sizes, please
// open an issue (https://github.com/kubernetes-sigs/controller-runtime/issues/new).
const maxRequestSize = int64(1 * 1024 * 1024)
func init() {
utilruntime.Must(authenticationv1.AddToScheme(authenticationScheme))
utilruntime.Must(authenticationv1beta1.AddToScheme(authenticationScheme))
}
var _ http.Handler = &Webhook{}
func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if wh.WithContextFunc != nil {
ctx = wh.WithContextFunc(ctx, r)
}
if r.Body == nil || r.Body == http.NoBody {
err := errors.New("request body is empty")
wh.getLogger(nil).Error(err, "bad request")
wh.writeResponse(w, Errored(err))
return
}
defer r.Body.Close()
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" {
err := fmt.Errorf("contentType=%s, expected application/json", contentType)
wh.getLogger(nil).Error(err, "unable to process a request with unknown content type")
wh.writeResponse(w, Errored(err))
return
}
// Both v1 and v1beta1 TokenReview types are exactly the same, so the v1beta1 type can
// be decoded into the v1 type. The v1beta1 api is deprecated as of 1.19 and will be
// removed in authenticationv1.22. However the runtime codec's decoder guesses which type to
// decode into by type name if an Object's TypeMeta isn't set. By setting TypeMeta of an
// unregistered type to the v1 GVK, the decoder will coerce a v1beta1 TokenReview to authenticationv1.
// The actual TokenReview GVK will be used to write a typed response in case the
// webhook config permits multiple versions, otherwise this response will fail.
req := Request{}
ar := unversionedTokenReview{}
// avoid an extra copy
ar.TokenReview = &req.TokenReview
ar.SetGroupVersionKind(authenticationv1.SchemeGroupVersion.WithKind("TokenReview"))
_, actualTokRevGVK, err := authenticationCodecs.UniversalDeserializer().Decode(body, nil, &ar)
if err != nil {
wh.getLogger(nil).Error(err, "unable to decode the request")
wh.writeResponse(w, Errored(err))
return
}
wh.getLogger(&req).V(5).Info("received request")
if req.Spec.Token == "" {
err := errors.New("token is empty")
wh.getLogger(&req).Error(err, "bad request")
wh.writeResponse(w, Errored(err))
return
}
wh.writeResponseTyped(w, wh.Handle(ctx, req), actualTokRevGVK)
}
// writeResponse writes response to w generically, i.e. without encoding GVK information.
func (wh *Webhook) writeResponse(w io.Writer, response Response) {
wh.writeTokenResponse(w, response.TokenReview)
}
// writeResponseTyped writes response to w with GVK set to tokRevGVK, which is necessary
// if multiple TokenReview versions are permitted by the webhook.
func (wh *Webhook) writeResponseTyped(w io.Writer, response Response, tokRevGVK *schema.GroupVersionKind) {
ar := response.TokenReview
// Default to a v1 TokenReview, otherwise the API server may not recognize the request
// if multiple TokenReview versions are permitted by the webhook config.
if tokRevGVK == nil || *tokRevGVK == (schema.GroupVersionKind{}) {
ar.SetGroupVersionKind(authenticationv1.SchemeGroupVersion.WithKind("TokenReview"))
} else {
ar.SetGroupVersionKind(*tokRevGVK)
}
wh.writeTokenResponse(w, ar)
}
// writeTokenResponse writes ar to w.
func (wh *Webhook) writeTokenResponse(w io.Writer, ar authenticationv1.TokenReview) {
if err := json.NewEncoder(w).Encode(ar); err != nil {
wh.getLogger(nil).Error(err, "unable to encode the response")
wh.writeResponse(w, Errored(err))
}
res := ar
wh.getLogger(nil).V(5).Info("wrote response", "requestID", res.UID, "authenticated", res.Status.Authenticated)
}
// unversionedTokenReview is used to decode both v1 and v1beta1 TokenReview types.
type unversionedTokenReview struct {
*authenticationv1.TokenReview
}
var _ runtime.Object = &unversionedTokenReview{}