Skip to content

Commit 7f854d9

Browse files
Merge pull request #16741 from enj/enj/i/unsafeproxy_reqinfo/16619
Automatic merge from submit-queue (batch tested with PRs 16741, 16692). Move browser safe proxy logic into an authorizer This change moves the browser safe proxy verification from a request info resolver to an authorizer. It is required because we moved to the upstream filters in 689cdee. The upstream handler chain resolves request info, then authentication and then authorization. Our old chain would mutate request info after authentication and before authorization. This meant that we had access to the user info data. Since the upstream handler chain does any mutation to the request info before authentication, our browser safe proxy request info resolver would never see any user data. This meant that it would incorrectly identify safe proxy requests as unsafe. To continue using the upstream chain, we must move this logic into an authorizer that wraps our existing authorizers. This will guarantee that it has access to the authentication information it requires. Since the authorizer only has access to authorization attributes, it cannot make any decisions based on the context or request info. In this case, it means that we can no longer honor the X-CSRF-Token header as a way of confirming that the request is not originating from a browser. This is an acceptable break from legacy behavior as that header was never documented as a way of passing the browser check. The most likely user of this feature is the proxy command, which is easy to use in an authenticated manner with either oc or kubectl. Administrators can continue to grant the unsafe verb to unauthenticated users should they desire to bypass this safety check entirely. Signed-off-by: Monis Khan <[email protected]> Fixes #16619 Fixes #16710 /assign @liggitt @deads2k @openshift/sig-security cc @jwendell @DirectXMan12 since you guys encountered this issue.
2 parents 7350dfb + 40b6cd6 commit 7f854d9

File tree

10 files changed

+240
-256
lines changed

10 files changed

+240
-256
lines changed

pkg/authorization/authorizer/authorizer.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,20 @@ import (
66
"k8s.io/apimachinery/pkg/util/sets"
77
"k8s.io/apiserver/pkg/authentication/serviceaccount"
88
"k8s.io/apiserver/pkg/authorization/authorizer"
9-
kauthorizer "k8s.io/apiserver/pkg/authorization/authorizer"
109
"k8s.io/kubernetes/pkg/apis/rbac"
1110
authorizerrbac "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
1211
)
1312

1413
type openshiftAuthorizer struct {
15-
delegate kauthorizer.Authorizer
14+
delegate authorizer.Authorizer
1615
forbiddenMessageMaker ForbiddenMessageMaker
1716
}
1817

1918
type openshiftSubjectLocator struct {
2019
delegate authorizerrbac.SubjectLocator
2120
}
2221

23-
func NewAuthorizer(delegate kauthorizer.Authorizer, forbiddenMessageMaker ForbiddenMessageMaker) authorizer.Authorizer {
22+
func NewAuthorizer(delegate authorizer.Authorizer, forbiddenMessageMaker ForbiddenMessageMaker) authorizer.Authorizer {
2423
return &openshiftAuthorizer{delegate: delegate, forbiddenMessageMaker: forbiddenMessageMaker}
2524
}
2625

pkg/authorization/authorizer/browser_safe_request_info_resolver.go

-74
This file was deleted.

pkg/authorization/authorizer/browser_safe_request_info_resolver_test.go

-146
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package browsersafe
2+
3+
import (
4+
"k8s.io/apimachinery/pkg/util/sets"
5+
"k8s.io/apiserver/pkg/authorization/authorizer"
6+
)
7+
8+
const (
9+
proxyAction = "proxy"
10+
unsafeProxy = "unsafeproxy"
11+
)
12+
13+
type browserSafeAuthorizer struct {
14+
delegate authorizer.Authorizer
15+
16+
// list of groups, any of which indicate the request is authenticated
17+
authenticatedGroups sets.String
18+
}
19+
20+
func NewBrowserSafeAuthorizer(delegate authorizer.Authorizer, authenticatedGroups ...string) authorizer.Authorizer {
21+
return &browserSafeAuthorizer{
22+
delegate: delegate,
23+
authenticatedGroups: sets.NewString(authenticatedGroups...),
24+
}
25+
}
26+
27+
func (a *browserSafeAuthorizer) Authorize(attributes authorizer.Attributes) (bool, string, error) {
28+
browserSafeAttributes := a.getBrowserSafeAttributes(attributes)
29+
return a.delegate.Authorize(browserSafeAttributes)
30+
}
31+
32+
func (a *browserSafeAuthorizer) getBrowserSafeAttributes(attributes authorizer.Attributes) authorizer.Attributes {
33+
if !attributes.IsResourceRequest() {
34+
return attributes
35+
}
36+
37+
isProxyVerb := attributes.GetVerb() == proxyAction
38+
isProxySubresource := attributes.GetSubresource() == proxyAction
39+
40+
if !isProxyVerb && !isProxySubresource {
41+
// Requests to non-proxy resources don't expose HTML or HTTP-handling user content to browsers
42+
return attributes
43+
}
44+
45+
if user := attributes.GetUser(); user != nil {
46+
if a.authenticatedGroups.HasAny(user.GetGroups()...) {
47+
// An authenticated request indicates this isn't a browser page load.
48+
// Browsers cannot make direct authenticated requests.
49+
// This depends on the API not enabling basic or cookie-based auth.
50+
return attributes
51+
}
52+
}
53+
54+
return &browserSafeAttributes{
55+
Attributes: attributes,
56+
isProxyVerb: isProxyVerb,
57+
isProxySubresource: isProxySubresource,
58+
}
59+
}
60+
61+
type browserSafeAttributes struct {
62+
authorizer.Attributes
63+
64+
isProxyVerb, isProxySubresource bool
65+
}
66+
67+
func (b *browserSafeAttributes) GetVerb() string {
68+
if b.isProxyVerb {
69+
return unsafeProxy
70+
}
71+
return b.Attributes.GetVerb()
72+
}
73+
74+
func (b *browserSafeAttributes) GetSubresource() string {
75+
if b.isProxySubresource {
76+
return unsafeProxy
77+
}
78+
return b.Attributes.GetSubresource()
79+
}

0 commit comments

Comments
 (0)