Skip to content

Commit 66e4984

Browse files
committed
Move OAuth server to standard handler chain
This change allows us to have delegated authentication and authorization back to the kube API server for all non-OAuth endpoints. For example, this will make our metrics endpoint protected with auth. Bug 1703506 Bug 1704822 Signed-off-by: Monis Khan <mkhan@redhat.com>
1 parent 74fb649 commit 66e4984

4 files changed

Lines changed: 75 additions & 18 deletions

File tree

pkg/cmd/openshift-integrated-oauth-server/server.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package openshift_integrated_oauth_server
33
import (
44
"errors"
55

6+
"k8s.io/apiserver/pkg/authentication/user"
67
genericapiserver "k8s.io/apiserver/pkg/server"
8+
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
79
"k8s.io/kubernetes/pkg/api/legacyscheme"
810

911
osinv1 "github.com/openshift/api/osin/v1"
@@ -48,20 +50,41 @@ func newOAuthServerConfig(osinConfig *osinv1.OsinServerConfig) (*oauthserver.OAu
4850
// the oauth-server must only run in http1 to avoid http2 connection re-use problems when improperly re-using a wildcard certificate
4951
genericConfig.Config.SecureServing.HTTP1Only = true
5052

53+
authenticationOptions := genericapiserveroptions.NewDelegatingAuthenticationOptions()
54+
authenticationOptions.ClientCert.ClientCA = osinConfig.ServingInfo.ClientCA
55+
authenticationOptions.RemoteKubeConfigFile = osinConfig.KubeClientConfig.KubeConfig
56+
if err := authenticationOptions.ApplyTo(&genericConfig.Authentication, genericConfig.SecureServing, genericConfig.OpenAPIConfig); err != nil {
57+
return nil, err
58+
}
59+
60+
authorizationOptions := genericapiserveroptions.NewDelegatingAuthorizationOptions().
61+
// TODO better formalize / generate this list as trailing * matters
62+
WithAlwaysAllowPaths( // The five sections are:
63+
"/healthz", "/healthz/", // 1. Health checks (root, no wildcard)
64+
"/oauth/*", // 2. OAuth (wildcard)
65+
"/login", "/login/*", // 3. Login (both root and wildcard)
66+
"/logout", "/logout/", // 4. Logout (root, no wildcard)
67+
"/oauth2callback/*", // 5. OAuth callbacks (wildcard)
68+
).
69+
WithAlwaysAllowGroups(user.SystemPrivilegedGroup)
70+
authorizationOptions.RemoteKubeConfigFile = osinConfig.KubeClientConfig.KubeConfig
71+
if err := authorizationOptions.ApplyTo(&genericConfig.Authorization); err != nil {
72+
return nil, err
73+
}
74+
5175
// TODO You need real overrides for rate limiting
5276
kubeClientConfig, err := helpers.GetKubeConfigOrInClusterConfig(osinConfig.KubeClientConfig.KubeConfig, osinConfig.KubeClientConfig.ConnectionOverrides)
5377
if err != nil {
5478
return nil, err
5579
}
5680

57-
oauthServerConfig, err := oauthserver.NewOAuthServerConfig(osinConfig.OAuthConfig, kubeClientConfig)
81+
oauthServerConfig, err := oauthserver.NewOAuthServerConfig(osinConfig.OAuthConfig, kubeClientConfig, genericConfig)
5882
if err != nil {
5983
return nil, err
6084
}
6185

6286
// TODO you probably want to set this
6387
oauthServerConfig.GenericConfig.CorsAllowedOriginList = osinConfig.CORSAllowedOrigins
64-
oauthServerConfig.GenericConfig.SecureServing = genericConfig.SecureServing
6588
//oauthServerConfig.GenericConfig.AuditBackend = genericConfig.AuditBackend
6689
//oauthServerConfig.GenericConfig.AuditPolicyChecker = genericConfig.AuditPolicyChecker
6790

pkg/cmd/openshift-kube-apiserver/openshiftkubeapiserver/patch_oauthserver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
// TODO this is taking a very large config for a small piece of it. The information must be broken up at some point so that
1212
// we can run this in a pod. This is an indication of leaky abstraction because it spent too much time in openshift start
1313
func NewOAuthServerConfigFromMasterConfig(genericConfig *genericapiserver.Config, oauthConfig *osinv1.OAuthConfig) (*oauthserver.OAuthServerConfig, error) {
14-
oauthServerConfig, err := oauthserver.NewOAuthServerConfig(*oauthConfig, genericConfig.LoopbackClientConfig)
14+
oauthServerConfig, err := oauthserver.NewOAuthServerConfig(*oauthConfig, genericConfig.LoopbackClientConfig, nil)
1515
if err != nil {
1616
return nil, err
1717
}

pkg/oauthserver/oauthserver/oauth_apiserver.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ import (
1212
"k8s.io/apimachinery/pkg/runtime/serializer"
1313
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1414
"k8s.io/apimachinery/pkg/util/wait"
15-
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
16-
"k8s.io/apiserver/pkg/features"
1715
genericapiserver "k8s.io/apiserver/pkg/server"
18-
genericfilters "k8s.io/apiserver/pkg/server/filters"
19-
utilfeature "k8s.io/apiserver/pkg/util/feature"
2016
kclientset "k8s.io/client-go/kubernetes"
2117
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
2218
"k8s.io/client-go/rest"
@@ -47,7 +43,7 @@ func init() {
4743

4844
// TODO we need to switch the oauth server to an external type, but that can be done after we get our externally facing flag values fixed
4945
// TODO remaining bits involve the session file, LDAP util code, validation, ...
50-
func NewOAuthServerConfig(oauthConfig osinv1.OAuthConfig, userClientConfig *rest.Config) (*OAuthServerConfig, error) {
46+
func NewOAuthServerConfig(oauthConfig osinv1.OAuthConfig, userClientConfig *rest.Config, genericConfig *genericapiserver.RecommendedConfig) (*OAuthServerConfig, error) {
5147
// TODO: there is probably some better way to do this
5248
decoder := codecs.UniversalDecoder(osinv1.GroupVersion)
5349
for i, idp := range oauthConfig.IdentityProviders {
@@ -62,7 +58,11 @@ func NewOAuthServerConfig(oauthConfig osinv1.OAuthConfig, userClientConfig *rest
6258
oauthConfig.IdentityProviders[i].Provider.Object = idpObject
6359
}
6460

65-
genericConfig := genericapiserver.NewRecommendedConfig(codecs)
61+
// this leaves the embedded OAuth server code path alone
62+
if genericConfig == nil {
63+
genericConfig = genericapiserver.NewRecommendedConfig(codecs)
64+
}
65+
6666
genericConfig.LoopbackClientConfig = userClientConfig
6767

6868
userClient, err := userclient.NewForConfig(userClientConfig)
@@ -277,21 +277,26 @@ func (c completedOAuthConfig) New(delegationTarget genericapiserver.DelegationTa
277277
}
278278

279279
func (c *OAuthServerConfig) buildHandlerChainForOAuth(startingHandler http.Handler, genericConfig *genericapiserver.Config) http.Handler {
280+
// add OAuth handlers on top of the generic API server handlers
280281
handler, err := c.WithOAuth(startingHandler)
281282
if err != nil {
282-
// the existing errors all cause the server to die anyway
283+
// the existing errors all cause the OAuth server to die anyway
283284
panic(err)
284285
}
285-
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
286-
handler = genericapifilters.WithAudit(handler, genericConfig.AuditBackend, genericConfig.AuditPolicyChecker, genericConfig.LongRunningFunc)
287-
}
288286

289-
handler = genericfilters.WithMaxInFlightLimit(handler, genericConfig.MaxRequestsInFlight, genericConfig.MaxMutatingRequestsInFlight, genericConfig.LongRunningFunc)
290-
handler = genericfilters.WithCORS(handler, genericConfig.CorsAllowedOriginList, nil, nil, nil, "true")
291-
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, genericConfig.LongRunningFunc, genericConfig.RequestTimeout)
292-
handler = genericapifilters.WithRequestInfo(handler, genericapiserver.NewRequestInfoResolver(genericConfig))
287+
// add back the Authorization header so that WithOAuth can use it even after WithAuthentication deletes it
288+
// WithOAuth sees users' passwords and can mint tokens so this is not really an issue
289+
handler = headers.WithRestoreAuthorizationHeader(handler)
290+
291+
// this is the normal kube handler chain
292+
handler = genericapiserver.DefaultBuildHandlerChain(handler, genericConfig)
293+
294+
// store a copy of the Authorization header for later use
295+
handler = headers.WithPreserveAuthorizationHeader(handler)
296+
297+
// protected endpoints should not be cached
293298
handler = headers.WithStandardHeaders(handler)
294-
handler = genericfilters.WithPanicRecovery(handler)
299+
295300
return handler
296301
}
297302

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package headers
2+
3+
import "net/http"
4+
5+
const (
6+
authzHeader = "Authorization"
7+
copyAuthzHeader = "oauth.openshift.io:" + authzHeader // will never conflict because : is not a valid header key
8+
)
9+
10+
func WithPreserveAuthorizationHeader(handler http.Handler) http.Handler {
11+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12+
if vv, ok := r.Header[authzHeader]; ok {
13+
r.Header[copyAuthzHeader] = vv // capture the values before they are deleted
14+
}
15+
16+
handler.ServeHTTP(w, r)
17+
})
18+
}
19+
20+
func WithRestoreAuthorizationHeader(handler http.Handler) http.Handler {
21+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22+
if vv, ok := r.Header[copyAuthzHeader]; ok {
23+
r.Header[authzHeader] = vv // add them back afterwards for use in OAuth flows
24+
delete(r.Header, copyAuthzHeader)
25+
}
26+
27+
handler.ServeHTTP(w, r)
28+
})
29+
}

0 commit comments

Comments
 (0)