k8s.io/kubernetes@v1.29.3/pkg/registry/rbac/rolebinding/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 RoleBinding that prevents privilege escalation. 18 package policybased 19 20 import ( 21 "context" 22 23 rbacv1 "k8s.io/api/rbac/v1" 24 "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 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 29 "k8s.io/apiserver/pkg/registry/rest" 30 kapihelper "k8s.io/kubernetes/pkg/apis/core/helper" 31 "k8s.io/kubernetes/pkg/apis/rbac" 32 rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1" 33 rbacregistry "k8s.io/kubernetes/pkg/registry/rbac" 34 rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation" 35 ) 36 37 var groupResource = rbac.Resource("rolebindings") 38 39 type Storage struct { 40 rest.StandardStorage 41 42 authorizer authorizer.Authorizer 43 44 ruleResolver rbacregistryvalidation.AuthorizationRuleResolver 45 } 46 47 func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage { 48 return &Storage{s, authorizer, ruleResolver} 49 } 50 51 // Destroy cleans up resources on shutdown. 52 func (r *Storage) Destroy() { 53 r.StandardStorage.Destroy() 54 } 55 56 func (r *Storage) NamespaceScoped() bool { 57 return true 58 } 59 60 func (r *Storage) StorageVersion() runtime.GroupVersioner { 61 svp, ok := r.StandardStorage.(rest.StorageVersionProvider) 62 if !ok { 63 return nil 64 } 65 return svp.StorageVersion() 66 } 67 68 var _ rest.StorageVersionProvider = &Storage{} 69 70 func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { 71 if rbacregistry.EscalationAllowed(ctx) { 72 return s.StandardStorage.Create(ctx, obj, createValidation, options) 73 } 74 75 // Get the namespace from the context (populated from the URL). 76 // The namespace in the object can be empty until StandardStorage.Create()->BeforeCreate() populates it from the context. 77 namespace, ok := genericapirequest.NamespaceFrom(ctx) 78 if !ok { 79 return nil, errors.NewBadRequest("namespace is required") 80 } 81 82 roleBinding := obj.(*rbac.RoleBinding) 83 if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) { 84 return s.StandardStorage.Create(ctx, obj, createValidation, options) 85 } 86 87 v1RoleRef := rbacv1.RoleRef{} 88 err := rbacv1helpers.Convert_rbac_RoleRef_To_v1_RoleRef(&roleBinding.RoleRef, &v1RoleRef, nil) 89 if err != nil { 90 return nil, err 91 } 92 rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, namespace) 93 if err != nil { 94 return nil, err 95 } 96 if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { 97 return nil, errors.NewForbidden(groupResource, roleBinding.Name, err) 98 } 99 return s.StandardStorage.Create(ctx, obj, createValidation, options) 100 } 101 102 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) { 103 if rbacregistry.EscalationAllowed(ctx) { 104 return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options) 105 } 106 107 nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { 108 // Get the namespace from the context (populated from the URL). 109 // The namespace in the object can be empty until StandardStorage.Update()->BeforeUpdate() populates it from the context. 110 namespace, ok := genericapirequest.NamespaceFrom(ctx) 111 if !ok { 112 return nil, errors.NewBadRequest("namespace is required") 113 } 114 115 roleBinding := obj.(*rbac.RoleBinding) 116 117 // if we're only mutating fields needed for the GC to eventually delete this obj, return 118 if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) { 119 return obj, nil 120 } 121 122 // if we're explicitly authorized to bind this role, return 123 if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) { 124 return obj, nil 125 } 126 127 // Otherwise, see if we already have all the permissions contained in the referenced role 128 v1RoleRef := rbacv1.RoleRef{} 129 err := rbacv1helpers.Convert_rbac_RoleRef_To_v1_RoleRef(&roleBinding.RoleRef, &v1RoleRef, nil) 130 if err != nil { 131 return nil, err 132 } 133 rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, namespace) 134 if err != nil { 135 return nil, err 136 } 137 if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { 138 return nil, errors.NewForbidden(groupResource, roleBinding.Name, err) 139 } 140 return obj, nil 141 }) 142 143 return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options) 144 } 145 146 var _ rest.SingularNameProvider = &Storage{} 147 148 func (s *Storage) GetSingularName() string { 149 snp, ok := s.StandardStorage.(rest.SingularNameProvider) 150 if !ok { 151 return "" 152 } 153 return snp.GetSingularName() 154 }