diff --git a/cmd/kube-apiserver/app/aggregator.go b/cmd/kube-apiserver/app/aggregator.go index 04a33d166fc..5b22406bdf4 100644 --- a/cmd/kube-apiserver/app/aggregator.go +++ b/cmd/kube-apiserver/app/aggregator.go @@ -21,7 +21,6 @@ package app import ( "fmt" - "io/ioutil" "net/http" "strings" "sync" @@ -95,28 +94,16 @@ func createAggregatorConfig( return nil, err } - var certBytes, keyBytes []byte - if len(commandOptions.ProxyClientCertFile) > 0 && len(commandOptions.ProxyClientKeyFile) > 0 { - certBytes, err = ioutil.ReadFile(commandOptions.ProxyClientCertFile) - if err != nil { - return nil, err - } - keyBytes, err = ioutil.ReadFile(commandOptions.ProxyClientKeyFile) - if err != nil { - return nil, err - } - } - aggregatorConfig := &aggregatorapiserver.Config{ GenericConfig: &genericapiserver.RecommendedConfig{ Config: genericConfig, SharedInformerFactory: externalInformers, }, ExtraConfig: aggregatorapiserver.ExtraConfig{ - ProxyClientCert: certBytes, - ProxyClientKey: keyBytes, - ServiceResolver: serviceResolver, - ProxyTransport: proxyTransport, + ProxyClientCertFile: commandOptions.ProxyClientCertFile, + ProxyClientKeyFile: commandOptions.ProxyClientKeyFile, + ServiceResolver: serviceResolver, + ProxyTransport: proxyTransport, }, } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/BUILD b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/BUILD index d527a263b89..aed87fbe3ef 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/BUILD +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/BUILD @@ -22,6 +22,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates:go_default_library", "//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1:go_default_library", "//staging/src/k8s.io/kube-aggregator/pkg/apiserver/scheme:go_default_library", @@ -62,6 +63,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//staging/src/k8s.io/apiserver/pkg/features:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/egressselector:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/storage:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go index 9e38d0412d1..1b22b24e274 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/pkg/version" openapicommon "k8s.io/kube-openapi/pkg/common" + "k8s.io/apiserver/pkg/server/dynamiccertificates" v1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" v1helper "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper" "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1" @@ -64,8 +65,8 @@ const legacyAPIServiceName = "v1." type ExtraConfig struct { // ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use // this to confirm the proxy's identity - ProxyClientCert []byte - ProxyClientKey []byte + ProxyClientCertFile string + ProxyClientKeyFile string // If present, the Dial method will be used for dialing out to delegate // apiservers. @@ -108,11 +109,9 @@ type APIAggregator struct { delegateHandler http.Handler - // proxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use - // this to confirm the proxy's identity - proxyClientCert []byte - proxyClientKey []byte - proxyTransport *http.Transport + // proxyCurrentCertKeyContent holds he client cert used to identify this proxy. Backing APIServices use this to confirm the proxy's identity + proxyCurrentCertKeyContent certKeyFunc + proxyTransport *http.Transport // proxyHandlers are the proxy handlers that are currently registered, keyed by apiservice.name proxyHandlers map[string]*proxyHandler @@ -178,18 +177,17 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg ) s := &APIAggregator{ - GenericAPIServer: genericServer, - delegateHandler: delegationTarget.UnprotectedHandler(), - proxyClientCert: c.ExtraConfig.ProxyClientCert, - proxyClientKey: c.ExtraConfig.ProxyClientKey, - proxyTransport: c.ExtraConfig.ProxyTransport, - proxyHandlers: map[string]*proxyHandler{}, - handledGroups: sets.String{}, - lister: informerFactory.Apiregistration().V1().APIServices().Lister(), - APIRegistrationInformers: informerFactory, - serviceResolver: c.ExtraConfig.ServiceResolver, - openAPIConfig: openAPIConfig, - egressSelector: c.GenericConfig.EgressSelector, + GenericAPIServer: genericServer, + delegateHandler: delegationTarget.UnprotectedHandler(), + proxyTransport: c.ExtraConfig.ProxyTransport, + proxyHandlers: map[string]*proxyHandler{}, + handledGroups: sets.String{}, + lister: informerFactory.Apiregistration().V1().APIServices().Lister(), + APIRegistrationInformers: informerFactory, + serviceResolver: c.ExtraConfig.ServiceResolver, + openAPIConfig: openAPIConfig, + egressSelector: c.GenericConfig.EgressSelector, + proxyCurrentCertKeyContent: func() (bytes []byte, bytes2 []byte) { return nil, nil }, } apiGroupInfo := apiservicerest.NewRESTStorage(c.GenericConfig.MergedResourceConfig, c.GenericConfig.RESTOptionsGetter) @@ -214,14 +212,30 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle("/apis/", apisHandler) apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().V1().APIServices(), s) + if len(c.ExtraConfig.ProxyClientCertFile) > 0 && len(c.ExtraConfig.ProxyClientKeyFile) > 0 { + aggregatorProxyCerts, err := dynamiccertificates.NewDynamicServingContentFromFiles("aggregator-proxy-cert", c.ExtraConfig.ProxyClientCertFile, c.ExtraConfig.ProxyClientKeyFile) + if err != nil { + return nil, err + } + if err := aggregatorProxyCerts.RunOnce(); err != nil { + return nil, err + } + aggregatorProxyCerts.AddListener(apiserviceRegistrationController) + s.proxyCurrentCertKeyContent = aggregatorProxyCerts.CurrentCertKeyContent + + s.GenericAPIServer.AddPostStartHookOrDie("aggregator-reload-proxy-client-cert", func(context genericapiserver.PostStartHookContext) error { + go aggregatorProxyCerts.Run(1, context.StopCh) + return nil + }) + } + availableController, err := statuscontrollers.NewAvailableConditionController( informerFactory.Apiregistration().V1().APIServices(), c.GenericConfig.SharedInformerFactory.Core().V1().Services(), c.GenericConfig.SharedInformerFactory.Core().V1().Endpoints(), apiregistrationClient.ApiregistrationV1(), c.ExtraConfig.ProxyTransport, - c.ExtraConfig.ProxyClientCert, - c.ExtraConfig.ProxyClientKey, + (func() ([]byte, []byte))(s.proxyCurrentCertKeyContent), s.serviceResolver, c.GenericConfig.EgressSelector, ) @@ -309,12 +323,11 @@ func (s *APIAggregator) AddAPIService(apiService *v1.APIService) error { // register the proxy handler proxyHandler := &proxyHandler{ - localDelegate: s.delegateHandler, - proxyClientCert: s.proxyClientCert, - proxyClientKey: s.proxyClientKey, - proxyTransport: s.proxyTransport, - serviceResolver: s.serviceResolver, - egressSelector: s.egressSelector, + localDelegate: s.delegateHandler, + proxyCurrentCertKeyContent: s.proxyCurrentCertKeyContent, + proxyTransport: s.proxyTransport, + serviceResolver: s.serviceResolver, + egressSelector: s.egressSelector, } proxyHandler.updateAPIService(apiService) if s.openAPIAggregationController != nil { diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go index b64c2f32bcf..506748954cc 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/labels" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/server/dynamiccertificates" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" "k8s.io/klog/v2" @@ -53,6 +54,8 @@ type APIServiceRegistrationController struct { queue workqueue.RateLimitingInterface } +var _ dynamiccertificates.Listener = &APIServiceRegistrationController{} + // NewAPIServiceRegistrationController returns a new APIServiceRegistrationController. func NewAPIServiceRegistrationController(apiServiceInformer informers.APIServiceInformer, apiHandlerManager APIHandlerManager) *APIServiceRegistrationController { c := &APIServiceRegistrationController{ @@ -152,7 +155,7 @@ func (c *APIServiceRegistrationController) processNextWorkItem() bool { return true } -func (c *APIServiceRegistrationController) enqueue(obj *v1.APIService) { +func (c *APIServiceRegistrationController) enqueueInternal(obj *v1.APIService) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err != nil { klog.Errorf("Couldn't get key for object %#v: %v", obj, err) @@ -165,13 +168,13 @@ func (c *APIServiceRegistrationController) enqueue(obj *v1.APIService) { func (c *APIServiceRegistrationController) addAPIService(obj interface{}) { castObj := obj.(*v1.APIService) klog.V(4).Infof("Adding %s", castObj.Name) - c.enqueue(castObj) + c.enqueueInternal(castObj) } func (c *APIServiceRegistrationController) updateAPIService(obj, _ interface{}) { castObj := obj.(*v1.APIService) klog.V(4).Infof("Updating %s", castObj.Name) - c.enqueue(castObj) + c.enqueueInternal(castObj) } func (c *APIServiceRegistrationController) deleteAPIService(obj interface{}) { @@ -189,5 +192,18 @@ func (c *APIServiceRegistrationController) deleteAPIService(obj interface{}) { } } klog.V(4).Infof("Deleting %q", castObj.Name) - c.enqueue(castObj) + c.enqueueInternal(castObj) +} + +// Enqueue queues all apiservices to be rehandled. +// This method is used by the controller to notify when the proxy cert content changes. +func (c *APIServiceRegistrationController) Enqueue() { + apiServices, err := c.apiServiceLister.List(labels.Everything()) + if err != nil { + utilruntime.HandleError(err) + return + } + for _, apiService := range apiServices { + c.addAPIService(apiService) + } } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go index ae733929aba..e09f0d1a238 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go @@ -48,17 +48,17 @@ const ( aggregatedDiscoveryTimeout = 5 * time.Second ) +type certKeyFunc func() ([]byte, []byte) + // proxyHandler provides a http.Handler which will proxy traffic to locations // specified by items implementing Redirector. type proxyHandler struct { // localDelegate is used to satisfy local APIServices localDelegate http.Handler - // proxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use - // this to confirm the proxy's identity - proxyClientCert []byte - proxyClientKey []byte - proxyTransport *http.Transport + // proxyCurrentCertKeyContent holds the client cert used to identify this proxy. Backing APIServices use this to confirm the proxy's identity + proxyCurrentCertKeyContent certKeyFunc + proxyTransport *http.Transport // Endpoints based routing to map from cluster IP to routable IP serviceResolver ServiceResolver @@ -248,14 +248,16 @@ func (r *proxyHandler) updateAPIService(apiService *apiregistrationv1api.APIServ return } + proxyClientCert, proxyClientKey := r.proxyCurrentCertKeyContent() + newInfo := proxyHandlingInfo{ name: apiService.Name, restConfig: &restclient.Config{ TLSClientConfig: restclient.TLSClientConfig{ Insecure: apiService.Spec.InsecureSkipTLSVerify, ServerName: apiService.Spec.Service.Name + "." + apiService.Spec.Service.Namespace + ".svc", - CertData: r.proxyClientCert, - KeyData: r.proxyClientKey, + CertData: proxyClientCert, + KeyData: proxyClientKey, CAData: apiService.Spec.CABundle, }, }, diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy_test.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy_test.go index a2bc5f22003..76af2746cb1 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy_test.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy_test.go @@ -18,12 +18,16 @@ package apiserver import ( "crypto/tls" + "crypto/x509" "fmt" "io/ioutil" + "k8s.io/apiserver/pkg/server/dynamiccertificates" "net/http" "net/http/httptest" "net/http/httputil" "net/url" + "os" + "path/filepath" "reflect" "strings" "sync/atomic" @@ -90,6 +94,10 @@ func (r *mockedRouter) ResolveEndpoint(namespace, name string, port int32) (*url return &url.URL{Scheme: "https", Host: r.destinationHost}, r.err } +func emptyCert() []byte { + return []byte{} +} + func TestProxyHandler(t *testing.T) { target := &targetHTTPHandler{} targetServer := httptest.NewUnstartedServer(target) @@ -274,9 +282,10 @@ func TestProxyHandler(t *testing.T) { serviceResolver = &mockedRouter{destinationHost: targetServer.Listener.Addr().String()} } handler := &proxyHandler{ - localDelegate: http.NewServeMux(), - serviceResolver: serviceResolver, - proxyTransport: &http.Transport{}, + localDelegate: http.NewServeMux(), + serviceResolver: serviceResolver, + proxyTransport: &http.Transport{}, + proxyCurrentCertKeyContent: func() ([]byte, []byte) { return emptyCert(), emptyCert() }, } server := httptest.NewServer(contextHandler(handler, tc.user)) defer server.Close() @@ -418,8 +427,9 @@ func TestProxyUpgrade(t *testing.T) { serverURL, _ := url.Parse(backendServer.URL) proxyHandler := &proxyHandler{ - serviceResolver: &mockedRouter{destinationHost: serverURL.Host}, - proxyTransport: &http.Transport{}, + serviceResolver: &mockedRouter{destinationHost: serverURL.Host}, + proxyTransport: &http.Transport{}, + proxyCurrentCertKeyContent: func() ([]byte, []byte) { return emptyCert(), emptyCert() }, } proxyHandler.updateAPIService(tc.APIService) aggregator := httptest.NewServer(contextHandler(proxyHandler, &user.DefaultInfo{Name: "username"})) @@ -566,3 +576,294 @@ func TestGetContextForNewRequest(t *testing.T) { } } + +// TestProxyCertReload verifies that the proxy reloading of certificates work +// to be able to test the reloading it starts a server with client auth enabled +// it first uses certs that does not match the client CA so the verification fails - expecting HTTP 503 +// then we write correct client certs to the disk, expecting the proxy to reload the cert and use it for the next request +// +// Note: this test doesn't use apiserviceRegistrationController nor it doesn't start DynamicServingContentFromFiles controller +// instead it manually calls to updateAPIService and RunOnce to reload the certificate +func TestProxyCertReload(t *testing.T) { + // STEP 1: set up a backend server that will require the client certificate + // this server uses clientCaCrt to validate the client certificate + backendHandler := &targetHTTPHandler{} + backendServer := httptest.NewUnstartedServer(backendHandler) + if cert, err := tls.X509KeyPair(backendCertificate, backendKey); err != nil { + t.Fatal(err) + } else { + caCertPool := x509.NewCertPool() + // we're testing this while enabling MTLS + caCertPool.AppendCertsFromPEM(clientCaCrt) + backendServer.TLS = &tls.Config{Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: caCertPool} + } + backendServer.StartTLS() + defer backendServer.Close() + + // STEP 2: set up the aggregator that will use an invalid certificate (it won't be validated by the clientCA) to auth against the backend server + aggregatorHandler := &proxyHandler{ + localDelegate: http.NewServeMux(), + serviceResolver: &mockedRouter{destinationHost: backendServer.Listener.Addr().String()}, + } + certFile, keyFile, dir := getCertAndKeyPaths(t) + writeCerts(certFile, keyFile, backendCertificate, backendKey, t) + + defer func() { + if err := os.RemoveAll(dir); err != nil { + t.Errorf("Unable to clean up test directory %q: %v", dir, err) + } + }() + + certProvider, err := dynamiccertificates.NewDynamicServingContentFromFiles("test", certFile, keyFile) + if err != nil { + t.Fatalf("Unable to create dynamic certificates: %v", err) + } + err = certProvider.RunOnce() + if err != nil { + t.Fatalf("Unable to load dynamic certificates: %v", err) + } + aggregatorHandler.proxyCurrentCertKeyContent = certProvider.CurrentCertKeyContent + + apiService := &apiregistration.APIService{ + ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"}, + Spec: apiregistration.APIServiceSpec{ + Service: &apiregistration.ServiceReference{Name: "test-service2", Namespace: "test-ns", Port: pointer.Int32Ptr(443)}, + Group: "foo", + Version: "v1", + CABundle: backendCaCertificate, // used to validate backendCertificate + }, + Status: apiregistration.APIServiceStatus{ + Conditions: []apiregistration.APIServiceCondition{ + {Type: apiregistration.Available, Status: apiregistration.ConditionTrue}, + }, + }, + } + aggregatorHandler.updateAPIService(apiService) + + server := httptest.NewServer(contextHandler(aggregatorHandler, &user.DefaultInfo{ + Name: "username", + Groups: []string{"one", "two"}, + })) + defer server.Close() + + resp, err := http.Get(server.URL + "/request/path") + if err != nil { + t.Fatalf("got unexpected error: %v", err) + } + if resp.StatusCode != http.StatusServiceUnavailable { + t.Fatalf("Expected status code 503 but got %d", resp.StatusCode) + } + + // STEP 3: swap the certificate used by the aggregator to auth against the backend server and verify the request passes + // note that this step uses the certificate that can be validated by the backend server with clientCaCrt + writeCerts(certFile, keyFile, clientCert, clientKey, t) + err = certProvider.RunOnce() + if err != nil { + t.Fatalf("Expected no error when refreshing dynamic certs, got %v", err) + } + aggregatorHandler.updateAPIService(apiService) + + resp, err = http.Get(server.URL + "/request/path") + if err != nil { + t.Errorf("%v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("Expected status code 200 but got %d", resp.StatusCode) + } +} + +func getCertAndKeyPaths(t *testing.T) (string, string, string) { + dir, err := ioutil.TempDir(os.TempDir(), "k8s-test-handler-proxy-cert") + if err != nil { + t.Fatalf("Unable to create the test directory %q: %v", dir, err) + } + certFile := filepath.Join(dir, "certfile.pem") + keyFile := filepath.Join(dir, "keytfile.pem") + return certFile, keyFile, dir +} + +func writeCerts(certFile, keyFile string, certContent, keyContent []byte, t *testing.T) { + if err := ioutil.WriteFile(certFile, certContent, 0600); err != nil { + t.Fatalf("Unable to create the file %q: %v", certFile, err) + } + if err := ioutil.WriteFile(keyFile, keyContent, 0600); err != nil { + t.Fatalf("Unable to create the file %q: %v", keyFile, err) + } +} + +// cert and ca for client auth +var clientCert = []byte(`-----BEGIN CERTIFICATE----- +MIIFaDCCA1ACAWUwDQYJKoZIhvcNAQEFBQAwejELMAkGA1UEBhMCVVMxEzARBgNV +BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxGDAWBgNVBAoM +D015IG9yZ2FuaXphdGlvbjEQMA4GA1UECwwHTXkgdW5pdDESMBAGA1UEAwwJbG9j +YWxob3N0MB4XDTIwMDUyMjA4MTA1MVoXDTIxMDUyMjA4MTA1MVowejELMAkGA1UE +BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZp +ZXcxGDAWBgNVBAoMD015IG9yZ2FuaXphdGlvbjEQMA4GA1UECwwHTXkgdW5pdDES +MBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEAwdDdguS2eVb950cmuyK/fTEBy+I1OFwPSg6S2zF5v/98Sva87Y/qFBrv1EzY +usU+OWuH0nnyk14bOGl+imbvk+tdiXr4i8tIY8QnBrUbyNvPwemcRejQQb1P5YX0 +An3BS8vckt1e1zahhyb+Uch/ApLFzv3nOEGg7OTA5vfyNs/OUcaz7XuKrFQipxLA +wEpPbukI8ThH2uLwiRxWUrLGmOeWocM4JFCk6LaQLWkTzl9WgKTYwzrI24LaUgb6 +0urlUi0bmE8AJRZBdmVCiEapxiHDre8c3CaLh8aF1LQ95ZraF8NZAvMxJvSK0R7I +05V+eZH+xdBH2n5naLjVuvm96VPbDGlcWRwi+ZKZXAvi6YMNJ5g564u2Nl+eACtd +9Kg6C9AIU8vSX9WrX4UcwaohQVjxUmHNL6YqHXhltyPdN3coFxDSPyp46x8Y2BIW +s1x1qnlor5xOOQhYPoIQzMgrgJw6wRLWdIkyP/NOazSwet2i4cpeLD3wgXpuylQp +Of06WChGN7NRx9JQSA7y6JKJq38jyB4+iNpU7NfkCQQndwvowPUBOSXNAUOgv2Qt +QEiODhNPsHhSHM6L4xSpwFzh7dDywpPCeb6Fzyp/EslaLiFoEQr2Wc0xM/Xssqa6 +yBjSpATBqP1exQVr7LQn50lf9penN4FOQRZ9k/49DLX1RFUCAwEAATANBgkqhkiG +9w0BAQUFAAOCAgEAVyFuPhtyDMi8FxD00fqnAxwnr7IyNBwYuQivu7gXKwQ2U9v1 +LSqDxvUft6sDWNUl/2f+Lga3CaVJ7FJL/rOwU5APkD4lcc43UcUv8pN2QAVFUs2h +8MPEZnM2oHEA3M77Yr1RZUHE24pHsv3Bi0u7w8kPhFb7ebAbfXAHIWkekPejroso +fOC2W8PXGqCJcpuIrAzIRvu/Ia0Cu4bmSZp4pK4lilgmUCr5LTc3YeNuAvbqco8f +mhXJ+qR4PYWkldgOdhz7eajKF0JP6R8pQacCTZ5OM1y9tg3yN6BEKus3EojpDtqs +5cTegj914lnNXI/bod6kqnuMT1sfnt2y8AmUcgD+NMhw6dG6zJI1Jf+01G2q3HCn +wtB0jPntk1hRepVkLfSvxoMofkjESHSVstYiGRQWQziFq98ei59uW1ZNpP/yVJGb +I7eM/b3vnFUBX2eypfVyY7+vBCxvgRjmpKnOuhCgm2bla1Ho7XUz1OvGkYfnHM3u +lUiTnAdNXQEf1Y2OjWeHeQeoeJ7gJiwJhMH8yZIierLHDP7FbBSLZ+VZW4Wfe6vT +WJ4no8kkD5ROWBNf0c0dt2uip6dZ5L2zMrqeUrhpy59ZhoZoMP5cmY/sfTzpRzNO +KitvR2SwVL12T6pAkwq3ItdiGZ16x5XrYv22H0jP8R6MCd59Sfnz9wWdY1Q= +-----END CERTIFICATE-----`) +var clientKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----`) +var backendCertificate = []byte(`-----BEGIN CERTIFICATE----- +MIICszCCAZsCCQDDGNgLmIQtOTANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAh0 +ZXN0LWNhMjAeFw0yMDA1MzExMDAwMTRaFw0yMjA5MDMxMDAwMTRaMCQxIjAgBgNV +BAMMGXRlc3Qtc2VydmljZTIudGVzdC1ucy5zdmMwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDFhy+tjaC7UcaHD0qqF8HOT22EUtUwaA0LQYHQtrbJVQb8 +pGcqm2IdGr1MelSkXO39quhfVDrlXQQV4SVIUmBHMIDmcc9rYoQKutqR7ukaYlSd +VqkQSTYRm10XeOp9qNmdXe/bq/DhP8Pc1JuISjBghmOEQGzI9SUw6aRfgXdixTOS +sL5gpVn6rnNJNGnN9RQPwAIzpp4xbe4UFOoEisHa5G5ohMIbA4bu8+CHLJzBHOww +llw7iRUZvn+i1gHtlGVgWz/U01iL+g0vvoPNi8HpDO5OYlTO1jdRonr/LxS5sgIw +wWTpMqItALzLPHZebTH1Y21+njPBE+MjYJZr9rnnAgMBAAEwDQYJKoZIhvcNAQEL +BQADggEBAJWqT0XnBVGUjUUYRJUzyLfHe9L7KJ2gHgI8S+AyscQUi2meOwl3tqlZ +Z1bQNFKGQ17n0uKCfr5vknHNAH+Rme7wcQaOHozrRsfx5ktziIRjOSc2tE9cssXz +8rTu4RbfxgRgkHxvW4XNn6liB4BarzfANtg6OjftB2RSCZ5de+e9Q/zOgZD8KAjR +GD9mE7P/UnZFobNUehaAY3FHPiP+r2txpBPqqxLcsb/qv9rFQsz7OO++n5AN8fb+ +wT/wiq1NdOVhyhncPnzdwJZxvOM3MtuXzN6UbqZ1ur/DxWYrhaiSWmWxCXWoQfde +Ijs5dRes3aVe33pMaDGTJ6QCEze2QxE= +-----END CERTIFICATE-----`) +var backendKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAxYcvrY2gu1HGhw9KqhfBzk9thFLVMGgNC0GB0La2yVUG/KRn +KptiHRq9THpUpFzt/aroX1Q65V0EFeElSFJgRzCA5nHPa2KECrrake7pGmJUnVap +EEk2EZtdF3jqfajZnV3v26vw4T/D3NSbiEowYIZjhEBsyPUlMOmkX4F3YsUzkrC+ +YKVZ+q5zSTRpzfUUD8ACM6aeMW3uFBTqBIrB2uRuaITCGwOG7vPghyycwRzsMJZc +O4kVGb5/otYB7ZRlYFs/1NNYi/oNL76DzYvB6QzuTmJUztY3UaJ6/y8UubICMMFk +6TKiLQC8yzx2Xm0x9WNtfp4zwRPjI2CWa/a55wIDAQABAoIBAQCAqU2k/ltzqBBo +aM15fX//ojzztACpRx0397NW/6yP95JVfcC1QADodEJZTlVTujRKxsgVUAgM3kmK +9twR/5Y2yKEteXRhvgnD83HrHHM5fFMhKRF2SjmtvkUkxN34e8NDfax+qcB898vc +S6ADZk+cj+zCeDRjsUpIUed/ThU1f5ftBUKnexVSWQpWzy1ceAFKmD3Qe+Up22AT +SCWXxv8pYoghs2iyAYb7eQFD/+BBVeJykXWvdDfi0TzqNhGC28PZBuE9sMq/+Yhu +uGd4BRlKaE6B+vqxsY5Ub3m/4kvSGo2HCL6GBiD1zwuTkyrcA/9y2bne/MWfFdj9 +2BBHKOYBAoGBAPPwkBVUbjWZ+rtcObxx32zUui+4wiN4srZgyQowwWH5eHfVr6T1 +DYN/fktTs8vtAqv5tgaDEo58V1SyOINCvp2b4PsnAASs3MbJCLiGzxroyKzhgeBe +gX+AY2ijwC/XGSZj94dK4dXBesza7CcWqJlP13Yp9DXnGPWFq/IOxulLAoGBAM9L +NkBiM7T+K0tnpRAe1cM7oyIRYgv1XqkHG/DoPq6npgFLeI3Dc/HMV0gS12YfY1f1 +s5JbVIPKQPr9viTmDcau64aqpZqVZcOqQV38AskRJQSHr7ss5i5gzcNkefROmdIA +2lYLrEt8H3PC1wkk8biOwEeXHnMhNCnBfUn7W0xVAoGACn4nhHNcRjv4WATQivWO ++bxwwcq9tw7jCQtCuoh8WP2FHAp6AqtzyFs8kHrqOfRY8BLOrJsIuk5I52C/I45E +ar0gwUzdKFZTLM3K7T0HPY4Ty7PrhT4rbdOU8xRQGP60mz0jkZM8AZjP8m3cSJYl +7GpNx0xor8TgAveb/M576d8CgYAwQ6fHB9ZYLtGvxdsFzNgik9EgzoFQnXnDyzbz +OW/WxIv/Qy43e6mUQ+qSimiCi45a3YdI7WDZKo9EoS3Tc4kDmJiYC0Vxn5VJIGwF +0PZpEEfZLSp6XzLc24ctFkja3C4uWip73E3qaWT9VAEzTNnHCd21DXd2gOWfT0C3 +qAGS3QKBgQCTXOZFOyBYuHTuW6WivhE37BYjSTQv8ig45xMdmM8/tFKtum9oUWpk +rxkwaxSqpHF5WjsepakAWRrARpsxNa9m1A7u8s5Ui6GremS5d/IMi6W0rrarK4xn +ktdTr3ZZVCFnQbkH5dIFGbn7gBCFntHSooPET+nqDBIZVkSBljY17w== +-----END RSA PRIVATE KEY-----`) +var backendCaCertificate = []byte(`-----BEGIN CERTIFICATE----- +MIICojCCAYoCCQD19rP3+torQjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAh0 +ZXN0LWNhMjAeFw0yMDA1MzEwOTU5NDFaFw0yNTA1MzAwOTU5NDFaMBMxETAPBgNV +BAMMCHRlc3QtY2EyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTvF +4u3OfiXSkJXQ899xqTSV43/U3YUZ+xg2wj8Mg1gOQ3TNW/LWNxErz9bvqZX0EPjx +j7ilqmneDKSsNTqQ/4sYxo0h/ZA7AEux3+A2fk+P6KzOb++AiYctJxZuYI3OrB/4 +seU9KO4nNYSVku6uH1nYCjzDTFWQDJuS/SLbPMc7jggywuhp65tlPR/nuL9G2V8t +5nXV08B4wQ7IdhmequIUPpMtajgobtrDhxpLR3V36t1f57BHU0N/IWWF+kIZFf5F +7xwsgFBtyXYmjYlmfEwCRQvHNVdUfpp2wI040s7fZs3A64mKA+Xe61J/fJCKzAuC +mhATlL+SJ7xNVTsqeQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCd+Esl57Zy36cO +gHNEvoo2TOtRf8qhuJChxEabIYg2RkRID8s+SYQPlSZ0iWKOsYbT2qmOBubGwVJH +r0DKGqYNRMEAMQYOwnvDJ4S+Bexj1zhBwS/PhdRL0gz7tAkzJOTyybFBHgOu+Xg5 +bqeOuCUY8piUl/UiuULcrF6+BttQZwBWixfHMMuQzTAsnHTqMGOSqnhTdbnELUcr +lOz+cVhXs4AWVCDOMXUKKNy1fQglqt/cMangLhrYj+//CKzimsgYHDHfaO2Uo7W+ +peBdV/d+f9YupxJoa83EilhIJtbj17csFxUloTRG2y9Xmf+jFdbz8H0+n0Pq7n38 +EyMJKLfk +-----END CERTIFICATE-----`) +var clientCaCrt = []byte(`-----BEGIN CERTIFICATE----- +MIIFcDCCA1gCCQDgTBDe5gjLSDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJV +UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEY +MBYGA1UECgwPTXkgb3JnYW5pemF0aW9uMRAwDgYDVQQLDAdNeSB1bml0MRIwEAYD +VQQDDAlsb2NhbGhvc3QwHhcNMjAwNTIyMDczNTQxWhcNMzAwNTIwMDczNTQxWjB6 +MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 +bnRhaW4gVmlldzEYMBYGA1UECgwPTXkgb3JnYW5pemF0aW9uMRAwDgYDVQQLDAdN +eSB1bml0MRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCj89Np0QeBHn6pyDUrzd45Ow9oHTBgvrDAmhND0i+WkcoDAOrX +V4W6aNLibM/5stR7PRwl93cwkLawE84YHevH7/69EeTjYqIUUTF/Otxh+qTZMDUu +Z3hcW7Pu/JnfHbmliR+ci4kr7KkVAYHJtT9DcyWAs5KUudPGKpQprVKtnJ04J/hV +gDrZbBVKU/N7Ik0ta0MWy97LegbRaGrcY/h7ICoaeMDL0UGU8b61tUCVObmhAnM6 +jK6xk/PtMk2d4we3yIWhowrGbp8vxN25WtFXIvJfyrrLFvpsl1f/dLwOzxU8RIt0 +soXkF5ig6BkjzXtG+WM8ZHBGgL1salP6B0IhLjIjsyZVNORyRJEn0SxDnVKtYLuO +tjcDZb1Ij/KzWdyXCMD8uJECO9z1Zt2kCfsZDjCal+nyas9Otn3djERaGaaQZd1q +oL/ioQSTgRhHO3Jx721YaetfM5Bf4h/xGIZlR0wsUPM86rN3s5LcN01C8MLMt3op +l5ECQE4zlCq2j7EZwlTcq7B5onwUDqQYImD/AHIaOMAeAxHCfeGAl9t+84pnd9iU +BG3XnaSdrhJJApK7Pa7peu7FDaeAkl71VQW0URHjCedCHNdqk1pbsCJMKfpMuRWp +LldTG83/bCyuNsku8rkKmkY25MSt80EpyYxg0ZfP2GqSX9+wbH67EJlEfQIDAQAB +MA0GCSqGSIb3DQEBCwUAA4ICAQAqaCc/LkDdJq/QS27qhCKEI885ZYOHuk8N64G6 +7Mfk6YhkSf5/Ln4qwP0f4HJCgupRMRLFs96qIh2HeEvytQk/xd8j111BHBUmjx3E +tS271x6PTkwkHa5j7kxE85b/wnUjVZ58NKccstp/Ub/ajssPdS7Ohzm0DGTjktja +Bavju5Q3fyBl4OmICOVDqIVBqNUfszesBtW9QcSgW7VcL2X+5/H/tu2YYnJG8IXp +v4uJRZ2rimhQZFFvcihCMN6wR7M5hqDPyffloHy+tFYFNd+Wc+RHU/DU2i83ySa/ +BwRD5J8iTHplDFosCo1u6EoALWQx/WM/l4E9P895LFFoF/8tvHUeLAQXjUbqEPUq +sbHlhZK18vxYUu/n+OtRdHDimjjoEWZHgoUNnNardukcLdGvk2dbmWltd8NA+kjh +e88NQn5x5mKUfENtK/GYKN4duguR6mOKlKBuobLcjeplnrHcRoWsvYOPJr0L9Ki3 +F1XEUPu0NgZyx5kTX3znm+7UV/W1rZeRppHSeqVfwHE+N2FEds65rMF1sEvw3fZv +mwAA1eyVJXIGum9MHf9XAgjjyubtwzPdCE6NQ9nYBuXr6sAqZx6irTHrtHl7zmbJ +St3GLAs3qHVMa6Va1imhvInbV6m9CauCbt4vAs6xVtR/jIaq1NKHP63f+bHp8hhK +4ulSKQ== +-----END CERTIFICATE-----`) diff --git a/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go b/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go index e5a0d1a4ecc..31ccdcf337e 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go @@ -17,9 +17,9 @@ limitations under the License. package server import ( + "errors" "fmt" "io" - "io/ioutil" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -152,16 +152,13 @@ func (o AggregatorOptions) RunAggregator(stopCh <-chan struct{}) error { }, } - var err error - config.ExtraConfig.ProxyClientCert, err = ioutil.ReadFile(o.ProxyClientCertFile) - if err != nil { - return err - } - config.ExtraConfig.ProxyClientKey, err = ioutil.ReadFile(o.ProxyClientKeyFile) - if err != nil { - return err + if len(o.ProxyClientCertFile) == 0 || len(o.ProxyClientKeyFile) == 0 { + return errors.New("missing a client certificate along with a key to identify the proxy to the API server") } + config.ExtraConfig.ProxyClientCertFile = o.ProxyClientCertFile + config.ExtraConfig.ProxyClientKeyFile = o.ProxyClientKeyFile + server, err := config.Complete().NewWithDelegate(genericapiserver.NewEmptyDelegate()) if err != nil { return err diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go index ca2c276eaa0..7cf38ced4c2 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go @@ -19,6 +19,7 @@ package apiserver import ( "context" "fmt" + "net" "net/http" "net/url" "reflect" @@ -51,6 +52,8 @@ import ( "k8s.io/kube-aggregator/pkg/controllers" ) +type certKeyFunc func() ([]byte, []byte) + // ServiceResolver knows how to convert a service reference into an actual location. type ServiceResolver interface { ResolveEndpoint(namespace, name string, port int32) (*url.URL, error) @@ -70,8 +73,10 @@ type AvailableConditionController struct { endpointsLister v1listers.EndpointsLister endpointsSynced cache.InformerSynced - discoveryClient *http.Client - serviceResolver ServiceResolver + // dialContext specifies the dial function for creating unencrypted TCP connections. + dialContext func(ctx context.Context, network, address string) (net.Conn, error) + proxyCurrentCertKeyContent certKeyFunc + serviceResolver ServiceResolver // To allow injection for testing. syncFn func(key string) error @@ -90,8 +95,7 @@ func NewAvailableConditionController( endpointsInformer v1informers.EndpointsInformer, apiServiceClient apiregistrationclient.APIServicesGetter, proxyTransport *http.Transport, - proxyClientCert []byte, - proxyClientKey []byte, + proxyCurrentCertKeyContent certKeyFunc, serviceResolver ServiceResolver, egressSelector *egressselector.EgressSelector, ) (*AvailableConditionController, error) { @@ -110,17 +114,7 @@ func NewAvailableConditionController( // the maximum disruption time to a minimum, but it does prevent hot loops. workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 30*time.Second), "AvailableConditionController"), - } - - // if a particular transport was specified, use that otherwise build one - // construct an http client that will ignore TLS verification (if someone owns the network and messes with your status - // that's not so bad) and sets a very short timeout. This is a best effort GET that provides no additional information - restConfig := &rest.Config{ - TLSClientConfig: rest.TLSClientConfig{ - Insecure: true, - CertData: proxyClientCert, - KeyData: proxyClientKey, - }, + proxyCurrentCertKeyContent: proxyCurrentCertKeyContent, } if egressSelector != nil { @@ -130,19 +124,9 @@ func NewAvailableConditionController( if err != nil { return nil, err } - restConfig.Dial = egressDialer + c.dialContext = egressDialer } else if proxyTransport != nil && proxyTransport.DialContext != nil { - restConfig.Dial = proxyTransport.DialContext - } - - transport, err := rest.TransportFor(restConfig) - if err != nil { - return nil, err - } - c.discoveryClient = &http.Client{ - Transport: transport, - // the request should happen quickly. - Timeout: 5 * time.Second, + c.dialContext = proxyTransport.DialContext } // resync on this one because it is low cardinality and rechecking the actual discovery @@ -183,6 +167,34 @@ func (c *AvailableConditionController) sync(key string) error { return err } + // if a particular transport was specified, use that otherwise build one + // construct an http client that will ignore TLS verification (if someone owns the network and messes with your status + // that's not so bad) and sets a very short timeout. This is a best effort GET that provides no additional information + restConfig := &rest.Config{ + TLSClientConfig: rest.TLSClientConfig{ + Insecure: true, + }, + } + + if c.proxyCurrentCertKeyContent != nil { + proxyClientCert, proxyClientKey := c.proxyCurrentCertKeyContent() + + restConfig.TLSClientConfig.CertData = proxyClientCert + restConfig.TLSClientConfig.KeyData = proxyClientKey + } + if c.dialContext != nil { + restConfig.Dial = c.dialContext + } + restTransport, err := rest.TransportFor(restConfig) + if err != nil { + return err + } + discoveryClient := &http.Client{ + Transport: restTransport, + // the request should happen quickly. + Timeout: 5 * time.Second, + } + apiService := originalAPIService.DeepCopy() availableCondition := apiregistrationv1.APIServiceCondition{ @@ -303,7 +315,7 @@ func (c *AvailableConditionController) sync(key string) error { // setting the system-masters identity ensures that we will always have access rights transport.SetAuthProxyHeaders(newReq, "system:kube-aggregator", []string{"system:masters"}, nil) - resp, err := c.discoveryClient.Do(newReq) + resp, err := discoveryClient.Do(newReq) if resp != nil { resp.Body.Close() // we should always been in the 200s or 300s diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go index af1f94166f9..ff389b5bf97 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go @@ -18,7 +18,6 @@ package apiserver import ( "fmt" - "k8s.io/utils/pointer" "net/http" "net/http/httptest" "net/url" @@ -26,6 +25,8 @@ import ( "testing" "time" + "k8s.io/utils/pointer" + "github.com/davecgh/go-spew/spew" v1 "k8s.io/api/core/v1" @@ -125,7 +126,6 @@ func setupAPIServices(apiServices []*apiregistration.APIService) (*AvailableCond apiServiceLister: listers.NewAPIServiceLister(apiServiceIndexer), serviceLister: v1listers.NewServiceLister(serviceIndexer), endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer), - discoveryClient: testServer.Client(), serviceResolver: &fakeServiceResolver{url: testServer.URL}, queue: workqueue.NewNamedRateLimitingQueue( // We want a fairly tight requeue time. The controller listens to the API, but because it relies on the routability of the @@ -350,12 +350,12 @@ func TestSync(t *testing.T) { defer testServer.Close() c := AvailableConditionController{ - apiServiceClient: fakeClient.ApiregistrationV1(), - apiServiceLister: listers.NewAPIServiceLister(apiServiceIndexer), - serviceLister: v1listers.NewServiceLister(serviceIndexer), - endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer), - discoveryClient: testServer.Client(), - serviceResolver: &fakeServiceResolver{url: testServer.URL}, + apiServiceClient: fakeClient.ApiregistrationV1(), + apiServiceLister: listers.NewAPIServiceLister(apiServiceIndexer), + serviceLister: v1listers.NewServiceLister(serviceIndexer), + endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer), + serviceResolver: &fakeServiceResolver{url: testServer.URL}, + proxyCurrentCertKeyContent: func() ([]byte, []byte) { return emptyCert(), emptyCert() }, } c.sync(tc.apiServiceName) @@ -415,5 +415,8 @@ func TestUpdateAPIServiceStatus(t *testing.T) { if e, a := 1, len(fakeClient.Actions()); e != a { t.Error(spew.Sdump(fakeClient.Actions())) } - +} + +func emptyCert() []byte { + return []byte{} }