Skip to content

Commit e16ed47

Browse files
committed
pkg/cvo/metrics: Do not require auth when --hypershift is set
In 313f8fb (CVO protects /metrics with authorization, 2025-07-22, #1215) and 833a491 (CVO protects /metrics with authorization, 2025-07-22, #1215), the /metrics endpoint began requiring client auth. The only authentication system was Bearer tokens, and the only authorization system was validating that the token belonged to system:serviceaccount:openshift-monitoring:prometheus-k8s. That worked well for standalone clusters, where the ServiceMonitor scraper is the Prometheus from the openshift-monitoring namespace. But it broke scraping on HyperShift [1], where the ServiceMonitor does not request any client authorization [2]. Getting ServiceAccount tokens (and keeping them fresh [3]) from the hosted cluster into a Prometheus scraper running on the management cluster is hard. This commit buys time to sort out a HyperShift metrics authentication strategy by wiring the existing --hypershift option to code that disables the authentication requirement in that environment. Standalone clusters will continue to require prometheus-k8s ServiceAccount tokens.
1 parent 95685e0 commit e16ed47

File tree

2 files changed

+39
-30
lines changed

2 files changed

+39
-30
lines changed

pkg/cvo/metrics.go

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,12 @@ type asyncResult struct {
133133
}
134134

135135
func createHttpServer(ctx context.Context, client *authenticationclientsetv1.AuthenticationV1Client) *http.Server {
136-
auth := authHandler{downstream: promhttp.Handler(), ctx: ctx, client: client.TokenReviews()}
136+
auth := &authHandler{downstream: promhttp.Handler(), ctx: ctx}
137+
if client != nil {
138+
auth.client = client.TokenReviews()
139+
}
137140
handler := http.NewServeMux()
138-
handler.Handle("/metrics", &auth)
141+
handler.Handle("/metrics", auth)
139142
server := &http.Server{
140143
Handler: handler,
141144
}
@@ -173,30 +176,32 @@ func (a *authHandler) authorize(token string) (bool, error) {
173176
}
174177

175178
func (a *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
176-
authHeader := r.Header.Get("Authorization")
177-
if authHeader == "" {
178-
http.Error(w, "failed to get the Authorization header", http.StatusUnauthorized)
179-
return
180-
}
181-
token := strings.TrimPrefix(authHeader, "Bearer ")
182-
if token == "" {
183-
http.Error(w, "empty Bearer token", http.StatusUnauthorized)
184-
return
185-
}
186-
if token == authHeader {
187-
http.Error(w, "failed to get the Bearer token", http.StatusUnauthorized)
188-
return
189-
}
179+
if a.client != nil {
180+
authHeader := r.Header.Get("Authorization")
181+
if authHeader == "" {
182+
http.Error(w, "failed to get the Authorization header", http.StatusUnauthorized)
183+
return
184+
}
185+
token := strings.TrimPrefix(authHeader, "Bearer ")
186+
if token == "" {
187+
http.Error(w, "empty Bearer token", http.StatusUnauthorized)
188+
return
189+
}
190+
if token == authHeader {
191+
http.Error(w, "failed to get the Bearer token", http.StatusUnauthorized)
192+
return
193+
}
190194

191-
authorized, err := a.authorize(token)
192-
if err != nil {
193-
klog.Warningf("Failed to authorize token: %v", err)
194-
http.Error(w, "failed to authorize due to an internal error", http.StatusInternalServerError)
195-
return
196-
}
197-
if !authorized {
198-
http.Error(w, "failed to authorize", http.StatusUnauthorized)
199-
return
195+
authorized, err := a.authorize(token)
196+
if err != nil {
197+
klog.Warningf("Failed to authorize token: %v", err)
198+
http.Error(w, "failed to authorize due to an internal error", http.StatusInternalServerError)
199+
return
200+
}
201+
if !authorized {
202+
http.Error(w, "failed to authorize", http.StatusUnauthorized)
203+
return
204+
}
200205
}
201206
a.downstream.ServeHTTP(w, r)
202207
}
@@ -246,7 +251,7 @@ func handleServerResult(result asyncResult, lastLoopError error) error {
246251
// Also detects changes to metrics certificate files upon which
247252
// the metrics HTTP server is shutdown and recreated with a new
248253
// TLS configuration.
249-
func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress, certFile, keyFile string, restConfig *rest.Config) error {
254+
func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress, certFile, keyFile string, hyperShift bool, restConfig *rest.Config) error {
250255
var tlsConfig *tls.Config
251256
if listenAddress != "" {
252257
var err error
@@ -258,9 +263,13 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
258263
return errors.New("TLS configuration is required to serve metrics")
259264
}
260265

261-
client, err := authenticationclientsetv1.NewForConfig(restConfig)
262-
if err != nil {
263-
return fmt.Errorf("failed to create config: %w", err)
266+
var client *authenticationclientsetv1.AuthenticationV1Client
267+
if !hypershift {
268+
var err error
269+
client, err = authenticationclientsetv1.NewForConfig(restConfig)
270+
if err != nil {
271+
return fmt.Errorf("failed to create config: %w", err)
272+
}
264273
}
265274

266275
server := createHttpServer(runContext, client)

pkg/start/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ func (o *Options) run(ctx context.Context, controllerCtx *Context, lock resource
350350
resultChannelCount++
351351
go func() {
352352
defer utilruntime.HandleCrash()
353-
err := cvo.RunMetrics(postMainContext, shutdownContext, o.ListenAddr, o.ServingCertFile, o.ServingKeyFile, restConfig)
353+
err := cvo.RunMetrics(postMainContext, shutdownContext, o.ListenAddr, o.ServingCertFile, o.ServingKeyFile, o.HyperShift, restConfig)
354354
resultChannel <- asyncResult{name: "metrics server", error: err}
355355
}()
356356
}

0 commit comments

Comments
 (0)