Skip to content
Prev Previous commit
Next Next commit
pkg/cvo/metrics: Use mTLS
Assisted-by: Claude Code
  • Loading branch information
DavidHurta committed Dec 12, 2025
commit 35e5cabdb63ed97f53c45ab90b8e50f1edd0e055
63 changes: 38 additions & 25 deletions pkg/cvo/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
"k8s.io/client-go/kubernetes"
authenticationclientsetv1 "k8s.io/client-go/kubernetes/typed/authentication/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -245,32 +246,62 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
}

// Create a dynamic serving cert/key controller to watch for serving certificate changes from files.
servingCertController, err := dynamiccertificates.NewDynamicServingContentFromFiles("metrics-serving-cert", certFile, keyFile)
servingContentController, err := dynamiccertificates.NewDynamicServingContentFromFiles("metrics-serving-cert", certFile, keyFile)
if err != nil {
return fmt.Errorf("failed to create serving certificate controller: %w", err)
}

// Start the serving cert controller to begin watching the cert and key files
go servingCertController.Run(runContext, 1)
go servingContentController.Run(runContext, 1)

// Create TLS config using the controllers. The config uses callbacks to dynamically
// fetch the latest certificates and CA bundles on each connection, so no server
// restart is needed when certificates change.
tlsConfig, err := makeTLSConfig(servingCertController)
// Create a dynamic CA controller to watch for client CA changes from a ConfigMap.
kubeClient, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return fmt.Errorf("failed to create TLS config: %w", err)
return fmt.Errorf("failed to create kube client: %w", err)
}

clientCAController, err := dynamiccertificates.NewDynamicCAFromConfigMapController(
"metrics-client-ca",
"kube-system",
"extension-apiserver-authentication",
"client-ca-file",
kubeClient)
if err != nil {
return fmt.Errorf("failed to create client CA controller: %w", err)
}

client, err := authenticationclientsetv1.NewForConfig(restConfig)
if err != nil {
return fmt.Errorf("failed to create config: %w", err)
}

// Start the client CA controller to begin watching the ConfigMap
go clientCAController.Run(runContext, 1)

servingCertController := dynamiccertificates.NewDynamicServingCertificateController(
crypto.SecureTLSConfig(&tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
}),
clientCAController,
servingContentController,
nil,
nil,
)
if err := servingCertController.RunOnce(); err != nil {
return fmt.Errorf("failed to initialize serving certificate controller: %w", err)
}

clientCAController.AddListener(servingCertController)
servingContentController.AddListener(servingCertController)

go servingCertController.Run(1, runContext.Done())

server := createHttpServer(runContext, client, disableMetricsAuth)

resultChannel := make(chan asyncResult, 1)
resultChannelCount := 1

tlsConfig := &tls.Config{GetConfigForClient: servingCertController.GetConfigForClient}
go startListening(server, tlsConfig, listenAddress, resultChannel)

// Wait for server to exit or shutdown signal
Expand Down Expand Up @@ -628,21 +659,3 @@ func mostRecentTimestamp(cv *configv1.ClusterVersion) int64 {
}
return latest.Unix()
}

func makeTLSConfig(servingCertController dynamiccertificates.CertKeyContentProvider) (*tls.Config, error) {
_, err := tls.X509KeyPair(servingCertController.CurrentCertKeyContent())
if err != nil {
return nil, fmt.Errorf("failed to create X509 key pair: %w", err)
}
tlsConfig := crypto.SecureTLSConfig(&tls.Config{
GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
cert, err := tls.X509KeyPair(servingCertController.CurrentCertKeyContent())
if err != nil {
klog.Errorf("Failed to load current serving certificate, rejecting connection: %v", err)
return nil, fmt.Errorf("invalid serving certificate: %w", err)
}
return &cert, nil
},
})
return tlsConfig, nil
}