@@ -19,6 +19,7 @@ import (
1919 "k8s.io/apimachinery/pkg/labels"
2020 "k8s.io/apimachinery/pkg/util/sets"
2121 "k8s.io/apiserver/pkg/server/dynamiccertificates"
22+ "k8s.io/client-go/kubernetes"
2223 authenticationclientsetv1 "k8s.io/client-go/kubernetes/typed/authentication/v1"
2324 "k8s.io/client-go/rest"
2425 "k8s.io/client-go/tools/cache"
@@ -252,35 +253,93 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
252253 resultChannelCount := 0
253254
254255 // Create a dynamic serving cert/key controller to watch for serving certificate changes from files.
255- servingCertController , err := dynamiccertificates .NewDynamicServingContentFromFiles ("metrics-serving-cert" , certFile , keyFile )
256+ servingContentController , err := dynamiccertificates .NewDynamicServingContentFromFiles ("metrics-serving-cert" , certFile , keyFile )
256257 if err != nil {
257258 return fmt .Errorf ("failed to create serving certificate controller: %w" , err )
258259 }
259- if err := servingCertController .RunOnce (metricsContext ); err != nil {
260+ if err := servingContentController .RunOnce (metricsContext ); err != nil {
260261 return fmt .Errorf ("failed to initialize serving content controller: %w" , err )
261262 }
262263
263264 // Start the serving cert controller to begin watching the cert and key files
264265 resultChannelCount ++
265266 go func () {
266- servingCertController .Run (metricsContext , 1 )
267+ servingContentController .Run (metricsContext , 1 )
267268 resultChannel <- asyncResult {name : "serving content controller" }
268269 }()
269270
270- // Create TLS config using the controllers. The config uses callbacks to dynamically
271- // fetch the latest certificates and CA bundles on each connection, so no server
272- // restart is needed when certificates change.
273- tlsConfig , err := makeTLSConfig (servingCertController )
271+ // Create a dynamic CA controller to watch for client CA changes from a ConfigMap.
272+ kubeClient , err := kubernetes .NewForConfig (restConfig )
274273 if err != nil {
275- return fmt .Errorf ("failed to create TLS config: %w" , err )
274+ return fmt .Errorf ("failed to create kube client: %w" , err )
275+ }
276+
277+ clientCAController , err := dynamiccertificates .NewDynamicCAFromConfigMapController (
278+ "metrics-client-ca" ,
279+ "kube-system" ,
280+ "extension-apiserver-authentication" ,
281+ "client-ca-file" ,
282+ kubeClient )
283+ if err != nil {
284+ return fmt .Errorf ("failed to create client CA controller: %w" , err )
285+ }
286+
287+ if err := clientCAController .RunOnce (metricsContext ); err != nil {
288+ return fmt .Errorf ("failed to initialize client CA controller: %w" , err )
276289 }
277290
278291 client , err := authenticationclientsetv1 .NewForConfig (restConfig )
279292 if err != nil {
280293 return fmt .Errorf ("failed to create config: %w" , err )
281294 }
282295
296+ // Start the client CA controller to begin watching the ConfigMap
297+ resultChannelCount ++
298+ go func () {
299+ clientCAController .Run (metricsContext , 1 )
300+ resultChannel <- asyncResult {name : "client CA from ConfigMap controller" }
301+ }()
302+
303+ servingCertController := dynamiccertificates .NewDynamicServingCertificateController (
304+ crypto .SecureTLSConfig (& tls.Config {
305+ ClientAuth : tls .RequireAndVerifyClientCert ,
306+ }),
307+ clientCAController ,
308+ servingContentController ,
309+ nil ,
310+ nil ,
311+ )
312+ if err := servingCertController .RunOnce (); err != nil {
313+ return fmt .Errorf ("failed to initialize serving certificate controller: %w" , err )
314+ }
315+
316+ // Register listeners so servingCertController is notified when certificates change.
317+ if clientCAController != nil {
318+ clientCAController .AddListener (servingCertController )
319+ }
320+ servingContentController .AddListener (servingCertController )
321+
322+ resultChannelCount ++
323+ go func () {
324+ servingCertController .Run (1 , metricsContext .Done ())
325+ resultChannel <- asyncResult {name : "serving certification controller" }
326+ }()
327+
283328 server := createHttpServer (metricsContext , client , disableMetricsAuth )
329+ tlsConfig := crypto .SecureTLSConfig (& tls.Config {
330+ GetConfigForClient : func (clientHello * tls.ClientHelloInfo ) (* tls.Config , error ) {
331+ config , err := servingCertController .GetConfigForClient (clientHello )
332+ if err != nil {
333+ return nil , err
334+ }
335+ if config == nil {
336+ // To ensure we rather safely fail connections when the desired config is nil. Safety over availability.
337+ err := fmt .Errorf ("serving certificate controller returned nil TLS configuration" )
338+ return nil , err
339+ }
340+ return config , nil
341+ },
342+ })
284343
285344 resultChannelCount ++
286345 go func () {
@@ -659,21 +718,3 @@ func mostRecentTimestamp(cv *configv1.ClusterVersion) int64 {
659718 }
660719 return latest .Unix ()
661720}
662-
663- func makeTLSConfig (servingCertController dynamiccertificates.CertKeyContentProvider ) (* tls.Config , error ) {
664- _ , err := tls .X509KeyPair (servingCertController .CurrentCertKeyContent ())
665- if err != nil {
666- return nil , fmt .Errorf ("failed to create X509 key pair: %w" , err )
667- }
668- tlsConfig := crypto .SecureTLSConfig (& tls.Config {
669- GetCertificate : func (_ * tls.ClientHelloInfo ) (* tls.Certificate , error ) {
670- cert , err := tls .X509KeyPair (servingCertController .CurrentCertKeyContent ())
671- if err != nil {
672- klog .Errorf ("Failed to load current serving certificate, rejecting connection: %v" , err )
673- return nil , fmt .Errorf ("invalid serving certificate: %w" , err )
674- }
675- return & cert , nil
676- },
677- })
678- return tlsConfig , nil
679- }
0 commit comments