@@ -3,7 +3,6 @@ package cvo
33import (
44 "context"
55 "crypto/tls"
6- "crypto/x509"
76 "errors"
87 "fmt"
98 "net"
@@ -211,13 +210,13 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
211210 }
212211
213212 // Create a dynamic serving cert/key controller to watch for serving certificate changes from files.
214- servingCertController , err := dynamiccertificates .NewDynamicServingContentFromFiles ("metrics-serving-cert" , certFile , keyFile )
213+ servingContentController , err := dynamiccertificates .NewDynamicServingContentFromFiles ("metrics-serving-cert" , certFile , keyFile )
215214 if err != nil {
216215 return fmt .Errorf ("failed to create serving certificate controller: %w" , err )
217216 }
218217
219218 // Start the serving cert controller to begin watching the cert and key files
220- go servingCertController .Run (runContext , 1 )
219+ go servingContentController .Run (runContext , 1 )
221220
222221 // Create a dynamic CA controller to watch for client CA changes from a ConfigMap.
223222 kubeClient , err := kubernetes .NewForConfig (restConfig )
@@ -238,19 +237,30 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
238237 // Start the client CA controller to begin watching the ConfigMap
239238 go clientCAController .Run (runContext , 1 )
240239
241- // Create TLS config using the controllers. The config uses callbacks to dynamically
242- // fetch the latest certificates and CA bundles on each connection, so no server
243- // restart is needed when certificates change.
244- tlsConfig , err := makeTLSConfig (servingCertController , clientCAController )
245- if err != nil {
246- return fmt .Errorf ("failed to create TLS config: %w" , err )
240+ servingCertController := dynamiccertificates .NewDynamicServingCertificateController (
241+ crypto .SecureTLSConfig (& tls.Config {
242+ ClientAuth : tls .RequireAndVerifyClientCert ,
243+ }),
244+ clientCAController ,
245+ servingContentController ,
246+ nil ,
247+ nil ,
248+ )
249+ if err := servingCertController .RunOnce (); err != nil {
250+ return fmt .Errorf ("failed to initialize serving certificate controller: %w" , err )
247251 }
248252
253+ clientCAController .AddListener (servingCertController )
254+ servingContentController .AddListener (servingCertController )
255+
256+ go servingCertController .Run (1 , runContext .Done ())
257+
249258 server := createHttpServer (disableMetricsAuth )
250259
251260 resultChannel := make (chan asyncResult , 1 )
252261 resultChannelCount := 1
253262
263+ tlsConfig := & tls.Config {GetConfigForClient : servingCertController .GetConfigForClient }
254264 go startListening (server , tlsConfig , listenAddress , resultChannel )
255265
256266 // Wait for server to exit or shutdown signal
@@ -608,65 +618,3 @@ func mostRecentTimestamp(cv *configv1.ClusterVersion) int64 {
608618 }
609619 return latest .Unix ()
610620}
611-
612- func makeTLSConfig (servingCertController dynamiccertificates.CertKeyContentProvider , clientCAController dynamiccertificates.CAContentProvider ) (* tls.Config , error ) {
613- // Get the current certificate and key content from the controller for validation.
614- _ , err := tls .X509KeyPair (servingCertController .CurrentCertKeyContent ())
615- if err != nil {
616- return nil , fmt .Errorf ("failed to create X509 key pair: %w" , err )
617- }
618-
619- tlsConfig := crypto .SecureTLSConfig (& tls.Config {
620- GetCertificate : func (_ * tls.ClientHelloInfo ) (* tls.Certificate , error ) {
621- // Always get the latest certificate from the controller on each TLS handshake.
622- // This ensures that certificate rotations are picked up immediately without server restart.
623- cert , err := tls .X509KeyPair (servingCertController .CurrentCertKeyContent ())
624- if err != nil {
625- // SECURITY: Never return a stale/cached certificate on error.
626- // Fail the connection rather than risk using an expired or invalid certificate.
627- klog .Errorf ("Failed to load current serving certificate, rejecting connection: %v" , err )
628- return nil , fmt .Errorf ("invalid serving certificate: %w" , err )
629- }
630- return & cert , nil
631- },
632- GetConfigForClient : func (_ * tls.ClientHelloInfo ) (* tls.Config , error ) {
633- // Create a fresh config with the latest CA bundle for each client connection.
634- // This ensures CA bundle updates are picked up immediately without server restart.
635- caBundle := clientCAController .CurrentCABundleContent ()
636- if len (caBundle ) == 0 {
637- // SECURITY: If CA bundle is not available, reject connections.
638- // This enforces mTLS and prevents the server from operating in an insecure mode.
639- klog .Errorf ("No client CA bundle available, rejecting connection to enforce mTLS" )
640- return nil , fmt .Errorf ("client CA bundle not available" )
641- }
642-
643- certPool := x509 .NewCertPool ()
644- if ! certPool .AppendCertsFromPEM (caBundle ) {
645- // SECURITY: If CA bundle is present but invalid, reject the connection.
646- // This prevents a downgrade attack where corrupted CA data disables mTLS.
647- klog .Errorf ("Failed to parse client CA bundle, rejecting connection to enforce mTLS" )
648- return nil , fmt .Errorf ("invalid client CA bundle" )
649- }
650-
651- return & tls.Config {
652- ClientCAs : certPool ,
653- ClientAuth : tls .RequireAndVerifyClientCert ,
654- }, nil
655- },
656- })
657-
658- // Log the initial mTLS state
659- caBundle := clientCAController .CurrentCABundleContent ()
660- if len (caBundle ) > 0 {
661- certPool := x509 .NewCertPool ()
662- if certPool .AppendCertsFromPEM (caBundle ) {
663- klog .Infof ("Configured mTLS with dynamic client CA verification" )
664- } else {
665- klog .Warningf ("Client CA bundle present but invalid, mTLS will reject all connections until fixed" )
666- }
667- } else {
668- klog .Warningf ("No client CA bundle available yet, mTLS will reject all connections until ConfigMap is created" )
669- }
670-
671- return tlsConfig , nil
672- }
0 commit comments