k8s.io/apiserver@v0.31.1/pkg/admission/plugin/webhook/accessors.go (about)

     1  /*
     2  Copyright 2019 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 webhook
    18  
    19  import (
    20  	"sync"
    21  
    22  	v1 "k8s.io/api/admissionregistration/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/labels"
    25  	"k8s.io/apiserver/pkg/admission/plugin/cel"
    26  	"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
    27  	"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
    28  	"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
    29  	"k8s.io/apiserver/pkg/cel/environment"
    30  	"k8s.io/apiserver/pkg/features"
    31  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    32  	webhookutil "k8s.io/apiserver/pkg/util/webhook"
    33  	"k8s.io/client-go/rest"
    34  )
    35  
    36  // WebhookAccessor provides a common interface to both mutating and validating webhook types.
    37  type WebhookAccessor interface {
    38  	// This accessor provides the methods needed to support matching against webhook
    39  	// predicates
    40  	namespace.NamespaceSelectorProvider
    41  	object.ObjectSelectorProvider
    42  
    43  	// GetUID gets a string that uniquely identifies the webhook.
    44  	GetUID() string
    45  
    46  	// GetConfigurationName gets the name of the webhook configuration that owns this webhook.
    47  	GetConfigurationName() string
    48  
    49  	// GetRESTClient gets the webhook client
    50  	GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error)
    51  
    52  	// GetCompiledMatcher gets the compiled matcher object
    53  	GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher
    54  
    55  	// GetName gets the webhook Name field. Note that the name is scoped to the webhook
    56  	// configuration and does not provide a globally unique identity, if a unique identity is
    57  	// needed, use GetUID.
    58  	GetName() string
    59  	// GetClientConfig gets the webhook ClientConfig field.
    60  	GetClientConfig() v1.WebhookClientConfig
    61  	// GetRules gets the webhook Rules field.
    62  	GetRules() []v1.RuleWithOperations
    63  	// GetFailurePolicy gets the webhook FailurePolicy field.
    64  	GetFailurePolicy() *v1.FailurePolicyType
    65  	// GetMatchPolicy gets the webhook MatchPolicy field.
    66  	GetMatchPolicy() *v1.MatchPolicyType
    67  	// GetNamespaceSelector gets the webhook NamespaceSelector field.
    68  	GetNamespaceSelector() *metav1.LabelSelector
    69  	// GetObjectSelector gets the webhook ObjectSelector field.
    70  	GetObjectSelector() *metav1.LabelSelector
    71  	// GetSideEffects gets the webhook SideEffects field.
    72  	GetSideEffects() *v1.SideEffectClass
    73  	// GetTimeoutSeconds gets the webhook TimeoutSeconds field.
    74  	GetTimeoutSeconds() *int32
    75  	// GetAdmissionReviewVersions gets the webhook AdmissionReviewVersions field.
    76  	GetAdmissionReviewVersions() []string
    77  
    78  	// GetMatchConditions gets the webhook match conditions field.
    79  	GetMatchConditions() []v1.MatchCondition
    80  
    81  	// GetMutatingWebhook if the accessor contains a MutatingWebhook, returns it and true, else returns false.
    82  	GetMutatingWebhook() (*v1.MutatingWebhook, bool)
    83  	// GetValidatingWebhook if the accessor contains a ValidatingWebhook, returns it and true, else returns false.
    84  	GetValidatingWebhook() (*v1.ValidatingWebhook, bool)
    85  
    86  	// GetType returns the type of the accessor (validate or admit)
    87  	GetType() string
    88  }
    89  
    90  // NewMutatingWebhookAccessor creates an accessor for a MutatingWebhook.
    91  func NewMutatingWebhookAccessor(uid, configurationName string, h *v1.MutatingWebhook) WebhookAccessor {
    92  	return &mutatingWebhookAccessor{uid: uid, configurationName: configurationName, MutatingWebhook: h}
    93  }
    94  
    95  type mutatingWebhookAccessor struct {
    96  	*v1.MutatingWebhook
    97  	uid               string
    98  	configurationName string
    99  
   100  	initObjectSelector sync.Once
   101  	objectSelector     labels.Selector
   102  	objectSelectorErr  error
   103  
   104  	initNamespaceSelector sync.Once
   105  	namespaceSelector     labels.Selector
   106  	namespaceSelectorErr  error
   107  
   108  	initClient sync.Once
   109  	client     *rest.RESTClient
   110  	clientErr  error
   111  
   112  	compileMatcher  sync.Once
   113  	compiledMatcher matchconditions.Matcher
   114  }
   115  
   116  func (m *mutatingWebhookAccessor) GetUID() string {
   117  	return m.uid
   118  }
   119  
   120  func (m *mutatingWebhookAccessor) GetConfigurationName() string {
   121  	return m.configurationName
   122  }
   123  
   124  func (m *mutatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) {
   125  	m.initClient.Do(func() {
   126  		m.client, m.clientErr = clientManager.HookClient(hookClientConfigForWebhook(m))
   127  	})
   128  	return m.client, m.clientErr
   129  }
   130  
   131  func (m *mutatingWebhookAccessor) GetType() string {
   132  	return "admit"
   133  }
   134  
   135  func (m *mutatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher {
   136  	m.compileMatcher.Do(func() {
   137  		expressions := make([]cel.ExpressionAccessor, len(m.MutatingWebhook.MatchConditions))
   138  		for i, matchCondition := range m.MutatingWebhook.MatchConditions {
   139  			expressions[i] = &matchconditions.MatchCondition{
   140  				Name:       matchCondition.Name,
   141  				Expression: matchCondition.Expression,
   142  			}
   143  		}
   144  		strictCost := false
   145  		if utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks) {
   146  			strictCost = true
   147  		}
   148  		m.compiledMatcher = matchconditions.NewMatcher(compiler.Compile(
   149  			expressions,
   150  			cel.OptionalVariableDeclarations{
   151  				HasParams:     false,
   152  				HasAuthorizer: true,
   153  				StrictCost:    strictCost,
   154  			},
   155  			environment.StoredExpressions,
   156  		), m.FailurePolicy, "webhook", "admit", m.Name)
   157  	})
   158  	return m.compiledMatcher
   159  }
   160  
   161  func (m *mutatingWebhookAccessor) GetParsedNamespaceSelector() (labels.Selector, error) {
   162  	m.initNamespaceSelector.Do(func() {
   163  		m.namespaceSelector, m.namespaceSelectorErr = metav1.LabelSelectorAsSelector(m.NamespaceSelector)
   164  	})
   165  	return m.namespaceSelector, m.namespaceSelectorErr
   166  }
   167  
   168  func (m *mutatingWebhookAccessor) GetParsedObjectSelector() (labels.Selector, error) {
   169  	m.initObjectSelector.Do(func() {
   170  		m.objectSelector, m.objectSelectorErr = metav1.LabelSelectorAsSelector(m.ObjectSelector)
   171  	})
   172  	return m.objectSelector, m.objectSelectorErr
   173  }
   174  
   175  func (m *mutatingWebhookAccessor) GetName() string {
   176  	return m.Name
   177  }
   178  
   179  func (m *mutatingWebhookAccessor) GetClientConfig() v1.WebhookClientConfig {
   180  	return m.ClientConfig
   181  }
   182  
   183  func (m *mutatingWebhookAccessor) GetRules() []v1.RuleWithOperations {
   184  	return m.Rules
   185  }
   186  
   187  func (m *mutatingWebhookAccessor) GetFailurePolicy() *v1.FailurePolicyType {
   188  	return m.FailurePolicy
   189  }
   190  
   191  func (m *mutatingWebhookAccessor) GetMatchPolicy() *v1.MatchPolicyType {
   192  	return m.MatchPolicy
   193  }
   194  
   195  func (m *mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
   196  	return m.NamespaceSelector
   197  }
   198  
   199  func (m *mutatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
   200  	return m.ObjectSelector
   201  }
   202  
   203  func (m *mutatingWebhookAccessor) GetSideEffects() *v1.SideEffectClass {
   204  	return m.SideEffects
   205  }
   206  
   207  func (m *mutatingWebhookAccessor) GetTimeoutSeconds() *int32 {
   208  	return m.TimeoutSeconds
   209  }
   210  
   211  func (m *mutatingWebhookAccessor) GetAdmissionReviewVersions() []string {
   212  	return m.AdmissionReviewVersions
   213  }
   214  
   215  func (m *mutatingWebhookAccessor) GetMatchConditions() []v1.MatchCondition {
   216  	return m.MatchConditions
   217  }
   218  
   219  func (m *mutatingWebhookAccessor) GetMutatingWebhook() (*v1.MutatingWebhook, bool) {
   220  	return m.MutatingWebhook, true
   221  }
   222  
   223  func (m *mutatingWebhookAccessor) GetValidatingWebhook() (*v1.ValidatingWebhook, bool) {
   224  	return nil, false
   225  }
   226  
   227  // NewValidatingWebhookAccessor creates an accessor for a ValidatingWebhook.
   228  func NewValidatingWebhookAccessor(uid, configurationName string, h *v1.ValidatingWebhook) WebhookAccessor {
   229  	return &validatingWebhookAccessor{uid: uid, configurationName: configurationName, ValidatingWebhook: h}
   230  }
   231  
   232  type validatingWebhookAccessor struct {
   233  	*v1.ValidatingWebhook
   234  	uid               string
   235  	configurationName string
   236  
   237  	initObjectSelector sync.Once
   238  	objectSelector     labels.Selector
   239  	objectSelectorErr  error
   240  
   241  	initNamespaceSelector sync.Once
   242  	namespaceSelector     labels.Selector
   243  	namespaceSelectorErr  error
   244  
   245  	initClient sync.Once
   246  	client     *rest.RESTClient
   247  	clientErr  error
   248  
   249  	compileMatcher  sync.Once
   250  	compiledMatcher matchconditions.Matcher
   251  }
   252  
   253  func (v *validatingWebhookAccessor) GetUID() string {
   254  	return v.uid
   255  }
   256  
   257  func (v *validatingWebhookAccessor) GetConfigurationName() string {
   258  	return v.configurationName
   259  }
   260  
   261  func (v *validatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) {
   262  	v.initClient.Do(func() {
   263  		v.client, v.clientErr = clientManager.HookClient(hookClientConfigForWebhook(v))
   264  	})
   265  	return v.client, v.clientErr
   266  }
   267  
   268  func (v *validatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher {
   269  	v.compileMatcher.Do(func() {
   270  		expressions := make([]cel.ExpressionAccessor, len(v.ValidatingWebhook.MatchConditions))
   271  		for i, matchCondition := range v.ValidatingWebhook.MatchConditions {
   272  			expressions[i] = &matchconditions.MatchCondition{
   273  				Name:       matchCondition.Name,
   274  				Expression: matchCondition.Expression,
   275  			}
   276  		}
   277  		strictCost := false
   278  		if utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks) {
   279  			strictCost = true
   280  		}
   281  		v.compiledMatcher = matchconditions.NewMatcher(compiler.Compile(
   282  			expressions,
   283  			cel.OptionalVariableDeclarations{
   284  				HasParams:     false,
   285  				HasAuthorizer: true,
   286  				StrictCost:    strictCost,
   287  			},
   288  			environment.StoredExpressions,
   289  		), v.FailurePolicy, "webhook", "validating", v.Name)
   290  	})
   291  	return v.compiledMatcher
   292  }
   293  
   294  func (v *validatingWebhookAccessor) GetParsedNamespaceSelector() (labels.Selector, error) {
   295  	v.initNamespaceSelector.Do(func() {
   296  		v.namespaceSelector, v.namespaceSelectorErr = metav1.LabelSelectorAsSelector(v.NamespaceSelector)
   297  	})
   298  	return v.namespaceSelector, v.namespaceSelectorErr
   299  }
   300  
   301  func (v *validatingWebhookAccessor) GetParsedObjectSelector() (labels.Selector, error) {
   302  	v.initObjectSelector.Do(func() {
   303  		v.objectSelector, v.objectSelectorErr = metav1.LabelSelectorAsSelector(v.ObjectSelector)
   304  	})
   305  	return v.objectSelector, v.objectSelectorErr
   306  }
   307  
   308  func (m *validatingWebhookAccessor) GetType() string {
   309  	return "validate"
   310  }
   311  
   312  func (v *validatingWebhookAccessor) GetName() string {
   313  	return v.Name
   314  }
   315  
   316  func (v *validatingWebhookAccessor) GetClientConfig() v1.WebhookClientConfig {
   317  	return v.ClientConfig
   318  }
   319  
   320  func (v *validatingWebhookAccessor) GetRules() []v1.RuleWithOperations {
   321  	return v.Rules
   322  }
   323  
   324  func (v *validatingWebhookAccessor) GetFailurePolicy() *v1.FailurePolicyType {
   325  	return v.FailurePolicy
   326  }
   327  
   328  func (v *validatingWebhookAccessor) GetMatchPolicy() *v1.MatchPolicyType {
   329  	return v.MatchPolicy
   330  }
   331  
   332  func (v *validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
   333  	return v.NamespaceSelector
   334  }
   335  
   336  func (v *validatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
   337  	return v.ObjectSelector
   338  }
   339  
   340  func (v *validatingWebhookAccessor) GetSideEffects() *v1.SideEffectClass {
   341  	return v.SideEffects
   342  }
   343  
   344  func (v *validatingWebhookAccessor) GetTimeoutSeconds() *int32 {
   345  	return v.TimeoutSeconds
   346  }
   347  
   348  func (v *validatingWebhookAccessor) GetAdmissionReviewVersions() []string {
   349  	return v.AdmissionReviewVersions
   350  }
   351  
   352  func (v *validatingWebhookAccessor) GetMatchConditions() []v1.MatchCondition {
   353  	return v.MatchConditions
   354  }
   355  
   356  func (v *validatingWebhookAccessor) GetMutatingWebhook() (*v1.MutatingWebhook, bool) {
   357  	return nil, false
   358  }
   359  
   360  func (v *validatingWebhookAccessor) GetValidatingWebhook() (*v1.ValidatingWebhook, bool) {
   361  	return v.ValidatingWebhook, true
   362  }
   363  
   364  // hookClientConfigForWebhook construct a webhookutil.ClientConfig using a WebhookAccessor to access
   365  // v1beta1.MutatingWebhook and v1beta1.ValidatingWebhook API objects.  webhookutil.ClientConfig is used
   366  // to create a HookClient and the purpose of the config struct is to share that with other packages
   367  // that need to create a HookClient.
   368  func hookClientConfigForWebhook(w WebhookAccessor) webhookutil.ClientConfig {
   369  	ret := webhookutil.ClientConfig{Name: w.GetName(), CABundle: w.GetClientConfig().CABundle}
   370  	if w.GetClientConfig().URL != nil {
   371  		ret.URL = *w.GetClientConfig().URL
   372  	}
   373  	if w.GetClientConfig().Service != nil {
   374  		ret.Service = &webhookutil.ClientConfigService{
   375  			Name:      w.GetClientConfig().Service.Name,
   376  			Namespace: w.GetClientConfig().Service.Namespace,
   377  		}
   378  		if w.GetClientConfig().Service.Port != nil {
   379  			ret.Service.Port = *w.GetClientConfig().Service.Port
   380  		} else {
   381  			ret.Service.Port = 443
   382  		}
   383  		if w.GetClientConfig().Service.Path != nil {
   384  			ret.Service.Path = *w.GetClientConfig().Service.Path
   385  		}
   386  	}
   387  	return ret
   388  }