func MakeTransport(config *KubeletConfig) (http.RoundTripper, error) { cfg := &Config{TLSClientConfig: config.TLSClientConfig} if config.EnableHttps { hasCA := len(config.CAFile) > 0 || len(config.CAData) > 0 if !hasCA { cfg.Insecure = true } } tlsConfig, err := TLSConfigFor(cfg) if err != nil { return nil, err } transport := http.DefaultTransport if config.Dial != nil || tlsConfig != nil { transport = util.SetTransportDefaults(&http.Transport{ Dial: config.Dial, TLSClientConfig: tlsConfig, }) } if len(config.BearerToken) > 0 { transport = NewBearerAuthRoundTripper(config.BearerToken, transport) } return transport, nil }
// tlsTransportFor returns a http.RoundTripper for the given config, or an error // The same RoundTripper will be returned for configs with identical TLS options // If the config has no custom TLS options, http.DefaultTransport is returned func tlsTransportFor(config *Config) (http.RoundTripper, error) { // Get a unique key for the TLS options in the config key, err := tlsConfigKey(config) if err != nil { return nil, err } // Ensure we only create a single transport for the given TLS options tlsTransportLock.Lock() defer tlsTransportLock.Unlock() // See if we already have a custom transport for this config if cachedTransport, ok := tlsTransports[key]; ok { return cachedTransport, nil } // Get the TLS options for this client config tlsConfig, err := TLSConfigFor(config) if err != nil { return nil, err } // The options didn't require a custom TLS config if tlsConfig == nil { return http.DefaultTransport, nil } // Cache a single transport for these options tlsTransports[key] = util.SetTransportDefaults(&http.Transport{ TLSClientConfig: tlsConfig, }) return tlsTransports[key], nil }
// newConnection creates a new connection func newConnection(url url.URL, dialTimeout time.Duration, allowInsecure, enableV2 bool) *connection { var isV2 *bool if !enableV2 { v2 := false isV2 = &v2 } var transport http.RoundTripper if allowInsecure { transport = kutil.SetTransportDefaults(&http.Transport{ Dial: (&net.Dialer{ Timeout: dialTimeout, KeepAlive: 30 * time.Second, }).Dial, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }) } else { transport = kutil.SetTransportDefaults(&http.Transport{ Dial: (&net.Dialer{ Timeout: dialTimeout, KeepAlive: 30 * time.Second, }).Dial, }) } switch { case bool(glog.V(9)): transport = kclient.NewDebuggingRoundTripper(transport, kclient.CurlCommand, kclient.URLTiming, kclient.ResponseHeaders) case bool(glog.V(8)): transport = kclient.NewDebuggingRoundTripper(transport, kclient.JustURL, kclient.RequestHeaders, kclient.ResponseStatus, kclient.ResponseHeaders) case bool(glog.V(7)): transport = kclient.NewDebuggingRoundTripper(transport, kclient.JustURL, kclient.RequestHeaders, kclient.ResponseStatus) case bool(glog.V(6)): transport = kclient.NewDebuggingRoundTripper(transport, kclient.URLTiming) } jar, _ := cookiejar.New(nil) client := &http.Client{Jar: jar, Transport: transport} return &connection{ url: url, client: client, cached: make(map[string]repository), isV2: isV2, allowInsecure: allowInsecure, } }
func insecureTransport() *http.Transport { return util.SetTransportDefaults(&http.Transport{ TLSClientConfig: &tls.Config{ // Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability) MinVersion: tls.VersionTLS10, InsecureSkipVerify: true, }, }) }
func newMesosClient( md detector.Master, mesosHttpClientTimeout, stateCacheTTL time.Duration) (*mesosClient, error) { tr := util.SetTransportDefaults(&http.Transport{}) httpClient := &http.Client{ Transport: tr, Timeout: mesosHttpClientTimeout, } return createMesosClient(md, httpClient, tr, stateCacheTTL) }
// newConnection creates a new connection func newConnection(url url.URL, dialTimeout time.Duration, allowInsecure, enableV2 bool) *connection { var isV2 *bool if !enableV2 { v2 := false isV2 = &v2 } var rt http.RoundTripper if allowInsecure { rt = kutil.SetTransportDefaults(&http.Transport{ Dial: (&net.Dialer{ Timeout: dialTimeout, KeepAlive: 30 * time.Second, }).Dial, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }) } else { rt = kutil.SetTransportDefaults(&http.Transport{ Dial: (&net.Dialer{ Timeout: dialTimeout, KeepAlive: 30 * time.Second, }).Dial, }) } rt = transport.DebugWrappers(rt) jar, _ := cookiejar.New(nil) client := &http.Client{Jar: jar, Transport: rt} return &connection{ url: url, client: client, cached: make(map[string]repository), isV2: isV2, allowInsecure: allowInsecure, } }
func MakeTransport(config *KubeletClientConfig) (http.RoundTripper, error) { tlsConfig, err := transport.TLSConfigFor(config.transportConfig()) if err != nil { return nil, err } rt := http.DefaultTransport if config.Dial != nil || tlsConfig != nil { rt = util.SetTransportDefaults(&http.Transport{ Dial: config.Dial, TLSClientConfig: tlsConfig, }) } return transport.HTTPWrappersForConfig(config.transportConfig(), rt) }
func MakeTransport(config *KubeletConfig) (http.RoundTripper, error) { cfg := &Config{TLSClientConfig: config.TLSClientConfig} if config.EnableHttps { hasCA := len(config.CAFile) > 0 || len(config.CAData) > 0 if !hasCA { cfg.Insecure = true } } tlsConfig, err := TLSConfigFor(cfg) if err != nil { return nil, err } if config.Dial != nil || tlsConfig != nil { return util.SetTransportDefaults(&http.Transport{ Dial: config.Dial, TLSClientConfig: tlsConfig, }), nil } else { return http.DefaultTransport, nil } }
// TODO: can this use pkg/probe/http func (server *Server) DoServerCheck(rt http.RoundTripper) (probe.Result, string, error) { var client *http.Client scheme := "http://" if server.EnableHTTPS { // TODO(roberthbailey): The servers that use HTTPS are currently the // kubelets, and we should be using a standard kubelet client library // to talk to them rather than a separate http client. transport := util.SetTransportDefaults(&http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }) client = &http.Client{Transport: transport} scheme = "https://" } else { client = &http.Client{Transport: rt} } resp, err := client.Get(scheme + net.JoinHostPort(server.Addr, strconv.Itoa(server.Port)) + server.Path) if err != nil { return probe.Unknown, "", err } defer resp.Body.Close() data, err := ioutil.ReadAll(resp.Body) if err != nil { return probe.Unknown, string(data), err } if resp.StatusCode != http.StatusOK { return probe.Failure, string(data), fmt.Errorf("unhealthy http status code: %d (%s)", resp.StatusCode, resp.Status) } if server.Validate != nil { if err := server.Validate(data); err != nil { return probe.Failure, string(data), err } } return probe.Success, string(data), nil }
// init initializes GenericAPIServer. func (s *GenericAPIServer) init(c *Config) { if c.ProxyDialer != nil || c.ProxyTLSClientConfig != nil { s.ProxyTransport = util.SetTransportDefaults(&http.Transport{ Dial: c.ProxyDialer, TLSClientConfig: c.ProxyTLSClientConfig, }) } // Register root handler. // We do not register this using restful Webservice since we do not want to surface this in api docs. // Allow GenericAPIServer to be embedded in contexts which already have something registered at the root if c.EnableIndex { s.mux.HandleFunc("/", apiserver.IndexHandler(s.HandlerContainer, s.MuxHelper)) } if c.EnableLogsSupport { apiserver.InstallLogsSupport(s.MuxHelper) } if c.EnableUISupport { ui.InstallSupport(s.MuxHelper, s.enableSwaggerSupport) } if c.EnableProfiling { s.mux.HandleFunc("/debug/pprof/", pprof.Index) s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile) s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) } handler := http.Handler(s.mux.(*http.ServeMux)) // TODO: handle CORS and auth using go-restful // See github.com/emicklei/go-restful/blob/GenericAPIServer/examples/restful-CORS-filter.go, and // github.com/emicklei/go-restful/blob/GenericAPIServer/examples/restful-basic-authentication.go if len(c.CorsAllowedOriginList) > 0 { allowedOriginRegexps, err := util.CompileRegexps(c.CorsAllowedOriginList) if err != nil { glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(c.CorsAllowedOriginList, ","), err) } handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true") } s.InsecureHandler = handler attributeGetter := apiserver.NewRequestAttributeGetter(s.RequestContextMapper, s.NewRequestInfoResolver()) handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, s.authorizer) // Install Authenticator if c.Authenticator != nil { authenticatedHandler, err := handlers.NewRequestAuthenticator(s.RequestContextMapper, c.Authenticator, handlers.Unauthorized(c.SupportsBasicAuth), handler) if err != nil { glog.Fatalf("Could not initialize authenticator: %v", err) } handler = authenticatedHandler } // TODO: Make this optional? Consumers of GenericAPIServer depend on this currently. s.Handler = handler // After all wrapping is done, put a context filter around both handlers if handler, err := api.NewRequestContextFilter(s.RequestContextMapper, s.Handler); err != nil { glog.Fatalf("Could not initialize request context filter: %v", err) } else { s.Handler = handler } if handler, err := api.NewRequestContextFilter(s.RequestContextMapper, s.InsecureHandler); err != nil { glog.Fatalf("Could not initialize request context filter: %v", err) } else { s.InsecureHandler = handler } }
// init initializes master. func (m *Master) init(c *Config) { if c.ProxyDialer != nil || c.ProxyTLSClientConfig != nil { m.proxyTransport = util.SetTransportDefaults(&http.Transport{ Dial: c.ProxyDialer, TLSClientConfig: c.ProxyTLSClientConfig, }) } healthzChecks := []healthz.HealthzChecker{} storageDecorator := c.storageDecorator() dbClient := func(resource string) storage.Interface { return c.StorageDestinations.get("", resource) } podStorage := podetcd.NewStorage(dbClient("pods"), storageDecorator, c.KubeletClient, m.proxyTransport) podTemplateStorage := podtemplateetcd.NewREST(dbClient("podTemplates"), storageDecorator) eventStorage := eventetcd.NewREST(dbClient("events"), storageDecorator, uint64(c.EventTTL.Seconds())) limitRangeStorage := limitrangeetcd.NewREST(dbClient("limitRanges"), storageDecorator) resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewREST(dbClient("resourceQuotas"), storageDecorator) secretStorage := secretetcd.NewREST(dbClient("secrets"), storageDecorator) serviceAccountStorage := serviceaccountetcd.NewREST(dbClient("serviceAccounts"), storageDecorator) persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewREST(dbClient("persistentVolumes"), storageDecorator) persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewREST(dbClient("persistentVolumeClaims"), storageDecorator) namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewREST(dbClient("namespaces"), storageDecorator) m.namespaceRegistry = namespace.NewRegistry(namespaceStorage) endpointsStorage := endpointsetcd.NewREST(dbClient("endpoints"), storageDecorator) m.endpointRegistry = endpoint.NewRegistry(endpointsStorage) nodeStorage, nodeStatusStorage := nodeetcd.NewREST(dbClient("nodes"), storageDecorator, c.KubeletClient, m.proxyTransport) m.nodeRegistry = node.NewRegistry(nodeStorage) serviceStorage := serviceetcd.NewREST(dbClient("services"), storageDecorator) m.serviceRegistry = service.NewRegistry(serviceStorage) var serviceClusterIPRegistry service.RangeRegistry serviceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(m.serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", "serviceipallocation", dbClient("services")) serviceClusterIPRegistry = etcd return etcd }) m.serviceClusterIPAllocator = serviceClusterIPRegistry var serviceNodePortRegistry service.RangeRegistry serviceNodePortAllocator := portallocator.NewPortAllocatorCustom(m.serviceNodePortRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", "servicenodeportallocation", dbClient("services")) serviceNodePortRegistry = etcd return etcd }) m.serviceNodePortAllocator = serviceNodePortRegistry controllerStorage, controllerStatusStorage := controlleretcd.NewREST(dbClient("replicationControllers"), storageDecorator) // TODO: Factor out the core API registration m.storage = map[string]rest.Storage{ "pods": podStorage.Pod, "pods/attach": podStorage.Attach, "pods/status": podStorage.Status, "pods/log": podStorage.Log, "pods/exec": podStorage.Exec, "pods/portforward": podStorage.PortForward, "pods/proxy": podStorage.Proxy, "pods/binding": podStorage.Binding, "bindings": podStorage.Binding, "podTemplates": podTemplateStorage, "replicationControllers": controllerStorage, "replicationControllers/status": controllerStatusStorage, "services": service.NewStorage(m.serviceRegistry, m.endpointRegistry, serviceClusterIPAllocator, serviceNodePortAllocator, m.proxyTransport), "endpoints": endpointsStorage, "nodes": nodeStorage, "nodes/status": nodeStatusStorage, "events": eventStorage, "limitRanges": limitRangeStorage, "resourceQuotas": resourceQuotaStorage, "resourceQuotas/status": resourceQuotaStatusStorage, "namespaces": namespaceStorage, "namespaces/status": namespaceStatusStorage, "namespaces/finalize": namespaceFinalizeStorage, "secrets": secretStorage, "serviceAccounts": serviceAccountStorage, "persistentVolumes": persistentVolumeStorage, "persistentVolumes/status": persistentVolumeStatusStorage, "persistentVolumeClaims": persistentVolumeClaimStorage, "persistentVolumeClaims/status": persistentVolumeClaimStatusStorage, "componentStatuses": componentstatus.NewStorage(func() map[string]apiserver.Server { return m.getServersToValidate(c) }), } if m.tunneler != nil { m.tunneler.Run(m.getNodeAddresses) healthzChecks = append(healthzChecks, healthz.NamedCheck("SSH Tunnel Check", m.IsTunnelSyncHealthy)) prometheus.NewGaugeFunc(prometheus.GaugeOpts{ Name: "apiserver_proxy_tunnel_sync_latency_secs", Help: "The time since the last successful synchronization of the SSH tunnels for proxy requests.", }, func() float64 { return float64(m.tunneler.SecondsSinceSync()) }) } apiVersions := []string{} // Install v1 unless disabled. if !m.apiGroupVersionOverrides["api/v1"].Disable { if err := m.api_v1().InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup API v1: %v", err) } apiVersions = append(apiVersions, "v1") } apiserver.InstallSupport(m.muxHelper, m.rootWebService, c.EnableProfiling, healthzChecks...) apiserver.AddApiWebService(m.handlerContainer, c.APIPrefix, apiVersions) apiserver.InstallServiceErrorHandler(m.handlerContainer, m.newRequestInfoResolver(), apiVersions) // allGroups records all supported groups at /apis allGroups := []unversioned.APIGroup{} // Install extensions unless disabled. if !m.apiGroupVersionOverrides["extensions/v1beta1"].Disable { m.thirdPartyStorage = c.StorageDestinations.APIGroups["extensions"].Default m.thirdPartyResources = map[string]*thirdpartyresourcedataetcd.REST{} expVersion := m.experimental(c) if err := expVersion.InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup experimental api: %v", err) } g, err := latest.Group("extensions") if err != nil { glog.Fatalf("Unable to setup experimental api: %v", err) } expAPIVersions := []unversioned.GroupVersionForDiscovery{ { GroupVersion: expVersion.GroupVersion.String(), Version: expVersion.GroupVersion.Version, }, } storageVersion, found := c.StorageVersions[g.Group] if !found { glog.Fatalf("Couldn't find storage version of group %v", g.Group) } group := unversioned.APIGroup{ Name: g.Group, Versions: expAPIVersions, PreferredVersion: unversioned.GroupVersionForDiscovery{GroupVersion: storageVersion, Version: apiutil.GetVersion(storageVersion)}, } apiserver.AddGroupWebService(m.handlerContainer, c.APIGroupPrefix+"/"+latest.GroupOrDie("extensions").Group, group) allGroups = append(allGroups, group) apiserver.InstallServiceErrorHandler(m.handlerContainer, m.newRequestInfoResolver(), []string{expVersion.GroupVersion.String()}) } // This should be done after all groups are registered // TODO: replace the hardcoded "apis". apiserver.AddApisWebService(m.handlerContainer, "/apis", allGroups) // Register root handler. // We do not register this using restful Webservice since we do not want to surface this in api docs. // Allow master to be embedded in contexts which already have something registered at the root if c.EnableIndex { m.mux.HandleFunc("/", apiserver.IndexHandler(m.handlerContainer, m.muxHelper)) } if c.EnableLogsSupport { apiserver.InstallLogsSupport(m.muxHelper) } if c.EnableUISupport { ui.InstallSupport(m.muxHelper, m.enableSwaggerSupport) } if c.EnableProfiling { m.mux.HandleFunc("/debug/pprof/", pprof.Index) m.mux.HandleFunc("/debug/pprof/profile", pprof.Profile) m.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) } handler := http.Handler(m.mux.(*http.ServeMux)) insecureHandler := handler // TODO: handle CORS and auth using go-restful // See github.com/emicklei/go-restful/blob/master/examples/restful-CORS-filter.go, and // github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go attributeGetter := apiserver.NewRequestAttributeGetter(m.requestContextMapper, m.newRequestInfoResolver()) handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, m.authorizer) // Install Authenticator if c.Authenticator != nil { authenticatedHandler, err := handlers.NewRequestAuthenticator(m.requestContextMapper, c.Authenticator, handlers.Unauthorized(c.SupportsBasicAuth), handler) if err != nil { glog.Fatalf("Could not initialize authenticator: %v", err) } handler = authenticatedHandler } // Since OPTIONS request cannot carry authn headers (by w3c standards), we are doing CORS check // before auth check. Otherwise all the CORS request will be rejected. if len(c.CorsAllowedOriginList) > 0 { allowedOriginRegexps, err := util.CompileRegexps(c.CorsAllowedOriginList) if err != nil { glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(c.CorsAllowedOriginList, ","), err) } handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true") insecureHandler = apiserver.CORS(insecureHandler, allowedOriginRegexps, nil, nil, "true") } m.InsecureHandler = insecureHandler // Install root web services m.handlerContainer.Add(m.rootWebService) // TODO: Make this optional? Consumers of master depend on this currently. m.Handler = handler if m.enableSwaggerSupport { m.InstallSwaggerAPI() } // After all wrapping is done, put a context filter around both handlers if handler, err := api.NewRequestContextFilter(m.requestContextMapper, m.Handler); err != nil { glog.Fatalf("Could not initialize request context filter: %v", err) } else { m.Handler = handler } if handler, err := api.NewRequestContextFilter(m.requestContextMapper, m.InsecureHandler); err != nil { glog.Fatalf("Could not initialize request context filter: %v", err) } else { m.InsecureHandler = handler } // TODO: Attempt clean shutdown? if m.enableCoreControllers { m.NewBootstrapController().Start() } }
// InstallAPI registers endpoints for an OAuth2 server into the provided mux, // then returns an array of strings indicating what endpoints were started // (these are format strings that will expect to be sent a single string value). func (c *AuthConfig) InstallAPI(container *restful.Container) []string { // TODO: register into container mux := container.ServeMux accessTokenStorage := accesstokenetcd.NewREST(c.EtcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(c.EtcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(c.EtcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) clientAuthStorage := clientauthetcd.NewREST(c.EtcdHelper) clientAuthRegistry := clientauthregistry.NewRegistry(clientAuthStorage) authRequestHandler, authHandler, authFinalizer, err := c.getAuthorizeAuthenticationHandlers(mux) if err != nil { glog.Fatal(err) } storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, registry.NewUserConversion()) config := osinserver.NewDefaultServerConfig() if c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds > 0 { config.AuthorizationExpiration = c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds } if c.Options.TokenConfig.AccessTokenMaxAgeSeconds > 0 { config.AccessExpiration = c.Options.TokenConfig.AccessTokenMaxAgeSeconds } grantChecker := registry.NewClientAuthorizationGrantChecker(clientAuthRegistry) grantHandler := c.getGrantHandler(mux, authRequestHandler, clientRegistry, clientAuthRegistry) server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( authRequestHandler, authHandler, handlers.EmptyError{}, ), handlers.NewGrantCheck( grantChecker, grantHandler, handlers.EmptyError{}, ), authFinalizer, }, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, osinserver.NewDefaultErrorHandler(), ) server.Install(mux, OpenShiftOAuthAPIPrefix) CreateOrUpdateDefaultOAuthClients(c.Options.MasterPublicURL, c.AssetPublicAddresses, clientRegistry) osOAuthClientConfig := c.NewOpenShiftOAuthClientConfig(&OSBrowserClientBase) osOAuthClientConfig.RedirectUrl = c.Options.MasterPublicURL + path.Join(OpenShiftOAuthAPIPrefix, tokenrequest.DisplayTokenEndpoint) osOAuthClient, _ := osincli.NewClient(osOAuthClientConfig) if len(*c.Options.MasterCA) > 0 { rootCAs, err := cmdutil.CertPoolFromFile(*c.Options.MasterCA) if err != nil { glog.Fatal(err) } osOAuthClient.Transport = kutil.SetTransportDefaults(&http.Transport{ TLSClientConfig: &tls.Config{RootCAs: rootCAs}, }) } tokenRequestEndpoints := tokenrequest.NewEndpoints(c.Options.MasterPublicURL, osOAuthClient) tokenRequestEndpoints.Install(mux, OpenShiftOAuthAPIPrefix) // glog.Infof("oauth server configured as: %#v", server) // glog.Infof("auth handler: %#v", authHandler) // glog.Infof("auth request handler: %#v", authRequestHandler) // glog.Infof("grant checker: %#v", grantChecker) // glog.Infof("grant handler: %#v", grantHandler) return []string{ fmt.Sprintf("Started OAuth2 API at %%s%s", OpenShiftOAuthAPIPrefix), fmt.Sprintf("Started Login endpoint at %%s%s", OpenShiftLoginPrefix), } }
// init initializes master. func (m *Master) init(c *Config) { if c.ProxyDialer != nil || c.ProxyTLSClientConfig != nil { m.proxyTransport = util.SetTransportDefaults(&http.Transport{ Dial: c.ProxyDialer, TLSClientConfig: c.ProxyTLSClientConfig, }) } healthzChecks := []healthz.HealthzChecker{} podStorage := podetcd.NewStorage(c.DatabaseStorage, c.EnableWatchCache, c.KubeletClient, m.proxyTransport) podTemplateStorage := podtemplateetcd.NewREST(c.DatabaseStorage) eventStorage := eventetcd.NewREST(c.DatabaseStorage, uint64(c.EventTTL.Seconds())) limitRangeStorage := limitrangeetcd.NewREST(c.DatabaseStorage) resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewREST(c.DatabaseStorage) secretStorage := secretetcd.NewREST(c.DatabaseStorage) serviceAccountStorage := serviceaccountetcd.NewREST(c.DatabaseStorage) persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewREST(c.DatabaseStorage) persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewREST(c.DatabaseStorage) namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewREST(c.DatabaseStorage) m.namespaceRegistry = namespace.NewRegistry(namespaceStorage) endpointsStorage := endpointsetcd.NewREST(c.DatabaseStorage, c.EnableWatchCache) m.endpointRegistry = endpoint.NewRegistry(endpointsStorage) securityContextConstraintsStorage := sccetcd.NewStorage(c.DatabaseStorage) nodeStorage, nodeStatusStorage := nodeetcd.NewREST(c.DatabaseStorage, c.EnableWatchCache, c.KubeletClient, m.proxyTransport) m.nodeRegistry = node.NewRegistry(nodeStorage) serviceStorage := serviceetcd.NewREST(c.DatabaseStorage) m.serviceRegistry = service.NewRegistry(serviceStorage) var serviceClusterIPRegistry service.RangeRegistry serviceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(m.serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", "serviceipallocation", c.DatabaseStorage) serviceClusterIPRegistry = etcd return etcd }) m.serviceClusterIPAllocator = serviceClusterIPRegistry var serviceNodePortRegistry service.RangeRegistry serviceNodePortAllocator := portallocator.NewPortAllocatorCustom(m.serviceNodePortRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", "servicenodeportallocation", c.DatabaseStorage) serviceNodePortRegistry = etcd return etcd }) m.serviceNodePortAllocator = serviceNodePortRegistry controllerStorage := controlleretcd.NewREST(c.DatabaseStorage) // TODO: Factor out the core API registration m.storage = map[string]rest.Storage{ "pods": podStorage.Pod, "pods/attach": podStorage.Attach, "pods/status": podStorage.Status, "pods/log": podStorage.Log, "pods/exec": podStorage.Exec, "pods/portforward": podStorage.PortForward, "pods/proxy": podStorage.Proxy, "pods/binding": podStorage.Binding, "bindings": podStorage.Binding, "podTemplates": podTemplateStorage, "replicationControllers": controllerStorage, "services": service.NewStorage(m.serviceRegistry, m.endpointRegistry, serviceClusterIPAllocator, serviceNodePortAllocator, m.proxyTransport), "endpoints": endpointsStorage, "nodes": nodeStorage, "nodes/status": nodeStatusStorage, "events": eventStorage, "limitRanges": limitRangeStorage, "resourceQuotas": resourceQuotaStorage, "resourceQuotas/status": resourceQuotaStatusStorage, "namespaces": namespaceStorage, "namespaces/status": namespaceStatusStorage, "namespaces/finalize": namespaceFinalizeStorage, "secrets": secretStorage, "serviceAccounts": serviceAccountStorage, "securityContextConstraints": securityContextConstraintsStorage, "persistentVolumes": persistentVolumeStorage, "persistentVolumes/status": persistentVolumeStatusStorage, "persistentVolumeClaims": persistentVolumeClaimStorage, "persistentVolumeClaims/status": persistentVolumeClaimStatusStorage, "componentStatuses": componentstatus.NewStorage(func() map[string]apiserver.Server { return m.getServersToValidate(c) }), } if m.tunneler != nil { m.tunneler.Run(m.getNodeAddresses) healthzChecks = append(healthzChecks, healthz.NamedCheck("SSH Tunnel Check", m.IsTunnelSyncHealthy)) prometheus.NewGaugeFunc(prometheus.GaugeOpts{ Name: "apiserver_proxy_tunnel_sync_latency_secs", Help: "The time since the last successful synchronization of the SSH tunnels for proxy requests.", }, func() float64 { return float64(m.tunneler.SecondsSinceSync()) }) } apiVersions := []string{} if m.v1beta3 { if err := m.api_v1beta3().InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup API v1beta3: %v", err) } apiVersions = append(apiVersions, "v1beta3") } if m.v1 { if err := m.api_v1().InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup API v1: %v", err) } apiVersions = append(apiVersions, "v1") } apiserver.InstallSupport(m.muxHelper, m.rootWebService, c.EnableProfiling, healthzChecks...) apiserver.AddApiWebService(m.handlerContainer, c.APIPrefix, apiVersions) defaultVersion := m.defaultAPIGroupVersion() requestInfoResolver := &apiserver.APIRequestInfoResolver{APIPrefixes: sets.NewString(strings.TrimPrefix(defaultVersion.Root, "/")), RestMapper: defaultVersion.Mapper} apiserver.InstallServiceErrorHandler(m.handlerContainer, requestInfoResolver, apiVersions) if m.exp { expVersion := m.experimental(c) if err := expVersion.InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup experimental api: %v", err) } apiserver.AddApiWebService(m.handlerContainer, c.ExpAPIPrefix, []string{expVersion.Version}) expRequestInfoResolver := &apiserver.APIRequestInfoResolver{APIPrefixes: sets.NewString(strings.TrimPrefix(expVersion.Root, "/")), RestMapper: expVersion.Mapper} apiserver.InstallServiceErrorHandler(m.handlerContainer, expRequestInfoResolver, []string{expVersion.Version}) } // Register root handler. // We do not register this using restful Webservice since we do not want to surface this in api docs. // Allow master to be embedded in contexts which already have something registered at the root if c.EnableIndex { m.mux.HandleFunc("/", apiserver.IndexHandler(m.handlerContainer, m.muxHelper)) } if c.EnableLogsSupport { apiserver.InstallLogsSupport(m.muxHelper) } /*if c.EnableUISupport { ui.InstallSupport(m.mux) }*/ if c.EnableProfiling { m.mux.HandleFunc("/debug/pprof/", pprof.Index) m.mux.HandleFunc("/debug/pprof/profile", pprof.Profile) m.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) } handler := http.Handler(m.mux.(*http.ServeMux)) // TODO: handle CORS and auth using go-restful // See github.com/emicklei/go-restful/blob/master/examples/restful-CORS-filter.go, and // github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go if len(c.CorsAllowedOriginList) > 0 { allowedOriginRegexps, err := util.CompileRegexps(c.CorsAllowedOriginList) if err != nil { glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(c.CorsAllowedOriginList, ","), err) } handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true") } m.InsecureHandler = handler attributeGetter := apiserver.NewRequestAttributeGetter(m.requestContextMapper, latest.RESTMapper, "api") handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, m.authorizer) // Install Authenticator if c.Authenticator != nil { authenticatedHandler, err := handlers.NewRequestAuthenticator(m.requestContextMapper, c.Authenticator, handlers.Unauthorized(c.SupportsBasicAuth), handler) if err != nil { glog.Fatalf("Could not initialize authenticator: %v", err) } handler = authenticatedHandler } // Install root web services m.handlerContainer.Add(m.rootWebService) // TODO: Make this optional? Consumers of master depend on this currently. m.Handler = handler if m.enableSwaggerSupport { m.InstallSwaggerAPI() } // After all wrapping is done, put a context filter around both handlers if handler, err := api.NewRequestContextFilter(m.requestContextMapper, m.Handler); err != nil { glog.Fatalf("Could not initialize request context filter: %v", err) } else { m.Handler = handler } if handler, err := api.NewRequestContextFilter(m.requestContextMapper, m.InsecureHandler); err != nil { glog.Fatalf("Could not initialize request context filter: %v", err) } else { m.InsecureHandler = handler } // TODO: Attempt clean shutdown? if m.enableCoreControllers { m.NewBootstrapController().Start() } }
) // podStrategy implements behavior for Pods type podStrategy struct { runtime.ObjectTyper api.NameGenerator } // Strategy is the default logic that applies when creating and updating Pod // objects via the REST API. var Strategy = podStrategy{api.Scheme, api.SimpleNameGenerator} // PodProxyTransport is used by the API proxy to connect to pods // Exported to allow overriding TLS options (like adding a client certificate) var PodProxyTransport = util.SetTransportDefaults(&http.Transport{ // Turn off hostname verification, because connections are to assigned IPs, not deterministic TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }) // NamespaceScoped is true for pods. func (podStrategy) NamespaceScoped() bool { return true } // PrepareForCreate clears fields that are not allowed to be set by end users on creation. func (podStrategy) PrepareForCreate(obj runtime.Object) { pod := obj.(*api.Pod) pod.Status = api.PodStatus{ Phase: api.PodPending, } }
// New creates a new OpenID Connect client with the given issuerURL and clientID. // NOTE(yifan): For now we assume the server provides the "jwks_uri" so we don't // need to manager the key sets by ourselves. func New(issuerURL, clientID, caFile, usernameClaim string) (*OIDCAuthenticator, error) { var cfg oidc.ProviderConfig var err error var roots *x509.CertPool url, err := url.Parse(issuerURL) if err != nil { return nil, err } if url.Scheme != "https" { return nil, fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", issuerURL, url.Scheme) } if caFile != "" { roots, err = util.CertPoolFromFile(caFile) if err != nil { glog.Errorf("Failed to read the CA file: %v", err) } } if roots == nil { glog.Info("No x509 certificates provided, will use host's root CA set") } // Copied from http.DefaultTransport. tr := util.SetTransportDefaults(&http.Transport{ // According to golang's doc, if RootCAs is nil, // TLS uses the host's root CA set. TLSClientConfig: &tls.Config{RootCAs: roots}, }) hc := &http.Client{} hc.Transport = tr for i := 0; i <= maxRetries; i++ { if i == maxRetries { return nil, fmt.Errorf("failed to fetch provider config after %v retries", maxRetries) } cfg, err = oidc.FetchProviderConfig(hc, issuerURL) if err == nil { break } glog.Errorf("Failed to fetch provider config, trying again in %v: %v", retryBackoff, err) time.Sleep(retryBackoff) } glog.Infof("Fetched provider config from %s: %#v", issuerURL, cfg) if cfg.KeysEndpoint == "" { return nil, fmt.Errorf("OIDC provider must provide 'jwks_uri' for public key discovery") } ccfg := oidc.ClientConfig{ HTTPClient: hc, Credentials: oidc.ClientCredentials{ID: clientID}, ProviderConfig: cfg, } client, err := oidc.NewClient(ccfg) if err != nil { return nil, err } // SyncProviderConfig will start a goroutine to periodically synchronize the provider config. // The synchronization interval is set by the expiration length of the config, and has a mininum // and maximum threshold. client.SyncProviderConfig(issuerURL) return &OIDCAuthenticator{ccfg, client, usernameClaim}, nil }