k8s.io/kubernetes@v1.29.3/pkg/registry/rbac/clusterrole/policybased/storage.go (about) 1 /* 2 Copyright 2016 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 policybased implements a standard storage for ClusterRole that prevents privilege escalation. 18 package policybased 19 20 import ( 21 "context" 22 "errors" 23 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apiserver/pkg/authorization/authorizer" 28 "k8s.io/apiserver/pkg/registry/rest" 29 kapihelper "k8s.io/kubernetes/pkg/apis/core/helper" 30 "k8s.io/kubernetes/pkg/apis/rbac" 31 rbacregistry "k8s.io/kubernetes/pkg/registry/rbac" 32 rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation" 33 ) 34 35 var groupResource = rbac.Resource("clusterroles") 36 37 type Storage struct { 38 rest.StandardStorage 39 40 authorizer authorizer.Authorizer 41 42 ruleResolver rbacregistryvalidation.AuthorizationRuleResolver 43 } 44 45 func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage { 46 return &Storage{s, authorizer, ruleResolver} 47 } 48 49 // Destroy cleans up resources on shutdown. 50 func (r *Storage) Destroy() { 51 r.StandardStorage.Destroy() 52 } 53 54 func (r *Storage) NamespaceScoped() bool { 55 return false 56 } 57 58 func (r *Storage) StorageVersion() runtime.GroupVersioner { 59 svp, ok := r.StandardStorage.(rest.StorageVersionProvider) 60 if !ok { 61 return nil 62 } 63 return svp.StorageVersion() 64 } 65 66 var _ rest.StorageVersionProvider = &Storage{} 67 68 var fullAuthority = []rbac.PolicyRule{ 69 rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(), 70 rbac.NewRule("*").URLs("*").RuleOrDie(), 71 } 72 73 func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { 74 if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) { 75 return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, options) 76 } 77 78 clusterRole := obj.(*rbac.ClusterRole) 79 rules := clusterRole.Rules 80 if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, rules); err != nil { 81 return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err) 82 } 83 // to set the aggregation rule, since it can gather anything, requires * on *.* 84 if hasAggregationRule(clusterRole) { 85 if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, fullAuthority); err != nil { 86 return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule")) 87 } 88 } 89 90 return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, options) 91 } 92 93 func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { 94 if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) { 95 return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options) 96 } 97 98 nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { 99 clusterRole := obj.(*rbac.ClusterRole) 100 oldClusterRole := oldObj.(*rbac.ClusterRole) 101 102 // if we're only mutating fields needed for the GC to eventually delete this obj, return 103 if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) { 104 return obj, nil 105 } 106 107 rules := clusterRole.Rules 108 if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, rules); err != nil { 109 return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err) 110 } 111 // to change the aggregation rule, since it can gather anything and prevent tightening, requires * on *.* 112 if hasAggregationRule(clusterRole) || hasAggregationRule(oldClusterRole) { 113 if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, fullAuthority); err != nil { 114 return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule")) 115 } 116 } 117 118 return obj, nil 119 }) 120 121 return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options) 122 } 123 124 func hasAggregationRule(clusterRole *rbac.ClusterRole) bool { 125 return clusterRole.AggregationRule != nil && len(clusterRole.AggregationRule.ClusterRoleSelectors) > 0 126 } 127 128 var _ rest.SingularNameProvider = &Storage{} 129 130 func (s *Storage) GetSingularName() string { 131 snp, ok := s.StandardStorage.(rest.SingularNameProvider) 132 if !ok { 133 return "" 134 } 135 return snp.GetSingularName() 136 }