mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	adds metrics for authentication webhook
This commit is contained in:
		@@ -41,11 +41,11 @@ import (
 | 
				
			|||||||
func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubeletconfig.KubeletConfiguration) (server.AuthInterface, func(<-chan struct{}), error) {
 | 
					func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubeletconfig.KubeletConfiguration) (server.AuthInterface, func(<-chan struct{}), error) {
 | 
				
			||||||
	// Get clients, if provided
 | 
						// Get clients, if provided
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		tokenClient authenticationclient.TokenReviewInterface
 | 
							tokenClient authenticationclient.AuthenticationV1Interface
 | 
				
			||||||
		sarClient   authorizationclient.SubjectAccessReviewInterface
 | 
							sarClient   authorizationclient.SubjectAccessReviewInterface
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if client != nil && !reflect.ValueOf(client).IsNil() {
 | 
						if client != nil && !reflect.ValueOf(client).IsNil() {
 | 
				
			||||||
		tokenClient = client.AuthenticationV1().TokenReviews()
 | 
							tokenClient = client.AuthenticationV1()
 | 
				
			||||||
		sarClient = client.AuthorizationV1().SubjectAccessReviews()
 | 
							sarClient = client.AuthorizationV1().SubjectAccessReviews()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,7 +65,7 @@ func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubel
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BuildAuthn creates an authenticator compatible with the kubelet's needs
 | 
					// BuildAuthn creates an authenticator compatible with the kubelet's needs
 | 
				
			||||||
func BuildAuthn(client authenticationclient.TokenReviewInterface, authn kubeletconfig.KubeletAuthentication) (authenticator.Request, func(<-chan struct{}), error) {
 | 
					func BuildAuthn(client authenticationclient.AuthenticationV1Interface, authn kubeletconfig.KubeletAuthentication) (authenticator.Request, func(<-chan struct{}), error) {
 | 
				
			||||||
	var dynamicCAContentFromFile *dynamiccertificates.DynamicFileCAContent
 | 
						var dynamicCAContentFromFile *dynamiccertificates.DynamicFileCAContent
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if len(authn.X509.ClientCAFile) > 0 {
 | 
						if len(authn.X509.ClientCAFile) > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,7 @@ type DelegatingAuthenticatorConfig struct {
 | 
				
			|||||||
	Anonymous bool
 | 
						Anonymous bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored.
 | 
						// TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored.
 | 
				
			||||||
	TokenAccessReviewClient authenticationclient.TokenReviewInterface
 | 
						TokenAccessReviewClient authenticationclient.AuthenticationV1Interface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TokenAccessReviewTimeout specifies a time limit for requests made by the authorization webhook client.
 | 
						// TokenAccessReviewTimeout specifies a time limit for requests made by the authorization webhook client.
 | 
				
			||||||
	TokenAccessReviewTimeout time.Duration
 | 
						TokenAccessReviewTimeout time.Duration
 | 
				
			||||||
@@ -91,7 +91,10 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
 | 
				
			|||||||
		if c.WebhookRetryBackoff == nil {
 | 
							if c.WebhookRetryBackoff == nil {
 | 
				
			||||||
			return nil, nil, errors.New("retry backoff parameters for delegating authentication webhook has not been specified")
 | 
								return nil, nil, errors.New("retry backoff parameters for delegating authentication webhook has not been specified")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		tokenAuth, err := webhooktoken.NewFromInterface(c.TokenAccessReviewClient, c.APIAudiences, *c.WebhookRetryBackoff, c.TokenAccessReviewTimeout)
 | 
							tokenAuth, err := webhooktoken.NewFromInterface(c.TokenAccessReviewClient, c.APIAudiences, *c.WebhookRetryBackoff, c.TokenAccessReviewTimeout, webhooktoken.AuthenticatorMetrics{
 | 
				
			||||||
 | 
								RecordRequestTotal:   RecordRequestTotal,
 | 
				
			||||||
 | 
								RecordRequestLatency: RecordRequestLatency,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2021 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package authenticatorfactory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						compbasemetrics "k8s.io/component-base/metrics"
 | 
				
			||||||
 | 
						"k8s.io/component-base/metrics/legacyregistry"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type registerables []compbasemetrics.Registerable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// init registers all metrics.
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						for _, metric := range metrics {
 | 
				
			||||||
 | 
							legacyregistry.MustRegister(metric)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						requestTotal = compbasemetrics.NewCounterVec(
 | 
				
			||||||
 | 
							&compbasemetrics.CounterOpts{
 | 
				
			||||||
 | 
								Name:           "apiserver_delegated_authn_request_total",
 | 
				
			||||||
 | 
								Help:           "Number of HTTP requests partitioned by status code.",
 | 
				
			||||||
 | 
								StabilityLevel: compbasemetrics.ALPHA,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							[]string{"code"},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requestLatency = compbasemetrics.NewHistogramVec(
 | 
				
			||||||
 | 
							&compbasemetrics.HistogramOpts{
 | 
				
			||||||
 | 
								Name:           "apiserver_delegated_authn_request_duration_seconds",
 | 
				
			||||||
 | 
								Help:           "Request latency in seconds. Broken down by status code.",
 | 
				
			||||||
 | 
								Buckets:        []float64{0.25, 0.5, 0.7, 1, 1.5, 3, 5, 10},
 | 
				
			||||||
 | 
								StabilityLevel: compbasemetrics.ALPHA,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							[]string{"code"},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						metrics = registerables{
 | 
				
			||||||
 | 
							requestTotal,
 | 
				
			||||||
 | 
							requestLatency,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RecordRequestTotal increments the total number of requests for the delegated authentication.
 | 
				
			||||||
 | 
					func RecordRequestTotal(ctx context.Context, code string) {
 | 
				
			||||||
 | 
						requestTotal.WithContext(ctx).WithLabelValues(code).Inc()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RecordRequestLatency measures request latency in seconds for the delegated authentication. Broken down by status code.
 | 
				
			||||||
 | 
					func RecordRequestLatency(ctx context.Context, code string, latency float64) {
 | 
				
			||||||
 | 
						requestLatency.WithContext(ctx).WithLabelValues(code).Observe(latency)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -295,7 +295,7 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(authenticationInfo *server.Aut
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// configure token review
 | 
						// configure token review
 | 
				
			||||||
	if client != nil {
 | 
						if client != nil {
 | 
				
			||||||
		cfg.TokenAccessReviewClient = client.AuthenticationV1().TokenReviews()
 | 
							cfg.TokenAccessReviewClient = client.AuthenticationV1()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get the clientCA information
 | 
						// get the clientCA information
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2021 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package webhook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AuthenticatorMetrics specifies a set of methods that are used to register various metrics
 | 
				
			||||||
 | 
					type AuthenticatorMetrics struct {
 | 
				
			||||||
 | 
						// RecordRequestTotal increments the total number of requests for webhooks
 | 
				
			||||||
 | 
						RecordRequestTotal func(ctx context.Context, code string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RecordRequestLatency measures request latency in seconds for webhooks. Broken down by status code.
 | 
				
			||||||
 | 
						RecordRequestLatency func(ctx context.Context, code string, latency float64)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type noopMetrics struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (noopMetrics) RequestTotal(context.Context, string)            {}
 | 
				
			||||||
 | 
					func (noopMetrics) RequestLatency(context.Context, string, float64) {}
 | 
				
			||||||
@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2021 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package webhook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAuthenticatorMetrics(t *testing.T) {
 | 
				
			||||||
 | 
						scenarios := []struct {
 | 
				
			||||||
 | 
							name                            string
 | 
				
			||||||
 | 
							clientCert, clientKey, clientCA []byte
 | 
				
			||||||
 | 
							serverCert, serverKey, serverCA []byte
 | 
				
			||||||
 | 
							authnFakeServiceStatusCode      int
 | 
				
			||||||
 | 
							authFakeServiceDeny             bool
 | 
				
			||||||
 | 
							expectedRegisteredStatusCode    string
 | 
				
			||||||
 | 
							wantErr                         bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:       "happy path",
 | 
				
			||||||
 | 
								clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
 | 
				
			||||||
 | 
								serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
 | 
				
			||||||
 | 
								expectedRegisteredStatusCode: "200",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:       "an internal error returned from the webhook",
 | 
				
			||||||
 | 
								clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
 | 
				
			||||||
 | 
								serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
 | 
				
			||||||
 | 
								authnFakeServiceStatusCode:   500,
 | 
				
			||||||
 | 
								expectedRegisteredStatusCode: "500",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:       "incorrect client certificate used, the webhook not called, an error is recorded",
 | 
				
			||||||
 | 
								clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
 | 
				
			||||||
 | 
								serverCert: serverCert, serverKey: serverKey, serverCA: badCACert,
 | 
				
			||||||
 | 
								expectedRegisteredStatusCode: "<error>",
 | 
				
			||||||
 | 
								wantErr:                      true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, scenario := range scenarios {
 | 
				
			||||||
 | 
							t.Run(scenario.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								service := new(mockV1Service)
 | 
				
			||||||
 | 
								service.statusCode = scenario.authnFakeServiceStatusCode
 | 
				
			||||||
 | 
								if service.statusCode == 0 {
 | 
				
			||||||
 | 
									service.statusCode = 200
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								service.allow = !scenario.authFakeServiceDeny
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								server, err := NewV1TestServer(service, scenario.serverCert, scenario.serverKey, scenario.serverCA)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Errorf("%s: failed to create server: %v", scenario.name, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fakeAuthnMetrics := &fakeAuthenticatorMetrics{}
 | 
				
			||||||
 | 
								authnMetrics := AuthenticatorMetrics{
 | 
				
			||||||
 | 
									RecordRequestTotal:   fakeAuthnMetrics.RequestTotal,
 | 
				
			||||||
 | 
									RecordRequestLatency: fakeAuthnMetrics.RequestLatency,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								wh, err := newV1TokenAuthenticator(server.URL, scenario.clientCert, scenario.clientKey, scenario.clientCA, 0, nil, authnMetrics)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Error("failed to create client")
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_, _, err = wh.AuthenticateToken(context.Background(), "t0k3n")
 | 
				
			||||||
 | 
								if scenario.wantErr {
 | 
				
			||||||
 | 
									if err == nil {
 | 
				
			||||||
 | 
										t.Errorf("expected error making authorization request: %v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if fakeAuthnMetrics.totalCode != scenario.expectedRegisteredStatusCode {
 | 
				
			||||||
 | 
									t.Errorf("incorrect status code recorded for RecordRequestTotal method, expected = %v, got %v", scenario.expectedRegisteredStatusCode, fakeAuthnMetrics.totalCode)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if fakeAuthnMetrics.latencyCode != scenario.expectedRegisteredStatusCode {
 | 
				
			||||||
 | 
									t.Errorf("incorrect status code recorded for RecordRequestLatency method, expected = %v, got %v", scenario.expectedRegisteredStatusCode, fakeAuthnMetrics.latencyCode)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fakeAuthenticatorMetrics struct {
 | 
				
			||||||
 | 
						totalCode string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						latency     float64
 | 
				
			||||||
 | 
						latencyCode string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *fakeAuthenticatorMetrics) RequestTotal(_ context.Context, code string) {
 | 
				
			||||||
 | 
						f.totalCode = code
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *fakeAuthenticatorMetrics) RequestLatency(_ context.Context, code string, latency float64) {
 | 
				
			||||||
 | 
						f.latency = latency
 | 
				
			||||||
 | 
						f.latencyCode = code
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,7 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authenticationv1 "k8s.io/api/authentication/v1"
 | 
						authenticationv1 "k8s.io/api/authentication/v1"
 | 
				
			||||||
@@ -35,6 +36,7 @@ import (
 | 
				
			|||||||
	"k8s.io/apiserver/pkg/util/webhook"
 | 
						"k8s.io/apiserver/pkg/util/webhook"
 | 
				
			||||||
	"k8s.io/client-go/kubernetes/scheme"
 | 
						"k8s.io/client-go/kubernetes/scheme"
 | 
				
			||||||
	authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
 | 
						authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
 | 
				
			||||||
 | 
						"k8s.io/client-go/rest"
 | 
				
			||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,7 +50,7 @@ func DefaultRetryBackoff() *wait.Backoff {
 | 
				
			|||||||
var _ authenticator.Token = (*WebhookTokenAuthenticator)(nil)
 | 
					var _ authenticator.Token = (*WebhookTokenAuthenticator)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tokenReviewer interface {
 | 
					type tokenReviewer interface {
 | 
				
			||||||
	Create(ctx context.Context, review *authenticationv1.TokenReview, _ metav1.CreateOptions) (*authenticationv1.TokenReview, error)
 | 
						Create(ctx context.Context, review *authenticationv1.TokenReview, _ metav1.CreateOptions) (*authenticationv1.TokenReview, int, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type WebhookTokenAuthenticator struct {
 | 
					type WebhookTokenAuthenticator struct {
 | 
				
			||||||
@@ -56,14 +58,16 @@ type WebhookTokenAuthenticator struct {
 | 
				
			|||||||
	retryBackoff   wait.Backoff
 | 
						retryBackoff   wait.Backoff
 | 
				
			||||||
	implicitAuds   authenticator.Audiences
 | 
						implicitAuds   authenticator.Audiences
 | 
				
			||||||
	requestTimeout time.Duration
 | 
						requestTimeout time.Duration
 | 
				
			||||||
 | 
						metrics        AuthenticatorMetrics
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewFromInterface creates a webhook authenticator using the given tokenReview
 | 
					// NewFromInterface creates a webhook authenticator using the given tokenReview
 | 
				
			||||||
// client. It is recommend to wrap this authenticator with the token cache
 | 
					// client. It is recommend to wrap this authenticator with the token cache
 | 
				
			||||||
// authenticator implemented in
 | 
					// authenticator implemented in
 | 
				
			||||||
// k8s.io/apiserver/pkg/authentication/token/cache.
 | 
					// k8s.io/apiserver/pkg/authentication/token/cache.
 | 
				
			||||||
func NewFromInterface(tokenReview authenticationv1client.TokenReviewInterface, implicitAuds authenticator.Audiences, retryBackoff wait.Backoff, requestTimeout time.Duration) (*WebhookTokenAuthenticator, error) {
 | 
					func NewFromInterface(tokenReview authenticationv1client.AuthenticationV1Interface, implicitAuds authenticator.Audiences, retryBackoff wait.Backoff, requestTimeout time.Duration, metrics AuthenticatorMetrics) (*WebhookTokenAuthenticator, error) {
 | 
				
			||||||
	return newWithBackoff(tokenReview, retryBackoff, implicitAuds, requestTimeout)
 | 
						tokenReviewClient := &tokenReviewV1Client{tokenReview.RESTClient()}
 | 
				
			||||||
 | 
						return newWithBackoff(tokenReviewClient, retryBackoff, implicitAuds, requestTimeout, metrics)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New creates a new WebhookTokenAuthenticator from the provided kubeconfig
 | 
					// New creates a new WebhookTokenAuthenticator from the provided kubeconfig
 | 
				
			||||||
@@ -75,12 +79,21 @@ func New(kubeConfigFile string, version string, implicitAuds authenticator.Audie
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return newWithBackoff(tokenReview, retryBackoff, implicitAuds, time.Duration(0))
 | 
						return newWithBackoff(tokenReview, retryBackoff, implicitAuds, time.Duration(0), AuthenticatorMetrics{
 | 
				
			||||||
 | 
							RecordRequestTotal:   noopMetrics{}.RequestTotal,
 | 
				
			||||||
 | 
							RecordRequestLatency: noopMetrics{}.RequestLatency,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newWithBackoff allows tests to skip the sleep.
 | 
					// newWithBackoff allows tests to skip the sleep.
 | 
				
			||||||
func newWithBackoff(tokenReview tokenReviewer, retryBackoff wait.Backoff, implicitAuds authenticator.Audiences, requestTimeout time.Duration) (*WebhookTokenAuthenticator, error) {
 | 
					func newWithBackoff(tokenReview tokenReviewer, retryBackoff wait.Backoff, implicitAuds authenticator.Audiences, requestTimeout time.Duration, metrics AuthenticatorMetrics) (*WebhookTokenAuthenticator, error) {
 | 
				
			||||||
	return &WebhookTokenAuthenticator{tokenReview, retryBackoff, implicitAuds, requestTimeout}, nil
 | 
						return &WebhookTokenAuthenticator{
 | 
				
			||||||
 | 
							tokenReview,
 | 
				
			||||||
 | 
							retryBackoff,
 | 
				
			||||||
 | 
							implicitAuds,
 | 
				
			||||||
 | 
							requestTimeout,
 | 
				
			||||||
 | 
							metrics,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AuthenticateToken implements the authenticator.Token interface.
 | 
					// AuthenticateToken implements the authenticator.Token interface.
 | 
				
			||||||
@@ -120,7 +133,22 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token
 | 
				
			|||||||
	// WithExponentialBackoff will return tokenreview create error (tokenReviewErr) if any.
 | 
						// WithExponentialBackoff will return tokenreview create error (tokenReviewErr) if any.
 | 
				
			||||||
	if err := webhook.WithExponentialBackoff(ctx, w.retryBackoff, func() error {
 | 
						if err := webhook.WithExponentialBackoff(ctx, w.retryBackoff, func() error {
 | 
				
			||||||
		var tokenReviewErr error
 | 
							var tokenReviewErr error
 | 
				
			||||||
		result, tokenReviewErr = w.tokenReview.Create(ctx, r, metav1.CreateOptions{})
 | 
							var statusCode int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							start := time.Now()
 | 
				
			||||||
 | 
							result, statusCode, tokenReviewErr = w.tokenReview.Create(ctx, r, metav1.CreateOptions{})
 | 
				
			||||||
 | 
							latency := time.Now().Sub(start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if statusCode != 0 {
 | 
				
			||||||
 | 
								w.metrics.RecordRequestTotal(ctx, strconv.Itoa(statusCode))
 | 
				
			||||||
 | 
								w.metrics.RecordRequestLatency(ctx, strconv.Itoa(statusCode), latency.Seconds())
 | 
				
			||||||
 | 
								return tokenReviewErr
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if tokenReviewErr != nil {
 | 
				
			||||||
 | 
								w.metrics.RecordRequestTotal(ctx, "<error>")
 | 
				
			||||||
 | 
								w.metrics.RecordRequestLatency(ctx, "<error>", latency.Seconds())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return tokenReviewErr
 | 
							return tokenReviewErr
 | 
				
			||||||
	}, webhook.DefaultShouldRetry); err != nil {
 | 
						}, webhook.DefaultShouldRetry); err != nil {
 | 
				
			||||||
		// An error here indicates bad configuration or an outage. Log for debugging.
 | 
							// An error here indicates bad configuration or an outage. Log for debugging.
 | 
				
			||||||
@@ -186,7 +214,7 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, r
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return &tokenReviewV1Client{gw}, nil
 | 
							return &tokenReviewV1ClientGW{gw.RestClient}, nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case authenticationv1beta1.SchemeGroupVersion.Version:
 | 
						case authenticationv1beta1.SchemeGroupVersion.Version:
 | 
				
			||||||
		groupVersions := []schema.GroupVersion{authenticationv1beta1.SchemeGroupVersion}
 | 
							groupVersions := []schema.GroupVersion{authenticationv1beta1.SchemeGroupVersion}
 | 
				
			||||||
@@ -197,7 +225,7 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, r
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return &tokenReviewV1beta1Client{gw}, nil
 | 
							return &tokenReviewV1beta1ClientGW{gw.RestClient}, nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return nil, fmt.Errorf(
 | 
							return nil, fmt.Errorf(
 | 
				
			||||||
@@ -211,28 +239,60 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, r
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tokenReviewV1Client struct {
 | 
					type tokenReviewV1Client struct {
 | 
				
			||||||
	w *webhook.GenericWebhook
 | 
						client rest.Interface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *tokenReviewV1Client) Create(ctx context.Context, review *authenticationv1.TokenReview, _ metav1.CreateOptions) (*authenticationv1.TokenReview, error) {
 | 
					// Create takes the representation of a tokenReview and creates it.  Returns the server's representation of the tokenReview, HTTP status code and an error, if there is any.
 | 
				
			||||||
	result := &authenticationv1.TokenReview{}
 | 
					func (c *tokenReviewV1Client) Create(ctx context.Context, tokenReview *authenticationv1.TokenReview, opts metav1.CreateOptions) (result *authenticationv1.TokenReview, statusCode int, err error) {
 | 
				
			||||||
	err := t.w.RestClient.Post().Body(review).Do(ctx).Into(result)
 | 
						result = &authenticationv1.TokenReview{}
 | 
				
			||||||
	return result, err
 | 
					
 | 
				
			||||||
 | 
						restResult := c.client.Post().
 | 
				
			||||||
 | 
							Resource("tokenreviews").
 | 
				
			||||||
 | 
							VersionedParams(&opts, scheme.ParameterCodec).
 | 
				
			||||||
 | 
							Body(tokenReview).
 | 
				
			||||||
 | 
							Do(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						restResult.StatusCode(&statusCode)
 | 
				
			||||||
 | 
						err = restResult.Into(result)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tokenReviewV1beta1Client struct {
 | 
					// tokenReviewV1ClientGW used by the generic webhook, doesn't specify GVR.
 | 
				
			||||||
	w *webhook.GenericWebhook
 | 
					type tokenReviewV1ClientGW struct {
 | 
				
			||||||
 | 
						client rest.Interface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *tokenReviewV1beta1Client) Create(ctx context.Context, review *authenticationv1.TokenReview, _ metav1.CreateOptions) (*authenticationv1.TokenReview, error) {
 | 
					// Create takes the representation of a tokenReview and creates it.  Returns the server's representation of the tokenReview, HTTP status code and an error, if there is any.
 | 
				
			||||||
 | 
					func (c *tokenReviewV1ClientGW) Create(ctx context.Context, tokenReview *authenticationv1.TokenReview, opts metav1.CreateOptions) (result *authenticationv1.TokenReview, statusCode int, err error) {
 | 
				
			||||||
 | 
						result = &authenticationv1.TokenReview{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						restResult := c.client.Post().
 | 
				
			||||||
 | 
							Body(tokenReview).
 | 
				
			||||||
 | 
							Do(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						restResult.StatusCode(&statusCode)
 | 
				
			||||||
 | 
						err = restResult.Into(result)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tokenReviewV1beta1ClientGW used by the generic webhook, doesn't specify GVR.
 | 
				
			||||||
 | 
					type tokenReviewV1beta1ClientGW struct {
 | 
				
			||||||
 | 
						client rest.Interface
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *tokenReviewV1beta1ClientGW) Create(ctx context.Context, review *authenticationv1.TokenReview, _ metav1.CreateOptions) (*authenticationv1.TokenReview, int, error) {
 | 
				
			||||||
 | 
						var statusCode int
 | 
				
			||||||
	v1beta1Review := &authenticationv1beta1.TokenReview{Spec: v1SpecToV1beta1Spec(&review.Spec)}
 | 
						v1beta1Review := &authenticationv1beta1.TokenReview{Spec: v1SpecToV1beta1Spec(&review.Spec)}
 | 
				
			||||||
	v1beta1Result := &authenticationv1beta1.TokenReview{}
 | 
						v1beta1Result := &authenticationv1beta1.TokenReview{}
 | 
				
			||||||
	err := t.w.RestClient.Post().Body(v1beta1Review).Do(ctx).Into(v1beta1Result)
 | 
					
 | 
				
			||||||
 | 
						restResult := t.client.Post().Body(v1beta1Review).Do(ctx)
 | 
				
			||||||
 | 
						restResult.StatusCode(&statusCode)
 | 
				
			||||||
 | 
						err := restResult.Into(v1beta1Result)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, statusCode, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	review.Status = v1beta1StatusToV1Status(&v1beta1Result.Status)
 | 
						review.Status = v1beta1StatusToV1Status(&v1beta1Result.Status)
 | 
				
			||||||
	return review, nil
 | 
						return review, statusCode, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func v1SpecToV1beta1Spec(in *authenticationv1.TokenReviewSpec) authenticationv1beta1.TokenReviewSpec {
 | 
					func v1SpecToV1beta1Spec(in *authenticationv1.TokenReviewSpec) authenticationv1beta1.TokenReviewSpec {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,7 +178,7 @@ func (m *mockV1Service) HTTPStatusCode() int { return m.statusCode }
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// newV1TokenAuthenticator creates a temporary kubeconfig file from the provided
 | 
					// newV1TokenAuthenticator creates a temporary kubeconfig file from the provided
 | 
				
			||||||
// arguments and attempts to load a new WebhookTokenAuthenticator from it.
 | 
					// arguments and attempts to load a new WebhookTokenAuthenticator from it.
 | 
				
			||||||
func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
 | 
					func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences, metrics AuthenticatorMetrics) (authenticator.Token, error) {
 | 
				
			||||||
	tempfile, err := ioutil.TempFile("", "")
 | 
						tempfile, err := ioutil.TempFile("", "")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -206,7 +206,7 @@ func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte,
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authn, err := newWithBackoff(c, testRetryBackoff, implicitAuds, 10*time.Second)
 | 
						authn, err := newWithBackoff(c, testRetryBackoff, implicitAuds, 10*time.Second, metrics)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -267,7 +267,7 @@ func TestV1TLSConfig(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			defer server.Close()
 | 
								defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			wh, err := newV1TokenAuthenticator(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
 | 
								wh, err := newV1TokenAuthenticator(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil, noopAuthenticatorMetrics())
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Errorf("%s: failed to create client: %v", tt.test, err)
 | 
									t.Errorf("%s: failed to create client: %v", tt.test, err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@@ -490,7 +490,7 @@ func TestV1WebhookTokenAuthenticator(t *testing.T) {
 | 
				
			|||||||
	token := "my-s3cr3t-t0ken" // Fake token for testing.
 | 
						token := "my-s3cr3t-t0ken" // Fake token for testing.
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		t.Run(tt.description, func(t *testing.T) {
 | 
							t.Run(tt.description, func(t *testing.T) {
 | 
				
			||||||
			wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
 | 
								wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds, noopAuthenticatorMetrics())
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatal(err)
 | 
									t.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -563,7 +563,7 @@ func TestV1WebhookCacheAndRetry(t *testing.T) {
 | 
				
			|||||||
	defer s.Close()
 | 
						defer s.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create an authenticator that caches successful responses "forever" (100 days).
 | 
						// Create an authenticator that caches successful responses "forever" (100 days).
 | 
				
			||||||
	wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
 | 
						wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil, noopAuthenticatorMetrics())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -690,3 +690,10 @@ func TestV1WebhookCacheAndRetry(t *testing.T) {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func noopAuthenticatorMetrics() AuthenticatorMetrics {
 | 
				
			||||||
 | 
						return AuthenticatorMetrics{
 | 
				
			||||||
 | 
							RecordRequestTotal:   noopMetrics{}.RequestTotal,
 | 
				
			||||||
 | 
							RecordRequestLatency: noopMetrics{}.RequestLatency,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -200,7 +200,10 @@ func newV1beta1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authn, err := newWithBackoff(c, testRetryBackoff, implicitAuds, 10*time.Second)
 | 
						authn, err := newWithBackoff(c, testRetryBackoff, implicitAuds, 10*time.Second, AuthenticatorMetrics{
 | 
				
			||||||
 | 
							RecordRequestTotal:   noopMetrics{}.RequestTotal,
 | 
				
			||||||
 | 
							RecordRequestLatency: noopMetrics{}.RequestLatency,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user