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

     1  /*
     2  Copyright 2017 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 rules
    18  
    19  import (
    20  	"strings"
    21  
    22  	"k8s.io/api/admissionregistration/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/runtime/schema"
    25  	"k8s.io/apiserver/pkg/admission"
    26  )
    27  
    28  // Matcher determines if the Attr matches the Rule.
    29  type Matcher struct {
    30  	Rule v1.RuleWithOperations
    31  	Attr admission.Attributes
    32  }
    33  
    34  // Matches returns if the Attr matches the Rule.
    35  func (r *Matcher) Matches() bool {
    36  	return r.scope() &&
    37  		r.operation() &&
    38  		r.group() &&
    39  		r.version() &&
    40  		r.resource()
    41  }
    42  
    43  func exactOrWildcard(items []string, requested string) bool {
    44  	for _, item := range items {
    45  		if item == "*" {
    46  			return true
    47  		}
    48  		if item == requested {
    49  			return true
    50  		}
    51  	}
    52  
    53  	return false
    54  }
    55  
    56  var namespaceResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}
    57  
    58  func (r *Matcher) scope() bool {
    59  	if r.Rule.Scope == nil || *r.Rule.Scope == v1.AllScopes {
    60  		return true
    61  	}
    62  	// attr.GetNamespace() is set to the name of the namespace for requests of the namespace object itself.
    63  	switch *r.Rule.Scope {
    64  	case v1.NamespacedScope:
    65  		// first make sure that we are not requesting a namespace object (namespace objects are cluster-scoped)
    66  		return r.Attr.GetResource() != namespaceResource && r.Attr.GetNamespace() != metav1.NamespaceNone
    67  	case v1.ClusterScope:
    68  		// also return true if the request is for a namespace object (namespace objects are cluster-scoped)
    69  		return r.Attr.GetResource() == namespaceResource || r.Attr.GetNamespace() == metav1.NamespaceNone
    70  	default:
    71  		return false
    72  	}
    73  }
    74  
    75  func (r *Matcher) group() bool {
    76  	return exactOrWildcard(r.Rule.APIGroups, r.Attr.GetResource().Group)
    77  }
    78  
    79  func (r *Matcher) version() bool {
    80  	return exactOrWildcard(r.Rule.APIVersions, r.Attr.GetResource().Version)
    81  }
    82  
    83  func (r *Matcher) operation() bool {
    84  	attrOp := r.Attr.GetOperation()
    85  	for _, op := range r.Rule.Operations {
    86  		if op == v1.OperationAll {
    87  			return true
    88  		}
    89  		// The constants are the same such that this is a valid cast (and this
    90  		// is tested).
    91  		if op == v1.OperationType(attrOp) {
    92  			return true
    93  		}
    94  	}
    95  	return false
    96  }
    97  
    98  func splitResource(resSub string) (res, sub string) {
    99  	parts := strings.SplitN(resSub, "/", 2)
   100  	if len(parts) == 2 {
   101  		return parts[0], parts[1]
   102  	}
   103  	return parts[0], ""
   104  }
   105  
   106  func (r *Matcher) resource() bool {
   107  	opRes, opSub := r.Attr.GetResource().Resource, r.Attr.GetSubresource()
   108  	for _, res := range r.Rule.Resources {
   109  		res, sub := splitResource(res)
   110  		resMatch := res == "*" || res == opRes
   111  		subMatch := sub == "*" || sub == opSub
   112  		if resMatch && subMatch {
   113  			return true
   114  		}
   115  	}
   116  	return false
   117  }
   118  
   119  // IsExemptAdmissionConfigurationResource determines if an admission.Attributes object is describing
   120  // the admission of a ValidatingWebhookConfiguration or a MutatingWebhookConfiguration or a ValidatingAdmissionPolicy or a ValidatingAdmissionPolicyBinding
   121  func IsExemptAdmissionConfigurationResource(attr admission.Attributes) bool {
   122  	gvk := attr.GetKind()
   123  	if gvk.Group == "admissionregistration.k8s.io" {
   124  		if gvk.Kind == "ValidatingWebhookConfiguration" || gvk.Kind == "MutatingWebhookConfiguration" || gvk.Kind == "ValidatingAdmissionPolicy" || gvk.Kind == "ValidatingAdmissionPolicyBinding" {
   125  			return true
   126  		}
   127  	}
   128  	return false
   129  }