k8s.io/apiserver@v0.31.1/plugin/pkg/authenticator/token/oidc/metrics.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package oidc
    18  
    19  import (
    20  	"context"
    21  	"crypto/sha256"
    22  	"fmt"
    23  	"sync"
    24  	"time"
    25  
    26  	"k8s.io/apiserver/pkg/authentication/authenticator"
    27  	"k8s.io/component-base/metrics"
    28  	"k8s.io/component-base/metrics/legacyregistry"
    29  	"k8s.io/utils/clock"
    30  )
    31  
    32  const (
    33  	namespace = "apiserver"
    34  	subsystem = "authentication"
    35  )
    36  
    37  var (
    38  	jwtAuthenticatorLatencyMetric = metrics.NewHistogramVec(
    39  		&metrics.HistogramOpts{
    40  			Namespace:      namespace,
    41  			Subsystem:      subsystem,
    42  			Name:           "jwt_authenticator_latency_seconds",
    43  			Help:           "Latency of jwt authentication operations in seconds. This is the time spent authenticating a token for cache miss only (i.e. when the token is not found in the cache).",
    44  			StabilityLevel: metrics.ALPHA,
    45  			// default histogram buckets with a 1ms starting point
    46  			Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
    47  		},
    48  		[]string{"result", "jwt_issuer_hash"},
    49  	)
    50  )
    51  
    52  var registerMetrics sync.Once
    53  
    54  func RegisterMetrics() {
    55  	registerMetrics.Do(func() {
    56  		legacyregistry.MustRegister(jwtAuthenticatorLatencyMetric)
    57  	})
    58  }
    59  
    60  func recordAuthenticationLatency(result, jwtIssuerHash string, duration time.Duration) {
    61  	jwtAuthenticatorLatencyMetric.WithLabelValues(result, jwtIssuerHash).Observe(duration.Seconds())
    62  }
    63  
    64  func getHash(data string) string {
    65  	if len(data) > 0 {
    66  		return fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(data)))
    67  	}
    68  	return ""
    69  }
    70  
    71  func newInstrumentedAuthenticator(jwtIssuer string, delegate AuthenticatorTokenWithHealthCheck) AuthenticatorTokenWithHealthCheck {
    72  	return newInstrumentedAuthenticatorWithClock(jwtIssuer, delegate, clock.RealClock{})
    73  }
    74  
    75  func newInstrumentedAuthenticatorWithClock(jwtIssuer string, delegate AuthenticatorTokenWithHealthCheck, clock clock.PassiveClock) *instrumentedAuthenticator {
    76  	RegisterMetrics()
    77  	return &instrumentedAuthenticator{
    78  		jwtIssuerHash: getHash(jwtIssuer),
    79  		delegate:      delegate,
    80  		clock:         clock,
    81  	}
    82  }
    83  
    84  type instrumentedAuthenticator struct {
    85  	jwtIssuerHash string
    86  	delegate      AuthenticatorTokenWithHealthCheck
    87  	clock         clock.PassiveClock
    88  }
    89  
    90  func (a *instrumentedAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
    91  	start := a.clock.Now()
    92  	response, ok, err := a.delegate.AuthenticateToken(ctx, token)
    93  	// this only happens when issuer doesn't match the authenticator
    94  	// we don't want to record metrics for this case
    95  	if !ok && err == nil {
    96  		return response, ok, err
    97  	}
    98  
    99  	duration := a.clock.Since(start)
   100  	if err != nil {
   101  		recordAuthenticationLatency("failure", a.jwtIssuerHash, duration)
   102  	} else {
   103  		recordAuthenticationLatency("success", a.jwtIssuerHash, duration)
   104  	}
   105  	return response, ok, err
   106  }
   107  
   108  func (a *instrumentedAuthenticator) HealthCheck() error {
   109  	return a.delegate.HealthCheck()
   110  }