@@ -3,6 +3,7 @@ package cvo
33import (
44 "context"
55 "crypto/tls"
6+ "crypto/x509"
67 "errors"
78 "fmt"
89 "net"
@@ -125,52 +126,101 @@ type asyncResult struct {
125126 error error
126127}
127128
128- func createHttpServer (disableAuth bool ) * http.Server {
129- if disableAuth {
129+ func createHttpServer (options MetricsOptions , clientCA dynamiccertificates. CAContentProvider ) * http.Server {
130+ if options . DisableAuthentication && options . DisableAuthorization {
130131 handler := http .NewServeMux ()
131132 handler .Handle ("/metrics" , promhttp .Handler ())
132- server := & http.Server {
133- Handler : handler ,
134- }
135- return server
133+ return & http.Server {Handler : handler }
136134 }
137135
138- auth := authHandler {downstream : promhttp .Handler ()}
136+ auth := authHandler {
137+ downstream : promhttp .Handler (),
138+ clientCA : clientCA ,
139+ enableAuthentication : ! options .DisableAuthentication ,
140+ enableAuthorization : ! options .DisableAuthorization ,
141+ }
139142 handler := http .NewServeMux ()
140143 handler .Handle ("/metrics" , & auth )
141- server := & http.Server {
142- Handler : handler ,
143- }
144- return server
144+ return & http.Server {Handler : handler }
145145}
146146
147147type authHandler struct {
148- downstream http.Handler
148+ downstream http.Handler
149+ clientCA dynamiccertificates.CAContentProvider
150+ enableAuthentication bool
151+ enableAuthorization bool
149152}
150153
154+ // ServeHTTP performs application-level authentication and authorization.
151155func (a * authHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
156+ if ! a .enableAuthentication && ! a .enableAuthorization {
157+ a .downstream .ServeHTTP (w , r )
158+ return
159+ }
160+
161+ // Both authentication and authorization require a client certificate
152162 if r .TLS == nil || len (r .TLS .PeerCertificates ) == 0 {
153163 klog .V (4 ).Info ("Client certificate required but not provided" )
154164 http .Error (w , "client certificate required" , http .StatusUnauthorized )
155165 return
156166 }
157167
168+ if a .enableAuthentication && ! a .authenticate (w , r ) {
169+ return
170+ }
171+
172+ if a .enableAuthorization && ! a .authorize (w , r ) {
173+ return
174+ }
175+
176+ a .downstream .ServeHTTP (w , r )
177+ }
178+
179+ // authenticate verifies the client certificate chain against the configured CA.
180+ // Returns true if authenticated successfully, false otherwise (and writes HTTP error).
181+ func (a * authHandler ) authenticate (w http.ResponseWriter , r * http.Request ) bool {
182+ opts , ok := a .clientCA .VerifyOptions ()
183+ if ! ok {
184+ klog .Error ("verify options from client CA provider could not be loaded" )
185+ http .Error (w , "internal server error" , http .StatusInternalServerError )
186+ return false
187+ }
188+
189+ if len (r .TLS .PeerCertificates ) > 1 {
190+ intermediates := x509 .NewCertPool ()
191+ for _ , cert := range r .TLS .PeerCertificates [1 :] {
192+ intermediates .AddCert (cert )
193+ }
194+ opts .Intermediates = intermediates
195+ }
196+
197+ if _ , err := r .TLS .PeerCertificates [0 ].Verify (opts ); err != nil {
198+ klog .V (4 ).Infof ("Client certificate verification failed: %v" , err )
199+ http .Error (w , "client certificate not trusted" , http .StatusUnauthorized )
200+ return false
201+ }
202+
203+ return true
204+ }
205+
206+ // authorize verifies the client certificate CN against the allowed CN.
207+ // Returns true if authorized, false otherwise (and writes HTTP error).
208+ func (a * authHandler ) authorize (w http.ResponseWriter , r * http.Request ) bool {
158209 // metricsAllowedClientCommonName is the Common Name (CN) of the client certificate
159210 // that is authorized to access the metrics endpoint. This corresponds to the
160211 // well-known Prometheus service account in OpenShift monitoring.
161212 // See: https://github.com/openshift/enhancements/blob/master/CONVENTIONS.md#metrics
162213 metricsAllowedClientCommonName := "system:serviceaccount:openshift-monitoring:prometheus-k8s"
163214
164- // The first element is the leaf certificate that the connection is verified against
165215 commonName := r .TLS .PeerCertificates [0 ].Subject .CommonName
166216 if commonName != metricsAllowedClientCommonName {
167217 klog .V (4 ).Infof ("Access denied for common name: %s" , commonName )
168218 http .Error (w , fmt .Sprintf ("unauthorized common name: %s" , commonName ), http .StatusForbidden )
169- return
219+ return false
170220 }
171221
172222 klog .V (5 ).Infof ("Access granted for common name: %s" , commonName )
173- a . downstream . ServeHTTP ( w , r )
223+ return true
174224}
175225
176226func startListening (svr * http.Server , tlsConfig * tls.Config , lAddr string , resultChannel chan asyncResult ) {
@@ -288,8 +338,10 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, res
288338 // Assign to interface variable to ensure proper nil handling
289339 clientCA = clientCAController
290340
291- // Enforce mTLS
292- clientAuth = tls .RequireAndVerifyClientCert
341+ // Request client certificates but don't verify at TLS layer.
342+ // Verification happens in the HTTP handler, which allows returning
343+ // HTTP error codes that are expected by the origin test suite, instead of TLS errors.
344+ clientAuth = tls .RequestClientCert
293345 }
294346
295347 // Log certificate controller events to stdout because the controller is reported to generate invalid events,
@@ -327,7 +379,7 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, res
327379 resultChannel <- asyncResult {name : "serving certification controller" }
328380 }()
329381
330- server := createHttpServer (options . DisableAuthorization )
382+ server := createHttpServer (options , clientCA )
331383 tlsConfig := crypto .SecureTLSConfig (& tls.Config {
332384 GetConfigForClient : func (clientHello * tls.ClientHelloInfo ) (* tls.Config , error ) {
333385 config , err := servingCertController .GetConfigForClient (clientHello )
0 commit comments