This repository was archived by the owner on May 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 99
/
Copy pathhandlers.go
226 lines (185 loc) · 6.54 KB
/
handlers.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Copyright Jetstack Ltd. See LICENSE for details.
package proxy
import (
"net/http"
"strings"
authuser "k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/transport"
"k8s.io/klog"
"github.com/jetstack/kube-oidc-proxy/pkg/proxy/audit"
"github.com/jetstack/kube-oidc-proxy/pkg/proxy/context"
)
func (p *Proxy) withHandlers(handler http.Handler) http.Handler {
// Set up proxy handlers
handler = p.auditor.WithRequest(handler)
handler = p.withImpersonateRequest(handler)
handler = p.withAuthenticateRequest(handler)
// Add the auditor backend as a shutdown hook
p.hooks.AddPreShutdownHook("AuditBackend", p.auditor.Shutdown)
return handler
}
// withAuthenticateRequest adds the proxy authentication handler to a chain.
func (p *Proxy) withAuthenticateRequest(handler http.Handler) http.Handler {
tokenReviewHandler := p.withTokenReview(handler)
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// Auth request and handle unauthed
info, ok, err := p.oidcRequestAuther.AuthenticateRequest(req)
if err != nil {
klog.V(6).Infof("OIDC authenticate request failed due to: %q", err)
// Since we have failed OIDC auth, we will try a token review, if enabled.
tokenReviewHandler.ServeHTTP(rw, req)
return
}
// Failed authorization
if !ok {
p.handleError(rw, req, errUnauthorized)
return
}
var remoteAddr string
req, remoteAddr = context.RemoteAddr(req)
klog.V(4).Infof("authenticated request: %s", remoteAddr)
// Add the user info to the request context
req = req.WithContext(genericapirequest.WithUser(req.Context(), info.User))
handler.ServeHTTP(rw, req)
})
}
// withTokenReview will attempt a token review on the incoming request, if
// enabled.
func (p *Proxy) withTokenReview(handler http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// If token review is not enabled then error.
if !p.config.TokenReview {
p.handleError(rw, req, errUnauthorized)
return
}
// Attempt to passthrough request if valid token
if !p.reviewToken(rw, req) {
// Token review failed so error
p.handleError(rw, req, errUnauthorized)
return
}
// Set no impersonation headers and re-add removed headers.
req = context.WithNoImpersonation(req)
handler.ServeHTTP(rw, req)
})
}
// withImpersonateRequest adds the impersonation request handler to the chain.
func (p *Proxy) withImpersonateRequest(handler http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// If no impersonation has already been set, return early
if context.NoImpersonation(req) {
handler.ServeHTTP(rw, req)
return
}
var remoteAddr string
req, remoteAddr = context.RemoteAddr(req)
// If we have disabled impersonation we can forward the request right away
if p.config.DisableImpersonation {
klog.V(2).Infof("passing on request with no impersonation: %s", remoteAddr)
// Indicate we need to not use impersonation.
req = context.WithNoImpersonation(req)
handler.ServeHTTP(rw, req)
return
}
if p.hasImpersonation(req.Header) {
p.handleError(rw, req, errImpersonateHeader)
return
}
user, ok := genericapirequest.UserFrom(req.Context())
// No name available so reject request
if !ok || len(user.GetName()) == 0 {
p.handleError(rw, req, errNoName)
return
}
// Ensure group contains allauthenticated builtin
allAuthFound := false
groups := user.GetGroups()
for _, elem := range groups {
if elem == authuser.AllAuthenticated {
allAuthFound = true
break
}
}
if !allAuthFound {
groups = append(groups, authuser.AllAuthenticated)
}
extra := user.GetExtra()
if extra == nil {
extra = make(map[string][]string)
}
// If client IP user extra header option set then append the remote client
// address.
if p.config.ExtraUserHeadersClientIPEnabled {
klog.V(6).Infof("adding impersonate extra user header %s: %s (%s)",
UserHeaderClientIPKey, remoteAddr, remoteAddr)
extra[UserHeaderClientIPKey] = append(extra[UserHeaderClientIPKey], remoteAddr)
}
// Add custom extra user headers to impersonation request.
for k, vs := range p.config.ExtraUserHeaders {
for _, v := range vs {
klog.V(6).Infof("adding impersonate extra user header %s: %s (%s)",
k, v, remoteAddr)
extra[k] = append(extra[k], v)
}
}
conf := &transport.ImpersonationConfig{
UserName: user.GetName(),
Groups: groups,
Extra: extra,
}
// Add the impersonation configuration to the context.
req = context.WithImpersonationConfig(req, conf)
handler.ServeHTTP(rw, req)
})
}
// newErrorHandler returns a handler failed requests.
func (p *Proxy) newErrorHandler() func(rw http.ResponseWriter, r *http.Request, err error) {
unauthedHandler := audit.NewUnauthenticatedHandler(p.auditor, func(rw http.ResponseWriter, r *http.Request) {
klog.V(2).Infof("unauthenticated user request %s", r.RemoteAddr)
http.Error(rw, "Unauthorized", http.StatusUnauthorized)
})
return func(rw http.ResponseWriter, r *http.Request, err error) {
if err == nil {
klog.Error("error was called with no error")
http.Error(rw, "", http.StatusInternalServerError)
return
}
switch err {
// Failed auth
case errUnauthorized:
// If Unauthorized then error and report to audit
unauthedHandler.ServeHTTP(rw, r)
return
// User request with impersonation
case errImpersonateHeader:
klog.V(2).Infof("impersonation user request %s", r.RemoteAddr)
http.Error(rw, "Impersonation requests are disabled when using kube-oidc-proxy", http.StatusForbidden)
return
// No name given or available in oidc request
case errNoName:
klog.V(2).Infof("no name available in oidc info %s", r.RemoteAddr)
http.Error(rw, "Username claim not available in OIDC Issuer response", http.StatusForbidden)
return
// No impersonation configuration found in context
case errNoImpersonationConfig:
klog.Errorf("if you are seeing this, there is likely a bug in the proxy (%s): %s", r.RemoteAddr, err)
http.Error(rw, "", http.StatusInternalServerError)
return
// Server or unknown error
default:
klog.Errorf("unknown error (%s): %s", r.RemoteAddr, err)
http.Error(rw, "", http.StatusInternalServerError)
}
}
}
func (p *Proxy) hasImpersonation(header http.Header) bool {
for h := range header {
if strings.ToLower(h) == impersonateUserHeader ||
strings.ToLower(h) == impersonateGroupHeader ||
strings.HasPrefix(strings.ToLower(h), impersonateExtraHeader) {
return true
}
}
return false
}