diff --git a/pkg/kubeapiserver/admission/config.go b/pkg/kubeapiserver/admission/config.go index 16f994093a0..24e565bcffa 100644 --- a/pkg/kubeapiserver/admission/config.go +++ b/pkg/kubeapiserver/admission/config.go @@ -70,7 +70,7 @@ func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselec cloudConfig, discoveryRESTMapper, quotainstall.NewQuotaConfigurationForAdmission(), - exclusion.NewFilter(), + exclusion.Excluded(), ) admissionPostStartHook := func(context genericapiserver.PostStartHookContext) error { diff --git a/pkg/kubeapiserver/admission/exclusion/filter.go b/pkg/kubeapiserver/admission/exclusion/filter.go deleted file mode 100644 index d4db639c89a..00000000000 --- a/pkg/kubeapiserver/admission/exclusion/filter.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2024 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 exclusion - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/admission/resourcefilter" -) - -// NewFilter creates a resource filter with the built-in exclusion list. -func NewFilter() resourcefilter.Interface { - return &filter{excluded: sets.New[schema.GroupResource](excluded...)} -} - -type filter struct { - excluded sets.Set[schema.GroupResource] -} - -func (f *filter) ShouldHandle(a admission.Attributes) bool { - gvr := a.GetResource() - // ignore the version for the decision-making - // because putting different versions into different category - // is almost always a mistake. - gr := gvr.GroupResource() - return !f.excluded.Has(gr) -} diff --git a/pkg/kubeapiserver/admission/initializer.go b/pkg/kubeapiserver/admission/initializer.go index 536dfbf5ce8..d3ef1ad33a5 100644 --- a/pkg/kubeapiserver/admission/initializer.go +++ b/pkg/kubeapiserver/admission/initializer.go @@ -18,9 +18,9 @@ package admission import ( "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/apiserver/pkg/admission/resourcefilter" quota "k8s.io/apiserver/pkg/quota/v1" ) @@ -33,10 +33,10 @@ type WantsCloudConfig interface { // PluginInitializer is used for initialization of the Kubernetes specific admission plugins. type PluginInitializer struct { - cloudConfig []byte - restMapper meta.RESTMapper - quotaConfiguration quota.Configuration - resourceFilter resourcefilter.Interface + cloudConfig []byte + restMapper meta.RESTMapper + quotaConfiguration quota.Configuration + excludedAdmissionResources []schema.GroupResource } var _ admission.PluginInitializer = &PluginInitializer{} @@ -48,13 +48,13 @@ func NewPluginInitializer( cloudConfig []byte, restMapper meta.RESTMapper, quotaConfiguration quota.Configuration, - resourceFilter resourcefilter.Interface, + excludedAdmissionResources []schema.GroupResource, ) *PluginInitializer { return &PluginInitializer{ - cloudConfig: cloudConfig, - restMapper: restMapper, - quotaConfiguration: quotaConfiguration, - resourceFilter: resourceFilter, + cloudConfig: cloudConfig, + restMapper: restMapper, + quotaConfiguration: quotaConfiguration, + excludedAdmissionResources: excludedAdmissionResources, } } @@ -73,7 +73,7 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) { wants.SetQuotaConfiguration(i.quotaConfiguration) } - if wants, ok := plugin.(initializer.WantsResourceFilter); ok { - wants.SetResourceFilter(i.resourceFilter) + if wants, ok := plugin.(initializer.WantsExcludedAdmissionResources); ok { + wants.SetExcludedAdmissionResources(i.excludedAdmissionResources) } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go index 233d880062a..21202bd7920 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go @@ -18,8 +18,8 @@ package initializer import ( "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/admission/resourcefilter" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/cel/openapi/resolver" quota "k8s.io/apiserver/pkg/quota/v1" @@ -91,9 +91,9 @@ type WantsSchemaResolver interface { admission.InitializationValidator } -// WantsResourceFilter defines a function which sets the ResourceFilter for -// an admission plugin that needs it. -type WantsResourceFilter interface { - SetResourceFilter(filter resourcefilter.Interface) +// WantsExcludedAdmissionResources defines a function which sets the ExcludedAdmissionResources +// for an admission plugin that needs it. +type WantsExcludedAdmissionResources interface { + SetExcludedAdmissionResources(excludedAdmissionResources []schema.GroupResource) admission.InitializationValidator } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/plugin.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/plugin.go index 3e6faa644b6..ed1c621bc8e 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/plugin.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/plugin.go @@ -21,12 +21,14 @@ import ( "errors" "fmt" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/initializer" "k8s.io/apiserver/pkg/admission/plugin/policy/matching" - "k8s.io/apiserver/pkg/admission/resourcefilter" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/client-go/dynamic" "k8s.io/client-go/informers" @@ -37,6 +39,15 @@ import ( type sourceFactory[H any] func(informers.SharedInformerFactory, kubernetes.Interface, dynamic.Interface, meta.RESTMapper) Source[H] type dispatcherFactory[H any] func(authorizer.Authorizer, *matching.Matcher) Dispatcher[H] +// admissionResources is the list of resources related to CEL-based admission +// features. +var admissionResources = []schema.GroupResource{ + {Group: admissionregistrationv1.GroupName, Resource: "validatingadmissionpolicies"}, + {Group: admissionregistrationv1.GroupName, Resource: "validatingadmissionpolicybindings"}, + {Group: admissionregistrationv1.GroupName, Resource: "mutatingadmissionpolicies"}, + {Group: admissionregistrationv1.GroupName, Resource: "mutatingadmissionpolicybindings"}, +} + // AdmissionPolicyManager is an abstract admission plugin with all the // infrastructure to define Admit or Validate on-top. type Plugin[H any] struct { @@ -49,14 +60,14 @@ type Plugin[H any] struct { dispatcher Dispatcher[H] matcher *matching.Matcher - informerFactory informers.SharedInformerFactory - client kubernetes.Interface - restMapper meta.RESTMapper - dynamicClient dynamic.Interface - resourceFilter resourcefilter.Interface // optional - stopCh <-chan struct{} - authorizer authorizer.Authorizer - enabled bool + informerFactory informers.SharedInformerFactory + client kubernetes.Interface + restMapper meta.RESTMapper + dynamicClient dynamic.Interface + excludedResources sets.Set[schema.GroupResource] + stopCh <-chan struct{} + authorizer authorizer.Authorizer + enabled bool } var ( @@ -66,7 +77,7 @@ var ( _ initializer.WantsDynamicClient = &Plugin[any]{} _ initializer.WantsDrainedNotification = &Plugin[any]{} _ initializer.WantsAuthorizer = &Plugin[any]{} - _ initializer.WantsResourceFilter = &Plugin[any]{} + _ initializer.WantsExcludedAdmissionResources = &Plugin[any]{} _ admission.InitializationValidator = &Plugin[any]{} ) @@ -79,6 +90,9 @@ func NewPlugin[H any]( Handler: handler, sourceFactory: sourceFactory, dispatcherFactory: dispatcherFactory, + + // always exclude admission/mutating policies and bindings + excludedResources: sets.New(admissionResources...), } } @@ -114,8 +128,8 @@ func (c *Plugin[H]) SetEnabled(enabled bool) { c.enabled = enabled } -func (c *Plugin[H]) SetResourceFilter(filter resourcefilter.Interface) { - c.resourceFilter = filter +func (c *Plugin[H]) SetExcludedAdmissionResources(excludedResources []schema.GroupResource) { + c.excludedResources.Insert(excludedResources...) } // ValidateInitialization - once clientset and informer factory are provided, creates and starts the admission controller @@ -184,7 +198,7 @@ func (c *Plugin[H]) Dispatch( ) (err error) { if !c.enabled { return nil - } else if isPolicyResource(a) || (c.resourceFilter != nil && !c.resourceFilter.ShouldHandle(a)) { + } else if c.shouldIgnoreResource(a) { return nil } else if !c.WaitForReady() { return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) @@ -193,14 +207,9 @@ func (c *Plugin[H]) Dispatch( return c.dispatcher.Dispatch(ctx, a, o, c.source.Hooks()) } -func isPolicyResource(attr admission.Attributes) bool { - gvk := attr.GetResource() - if gvk.Group == "admissionregistration.k8s.io" { - if gvk.Resource == "validatingadmissionpolicies" || gvk.Resource == "validatingadmissionpolicybindings" { - return true - } else if gvk.Resource == "mutatingadmissionpolicies" || gvk.Resource == "mutatingadmissionpolicybindings" { - return true - } - } - return false +func (c *Plugin[H]) shouldIgnoreResource(attr admission.Attributes) bool { + gvr := attr.GetResource() + // exclusion decision ignores the version. + gr := gvr.GroupResource() + return c.excludedResources.Has(gr) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/plugin.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/plugin.go index 5880af4fe9b..2fae52d197d 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/plugin.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/plugin.go @@ -74,7 +74,7 @@ type Plugin struct { var _ admission.Interface = &Plugin{} var _ admission.ValidationInterface = &Plugin{} var _ initializer.WantsFeatures = &Plugin{} -var _ initializer.WantsResourceFilter = &Plugin{} +var _ initializer.WantsExcludedAdmissionResources = &Plugin{} func NewPlugin(_ io.Reader) *Plugin { handler := admission.NewHandler(admission.Connect, admission.Create, admission.Delete, admission.Update) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/resourcefilter/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/resourcefilter/interfaces.go deleted file mode 100644 index 23711b6dfff..00000000000 --- a/staging/src/k8s.io/apiserver/pkg/admission/resourcefilter/interfaces.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2024 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 resourcefilter - -import ( - "k8s.io/apiserver/pkg/admission" -) - -// Interface is a resource filter that takes an Attributes and -// check if it should be handled or ignored by the admission plugin. -type Interface interface { - // ShouldHandle returns true if the admission plugin should handle the request, - // considering the given Attributes, or false otherwise. - ShouldHandle(admission.Attributes) bool -}