// handlerChain is a method to build the handler chain for this API server. We need a custom handler chain so that we // can have custom handling for `/apis`, since we're hosting discovery differently from anyone else and we're hosting // the endpoints differently, since we're proxying all groups except for apiregistration.k8s.io. func (h *handlerChainConfig) handlerChain(apiHandler http.Handler, c *genericapiserver.Config) (secure, insecure http.Handler) { // add this as a filter so that we never collide with "already registered" failures on `/apis` handler := WithAPIs(apiHandler, h.informers.Apiregistration().InternalVersion().APIServices()) handler = apiserverfilters.WithAuthorization(handler, c.RequestContextMapper, c.Authorizer) // this mux is NOT protected by authorization, but DOES have authentication information // this is so that everyone can hit the proxy and we can properly identify the user. The backing // API server will deal with authorization handler = WithProxyMux(handler, h.proxyMux) handler = apiserverfilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer) // audit to stdout to help with debugging as we get this started handler = apiserverfilters.WithAudit(handler, c.RequestContextMapper, os.Stdout) handler = authhandlers.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, authhandlers.Unauthorized(c.SupportsBasicAuth)) handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true") handler = genericfilters.WithPanicRecovery(handler, c.RequestContextMapper) handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc) handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc) handler = apiserverfilters.WithRequestInfo(handler, genericapiserver.NewRequestInfoResolver(c), c.RequestContextMapper) handler = api.WithRequestContext(handler, c.RequestContextMapper) return handler, nil }
func (c *MasterConfig) buildHandlerChain(assetConfig *AssetConfig) (func(http.Handler, *genericapiserver.Config) (secure, insecure http.Handler), []string, error) { var messages []string if c.Options.OAuthConfig != nil { messages = append(messages, fmt.Sprintf("Started OAuth2 API at %%s%s", OpenShiftOAuthAPIPrefix)) } if assetConfig != nil { publicURL, err := url.Parse(assetConfig.Options.PublicURL) if err != nil { return nil, nil, err } messages = append(messages, fmt.Sprintf("Started Web Console %%s%s", publicURL.Path)) } // TODO(sttts): resync with upstream handler chain and re-use upstream filters as much as possible return func(apiHandler http.Handler, kc *genericapiserver.Config) (secure, insecure http.Handler) { attributeGetter := kapiserverfilters.NewRequestAttributeGetter(c.RequestContextMapper) handler := c.versionSkewFilter(apiHandler, c.getRequestContextMapper()) handler = c.authorizationFilter(handler) handler = c.impersonationFilter(handler) // audit handler must comes before the impersonationFilter to read the original user if c.Options.AuditConfig.Enabled { var writer io.Writer if len(c.Options.AuditConfig.AuditFilePath) > 0 { writer = &lumberjack.Logger{ Filename: c.Options.AuditConfig.AuditFilePath, MaxAge: c.Options.AuditConfig.MaximumFileRetentionDays, MaxBackups: c.Options.AuditConfig.MaximumRetainedFiles, MaxSize: c.Options.AuditConfig.MaximumFileSizeMegabytes, } } else { // backwards compatible writer to regular log writer = cmdutil.NewGLogWriterV(0) } handler = kapiserverfilters.WithAudit(handler, attributeGetter, writer) } handler = authenticationHandlerFilter(handler, c.Authenticator, c.getRequestContextMapper()) handler = namespacingFilter(handler, c.getRequestContextMapper()) handler = cacheControlFilter(handler, "no-store") // protected endpoints should not be cached if c.Options.OAuthConfig != nil { authConfig, err := BuildAuthConfig(c) if err != nil { glog.Fatalf("Failed to setup OAuth2: %v", err) } handler, err = authConfig.WithOAuth(handler) if err != nil { glog.Fatalf("Failed to setup OAuth2: %v", err) } } handler, err := assetConfig.WithAssets(handler) if err != nil { glog.Fatalf("Failed to setup serving of assets: %v", err) } // skip authz/n for the index handler handler = WithPatternsHandler(handler, apiHandler, "/", "") if c.WebConsoleEnabled() { handler = WithAssetServerRedirect(handler, c.Options.AssetConfig.PublicURL) } handler = kgenericfilters.WithCORS(handler, c.Options.CORSAllowedOrigins, nil, nil, nil, "true") handler = kgenericfilters.WithPanicRecovery(handler, c.RequestContextMapper) handler = kgenericfilters.WithTimeoutForNonLongRunningRequests(handler, kc.LongRunningFunc) // TODO: MaxRequestsInFlight should be subdivided by intent, type of behavior, and speed of // execution - updates vs reads, long reads vs short reads, fat reads vs skinny reads. // NOTE: read vs. write is implemented in Kube 1.6+ handler = kgenericfilters.WithMaxInFlightLimit(handler, kc.MaxRequestsInFlight, kc.LongRunningFunc) handler = kapiserverfilters.WithRequestInfo(handler, genericapiserver.NewRequestInfoResolver(kc), kc.RequestContextMapper) handler = kapi.WithRequestContext(handler, kc.RequestContextMapper) return handler, nil }, messages, nil }