k8s.io/kubernetes@v1.29.3/pkg/registry/rbac/clusterrole/policybased/storage_test.go (about) 1 /* 2 Copyright 2018 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 18 19 import ( 20 "context" 21 "testing" 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/authentication/user" 28 "k8s.io/apiserver/pkg/authorization/authorizer" 29 "k8s.io/apiserver/pkg/endpoints/request" 30 "k8s.io/apiserver/pkg/registry/rest" 31 "k8s.io/kubernetes/pkg/apis/rbac" 32 _ "k8s.io/kubernetes/pkg/apis/rbac/install" 33 "k8s.io/kubernetes/pkg/registry/rbac/validation" 34 ) 35 36 func TestEscalation(t *testing.T) { 37 createContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), ""), &request.RequestInfo{ 38 IsResourceRequest: true, 39 Verb: "create", 40 APIGroup: "rbac.authorization.k8s.io", 41 APIVersion: "v1", 42 Resource: "clusterroles", 43 Name: "", 44 }) 45 updateContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), ""), &request.RequestInfo{ 46 IsResourceRequest: true, 47 Verb: "update", 48 APIGroup: "rbac.authorization.k8s.io", 49 APIVersion: "v1", 50 Resource: "clusterroles", 51 Name: "myrole", 52 }) 53 54 superuser := &user.DefaultInfo{Name: "superuser", Groups: []string{"system:masters"}} 55 bob := &user.DefaultInfo{Name: "bob"} 56 steve := &user.DefaultInfo{Name: "steve"} 57 alice := &user.DefaultInfo{Name: "alice"} 58 59 authzCalled := 0 60 fakeStorage := &fakeStorage{} 61 fakeAuthorizer := authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) { 62 authzCalled++ 63 if attr.GetUser().GetName() == "steve" { 64 return authorizer.DecisionAllow, "", nil 65 } 66 return authorizer.DecisionNoOpinion, "", nil 67 }) 68 fakeRuleResolver, _ := validation.NewTestRuleResolver( 69 nil, 70 nil, 71 []*rbacv1.ClusterRole{{ObjectMeta: metav1.ObjectMeta{Name: "alice-role"}, Rules: []rbacv1.PolicyRule{{APIGroups: []string{"*"}, Resources: []string{"*"}, Verbs: []string{"*"}}}}}, 72 []*rbacv1.ClusterRoleBinding{{RoleRef: rbacv1.RoleRef{Name: "alice-role", APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole"}, Subjects: []rbacv1.Subject{{Name: "alice", Kind: "User", APIGroup: "rbac.authorization.k8s.io"}}}}, 73 ) 74 75 role := &rbac.ClusterRole{ 76 ObjectMeta: metav1.ObjectMeta{Name: "myrole", Namespace: ""}, 77 Rules: []rbac.PolicyRule{{APIGroups: []string{""}, Verbs: []string{"get"}, Resources: []string{"pods"}}}, 78 } 79 80 s := NewStorage(fakeStorage, fakeAuthorizer, fakeRuleResolver) 81 82 testcases := []struct { 83 name string 84 user user.Info 85 expectAllowed bool 86 expectAuthz bool 87 }{ 88 // superuser doesn't even trigger an authz check, and is allowed 89 { 90 name: "superuser", 91 user: superuser, 92 expectAuthz: false, 93 expectAllowed: true, 94 }, 95 // bob triggers an authz check, is disallowed by the authorizer, and has no RBAC permissions, so is not allowed 96 { 97 name: "bob", 98 user: bob, 99 expectAuthz: true, 100 expectAllowed: false, 101 }, 102 // steve triggers an authz check, is allowed by the authorizer, and has no RBAC permissions, but is still allowed 103 { 104 name: "steve", 105 user: steve, 106 expectAuthz: true, 107 expectAllowed: true, 108 }, 109 // alice triggers an authz check, is denied by the authorizer, but has RBAC permissions in the fakeRuleResolver, so is allowed 110 { 111 name: "alice", 112 user: alice, 113 expectAuthz: true, 114 expectAllowed: true, 115 }, 116 } 117 118 for _, tc := range testcases { 119 t.Run(tc.name, func(t *testing.T) { 120 authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0 121 _, err := s.Create(request.WithUser(createContext, tc.user), role, nil, nil) 122 123 if tc.expectAllowed { 124 if err != nil { 125 t.Error(err) 126 return 127 } 128 if fakeStorage.created != 1 { 129 t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created) 130 return 131 } 132 } else { 133 if !errors.IsForbidden(err) { 134 t.Errorf("expected forbidden, got %v", err) 135 return 136 } 137 if fakeStorage.created != 0 { 138 t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created) 139 return 140 } 141 } 142 143 if tc.expectAuthz != (authzCalled > 0) { 144 t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled) 145 } 146 147 authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0 148 _, _, err = s.Update(request.WithUser(updateContext, tc.user), role.Name, rest.DefaultUpdatedObjectInfo(role), nil, nil, false, nil) 149 150 if tc.expectAllowed { 151 if err != nil { 152 t.Error(err) 153 return 154 } 155 if fakeStorage.updated != 1 { 156 t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated) 157 return 158 } 159 } else { 160 if !errors.IsForbidden(err) { 161 t.Errorf("expected forbidden, got %v", err) 162 return 163 } 164 if fakeStorage.updated != 0 { 165 t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated) 166 return 167 } 168 } 169 170 if tc.expectAuthz != (authzCalled > 0) { 171 t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled) 172 } 173 }) 174 } 175 } 176 177 type fakeStorage struct { 178 updated int 179 created int 180 rest.StandardStorage 181 } 182 183 func (f *fakeStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { 184 f.created++ 185 return nil, nil 186 } 187 188 func (f *fakeStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { 189 obj, err := objInfo.UpdatedObject(ctx, &rbac.ClusterRole{}) 190 if err != nil { 191 return obj, false, err 192 } 193 f.updated++ 194 return nil, false, nil 195 }